940 lines
23 KiB
JavaScript
940 lines
23 KiB
JavaScript
|
/*
|
||
|
* jQuery Menu plugin
|
||
|
* Version: 0.0.9
|
||
|
*
|
||
|
* Copyright (c) 2007 Roman Weich
|
||
|
* http://p.sohei.org
|
||
|
*
|
||
|
* Dual licensed under the MIT and GPL licenses
|
||
|
* (This means that you can choose the license that best suits your project, and use it accordingly):
|
||
|
* http://www.opensource.org/licenses/mit-license.php
|
||
|
* http://www.gnu.org/licenses/gpl.html
|
||
|
*
|
||
|
* Changelog:
|
||
|
* v 0.0.9 - 2008-01-19
|
||
|
*/
|
||
|
|
||
|
(function($)
|
||
|
{
|
||
|
var menus = [], //list of all menus
|
||
|
visibleMenus = [], //list of all visible menus
|
||
|
activeMenu = activeItem = null,
|
||
|
menuDIVElement = $('<div class="menu-div outerbox" style="position:absolute;top:0;left:0;display:none;"><div class="shadowbox1"></div><div class="shadowbox2"></div><div class="shadowbox3"></div></div>')[0],
|
||
|
menuULElement = $('<ul class="menu-ul innerbox"></ul>')[0],
|
||
|
menuItemElement = $('<li style="position:relative;"><div class="menu-item"></div></li>')[0],
|
||
|
arrowElement = $('<img class="menu-item-arrow" />')[0],
|
||
|
$rootDiv = $('<div id="root-menu-div" style="position:absolute;top:0;left:0;"></div>'), //create main menu div
|
||
|
defaults = {
|
||
|
// $.Menu options
|
||
|
showDelay : 200,
|
||
|
hideDelay : 200,
|
||
|
hoverOpenDelay: 0,
|
||
|
offsetTop : 0,
|
||
|
offsetLeft : 0,
|
||
|
minWidth: 0,
|
||
|
onOpen: null,
|
||
|
onClose: null,
|
||
|
|
||
|
// $.MenuItem options
|
||
|
onClick: null,
|
||
|
arrowSrc: null,
|
||
|
addExpando: false,
|
||
|
|
||
|
// $.fn.menuFromElement options
|
||
|
copyClassAttr: false
|
||
|
};
|
||
|
|
||
|
$(function(){
|
||
|
$rootDiv.appendTo('body');
|
||
|
});
|
||
|
|
||
|
$.extend({
|
||
|
MenuCollection : function(items) {
|
||
|
|
||
|
this.menus = [];
|
||
|
|
||
|
this.init(items);
|
||
|
}
|
||
|
});
|
||
|
$.extend($.MenuCollection, {
|
||
|
prototype : {
|
||
|
init : function(items)
|
||
|
{
|
||
|
if ( items && items.length )
|
||
|
{
|
||
|
for ( var i = 0; i < items.length; i++ )
|
||
|
{
|
||
|
this.addMenu(items[i]);
|
||
|
items[i].menuCollection = this;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
addMenu : function(menu)
|
||
|
{
|
||
|
if ( menu instanceof $.Menu )
|
||
|
this.menus.push(menu);
|
||
|
|
||
|
menu.menuCollection = this;
|
||
|
|
||
|
var self = this;
|
||
|
$(menu.target).hover(function(){
|
||
|
if ( menu.visible )
|
||
|
return;
|
||
|
|
||
|
//when there is an open menu in this collection, hide it and show the new one
|
||
|
for ( var i = 0; i < self.menus.length; i++ )
|
||
|
{
|
||
|
if ( self.menus[i].visible )
|
||
|
{
|
||
|
self.menus[i].hide();
|
||
|
menu.show();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}, function(){});
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
$.extend({
|
||
|
Menu : function(target, items, options) {
|
||
|
this.menuItems = []; //all direct child $.MenuItem objects
|
||
|
this.subMenus = []; //all subMenus from this.menuItems
|
||
|
this.visible = false;
|
||
|
this.active = false; //this menu has hover or one of its submenus is open
|
||
|
this.parentMenuItem = null;
|
||
|
this.settings = $.extend({}, defaults, options);
|
||
|
this.target = target;
|
||
|
this.$eDIV = null;
|
||
|
this.$eUL = null;
|
||
|
this.timer = null;
|
||
|
this.menuCollection = null;
|
||
|
this.openTimer = null;
|
||
|
|
||
|
this.init();
|
||
|
if ( items && items.constructor == Array )
|
||
|
this.addItems(items);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
$.extend($.Menu, {
|
||
|
checkMouse : function(e)
|
||
|
{
|
||
|
var t = e.target;
|
||
|
|
||
|
//the user clicked on the target of the currenty open menu
|
||
|
if ( visibleMenus.length && t == visibleMenus[0].target )
|
||
|
return;
|
||
|
|
||
|
//get the last node before the #root-menu-div
|
||
|
while ( t.parentNode && t.parentNode != $rootDiv[0] )
|
||
|
t = t.parentNode;
|
||
|
|
||
|
//is the found node one of the visible menu elements?
|
||
|
if ( !$(visibleMenus).filter(function(){ return this.$eDIV[0] == t }).length )
|
||
|
{
|
||
|
$.Menu.closeAll();
|
||
|
}
|
||
|
},
|
||
|
checkKey : function(e)
|
||
|
{
|
||
|
switch ( e.keyCode )
|
||
|
{
|
||
|
case 13: //return
|
||
|
if ( activeItem )
|
||
|
activeItem.click(e, activeItem.$eLI[0]);
|
||
|
break;
|
||
|
case 27: //ESC
|
||
|
$.Menu.closeAll();
|
||
|
break;
|
||
|
case 37: //left
|
||
|
if ( !activeMenu )
|
||
|
activeMenu = visibleMenus[0];
|
||
|
var a = activeMenu;
|
||
|
if ( a && a.parentMenuItem ) //select the parent menu and close the submenu
|
||
|
{
|
||
|
//unbind the events temporary, as we dont want the hoverout event to fire
|
||
|
var pmi = a.parentMenuItem;
|
||
|
pmi.$eLI.unbind('mouseout').unbind('mouseover');
|
||
|
a.hide();
|
||
|
pmi.hoverIn(true);
|
||
|
setTimeout(function(){ //bind again..but delay it
|
||
|
pmi.bindHover();
|
||
|
});
|
||
|
}
|
||
|
else if ( a && a.menuCollection ) //select the previous menu in the collection
|
||
|
{
|
||
|
var pos,
|
||
|
mcm = a.menuCollection.menus;
|
||
|
if ( (pos = $.inArray(a, mcm)) > -1 )
|
||
|
{
|
||
|
if ( --pos < 0 )
|
||
|
pos = mcm.length - 1;
|
||
|
$.Menu.closeAll();
|
||
|
mcm[pos].show();
|
||
|
mcm[pos].setActive();
|
||
|
if ( mcm[pos].menuItems.length ) //select the first item
|
||
|
mcm[pos].menuItems[0].hoverIn(true);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 38: //up
|
||
|
if ( activeMenu )
|
||
|
activeMenu.selectNextItem(-1);
|
||
|
break;
|
||
|
case 39: //right
|
||
|
if ( !activeMenu )
|
||
|
activeMenu = visibleMenus[0];
|
||
|
var m,
|
||
|
a = activeMenu,
|
||
|
asm = activeItem ? activeItem.subMenu : null;
|
||
|
if ( a )
|
||
|
{
|
||
|
if ( asm && asm.menuItems.length ) //select the submenu
|
||
|
{
|
||
|
asm.show();
|
||
|
asm.menuItems[0].hoverIn();
|
||
|
}
|
||
|
else if ( (a = a.inMenuCollection()) ) //select the next menu in the collection
|
||
|
{
|
||
|
var pos,
|
||
|
mcm = a.menuCollection.menus;
|
||
|
if ( (pos = $.inArray(a, mcm)) > -1 )
|
||
|
{
|
||
|
if ( ++pos >= mcm.length )
|
||
|
pos = 0;
|
||
|
$.Menu.closeAll();
|
||
|
mcm[pos].show();
|
||
|
mcm[pos].setActive();
|
||
|
if ( mcm[pos].menuItems.length ) //select the first item
|
||
|
mcm[pos].menuItems[0].hoverIn(true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 40: //down
|
||
|
if ( !activeMenu )
|
||
|
{
|
||
|
if ( visibleMenus.length && visibleMenus[0].menuItems.length )
|
||
|
visibleMenus[0].menuItems[0].hoverIn();
|
||
|
}
|
||
|
else
|
||
|
activeMenu.selectNextItem();
|
||
|
break;
|
||
|
}
|
||
|
if ( e.keyCode > 36 && e.keyCode < 41 )
|
||
|
return false; //this will prevent scrolling
|
||
|
},
|
||
|
closeAll : function()
|
||
|
{
|
||
|
while ( visibleMenus.length )
|
||
|
visibleMenus[0].hide();
|
||
|
},
|
||
|
setDefaults : function(d)
|
||
|
{
|
||
|
$.extend(defaults, d);
|
||
|
},
|
||
|
prototype : {
|
||
|
/**
|
||
|
* create / initialize new menu
|
||
|
*/
|
||
|
init : function()
|
||
|
{
|
||
|
var self = this;
|
||
|
if ( !this.target )
|
||
|
return;
|
||
|
else if ( this.target instanceof $.MenuItem )
|
||
|
{
|
||
|
this.parentMenuItem = this.target;
|
||
|
this.target.addSubMenu(this);
|
||
|
this.target = this.target.$eLI;
|
||
|
}
|
||
|
|
||
|
menus.push(this);
|
||
|
|
||
|
//use the dom methods instead the ones from jquery (faster)
|
||
|
this.$eDIV = $(menuDIVElement.cloneNode(1));
|
||
|
this.$eUL = $(menuULElement.cloneNode(1));
|
||
|
this.$eDIV[0].appendChild(this.$eUL[0]);
|
||
|
$rootDiv[0].appendChild(this.$eDIV[0]);
|
||
|
|
||
|
//bind events
|
||
|
if ( !this.parentMenuItem )
|
||
|
{
|
||
|
$(this.target).click(function(e){
|
||
|
self.onClick(e);
|
||
|
}).hover(function(e){
|
||
|
self.setActive();
|
||
|
|
||
|
if ( self.settings.hoverOpenDelay )
|
||
|
{
|
||
|
self.openTimer = setTimeout(function(){
|
||
|
if ( !self.visible )
|
||
|
self.onClick(e);
|
||
|
}, self.settings.hoverOpenDelay);
|
||
|
}
|
||
|
}, function(){
|
||
|
if ( !self.visible )
|
||
|
$(this).removeClass('activetarget');
|
||
|
|
||
|
if ( self.openTimer )
|
||
|
clearTimeout(self.openTimer);
|
||
|
});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.$eDIV.hover(function(){
|
||
|
self.setActive();
|
||
|
}, function(){});
|
||
|
}
|
||
|
},
|
||
|
setActive : function()
|
||
|
{
|
||
|
if ( !this.parentMenuItem )
|
||
|
$(this.target).addClass('activetarget');
|
||
|
else
|
||
|
this.active = true;
|
||
|
},
|
||
|
addItem : function(item)
|
||
|
{
|
||
|
if ( item instanceof $.MenuItem )
|
||
|
{
|
||
|
if ( $.inArray(item, this.menuItems) == -1 )
|
||
|
{
|
||
|
this.$eUL.append(item.$eLI);
|
||
|
this.menuItems.push(item);
|
||
|
item.parentMenu = this;
|
||
|
if ( item.subMenu )
|
||
|
this.subMenus.push(item.subMenu);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.addItem(new $.MenuItem(item, this.settings));
|
||
|
}
|
||
|
},
|
||
|
addItems : function(items)
|
||
|
{
|
||
|
for ( var i = 0; i < items.length; i++ )
|
||
|
{
|
||
|
this.addItem(items[i]);
|
||
|
}
|
||
|
},
|
||
|
removeItem : function(item)
|
||
|
{
|
||
|
var pos = $.inArray(item, this.menuItems);
|
||
|
if ( pos > -1 )
|
||
|
this.menuItems.splice(pos, 1);
|
||
|
item.parentMenu = null;
|
||
|
},
|
||
|
hide : function()
|
||
|
{
|
||
|
if ( !this.visible )
|
||
|
return;
|
||
|
|
||
|
var i,
|
||
|
pos = $.inArray(this, visibleMenus);
|
||
|
|
||
|
this.$eDIV.hide();
|
||
|
|
||
|
if ( pos >= 0 )
|
||
|
visibleMenus.splice(pos, 1);
|
||
|
this.visible = this.active = false;
|
||
|
|
||
|
$(this.target).removeClass('activetarget');
|
||
|
|
||
|
//hide all submenus
|
||
|
for ( i = 0; i < this.subMenus.length; i++ )
|
||
|
{
|
||
|
this.subMenus[i].hide();
|
||
|
}
|
||
|
|
||
|
//set all items inactive (e.g. remove hover class..)
|
||
|
for ( i = 0; i < this.menuItems.length; i++ )
|
||
|
{
|
||
|
if ( this.menuItems[i].active )
|
||
|
this.menuItems[i].setInactive();
|
||
|
}
|
||
|
|
||
|
if ( !visibleMenus.length ) //unbind events when the last menu was closed
|
||
|
$(document).unbind('mousedown', $.Menu.checkMouse).unbind('keydown', $.Menu.checkKey);
|
||
|
|
||
|
if ( activeMenu == this )
|
||
|
activeMenu = null;
|
||
|
|
||
|
if ( this.settings.onClose )
|
||
|
this.settings.onClose.call(this);
|
||
|
},
|
||
|
show : function(e)
|
||
|
{
|
||
|
if ( this.visible )
|
||
|
return;
|
||
|
|
||
|
var zi,
|
||
|
pmi = this.parentMenuItem;
|
||
|
|
||
|
if ( this.menuItems.length ) //show only when it has items
|
||
|
{
|
||
|
if ( pmi ) //set z-index
|
||
|
{
|
||
|
zi = parseInt(pmi.parentMenu.$eDIV.css('z-index'));
|
||
|
this.$eDIV.css('z-index', (isNaN(zi) ? 1 : zi + 1));
|
||
|
}
|
||
|
this.$eDIV.css({visibility: 'hidden', display:'block'});
|
||
|
|
||
|
//set min-width
|
||
|
if ( this.settings.minWidth )
|
||
|
{
|
||
|
if ( this.$eDIV.width() < this.settings.minWidth )
|
||
|
this.$eDIV.css('width', this.settings.minWidth);
|
||
|
}
|
||
|
|
||
|
this.setPosition();
|
||
|
this.$eDIV.css({display:'none', visibility: ''}).show();
|
||
|
|
||
|
if ( this.settings.onOpen )
|
||
|
this.settings.onOpen.call(this);
|
||
|
}
|
||
|
if ( visibleMenus.length == 0 )
|
||
|
$(document).bind('mousedown', $.Menu.checkMouse).bind('keydown', $.Menu.checkKey);
|
||
|
|
||
|
this.visible = true;
|
||
|
visibleMenus.push(this);
|
||
|
},
|
||
|
setPosition : function()
|
||
|
{
|
||
|
var $t, o, posX, posY,
|
||
|
pmo, //parent menu offset
|
||
|
wst, //window scroll top
|
||
|
wsl, //window scroll left
|
||
|
ww = $(window).width(),
|
||
|
wh = $(window).height(),
|
||
|
pmi = this.parentMenuItem,
|
||
|
height = this.$eDIV[0].clientHeight,
|
||
|
width = this.$eDIV[0].clientWidth,
|
||
|
pheight; //parent height
|
||
|
|
||
|
if ( pmi )
|
||
|
{
|
||
|
//position on the right side of the parent menu item
|
||
|
o = pmi.$eLI.offset();
|
||
|
posX = o.left + pmi.$eLI.width();
|
||
|
posY = o.top;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//position right below the target
|
||
|
$t = $(this.target);
|
||
|
o = $t.offset();
|
||
|
posX = o.left + this.settings.offsetLeft;
|
||
|
posY = o.top + $t.height() + this.settings.offsetTop;
|
||
|
}
|
||
|
|
||
|
//y-pos
|
||
|
if ( $.fn.scrollTop )
|
||
|
{
|
||
|
wst = $(window).scrollTop();
|
||
|
if ( wh < height ) //menu is bigger than the window
|
||
|
{
|
||
|
//position the menu at the top of the visible area
|
||
|
posY = wst;
|
||
|
}
|
||
|
else if ( wh + wst < posY + height ) //outside on the bottom?
|
||
|
{
|
||
|
if ( pmi )
|
||
|
{
|
||
|
pmo = pmi.parentMenu.$eDIV.offset();
|
||
|
pheight = pmi.parentMenu.$eDIV[0].clientHeight;
|
||
|
if ( height <= pheight )
|
||
|
{
|
||
|
//bottom position = parentmenu-bottom position
|
||
|
posY = pmo.top + pheight - height;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//top position = parentmenu-top position
|
||
|
posY = pmo.top;
|
||
|
}
|
||
|
//still outside on the bottom?
|
||
|
if ( wh + wst < posY + height )
|
||
|
{
|
||
|
//shift the menu upwards till the bottom is visible
|
||
|
posY -= posY + height - (wh + wst);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//shift the menu upwards till the bottom is visible
|
||
|
posY -= posY + height - (wh + wst);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//x-pos
|
||
|
if ( $.fn.scrollLeft )
|
||
|
{
|
||
|
wsl = $(window).scrollLeft();
|
||
|
if ( ww + wsl < posX + width )
|
||
|
{
|
||
|
if ( pmi )
|
||
|
{
|
||
|
//display the menu not on the right side but on the left side
|
||
|
posX -= pmi.$eLI.width() + width;
|
||
|
//outside on the left now?
|
||
|
if ( posX < wsl )
|
||
|
posX = wsl;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//shift the menu to the left until it fits
|
||
|
posX -= posX + width - (ww + wsl);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//set position
|
||
|
this.$eDIV.css({left: posX, top: posY});
|
||
|
},
|
||
|
onClick : function(e)
|
||
|
{
|
||
|
if ( this.visible )
|
||
|
{
|
||
|
this.hide();
|
||
|
this.setActive(); //the class is removed in the hide() method..add it again
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//close all open menus
|
||
|
$.Menu.closeAll();
|
||
|
this.show(e);
|
||
|
}
|
||
|
},
|
||
|
addTimer : function(callback, delay)
|
||
|
{
|
||
|
var self = this;
|
||
|
this.timer = setTimeout(function(){
|
||
|
callback.call(self);
|
||
|
self.timer = null;
|
||
|
}, delay);
|
||
|
},
|
||
|
removeTimer : function()
|
||
|
{
|
||
|
if ( this.timer )
|
||
|
{
|
||
|
clearTimeout(this.timer);
|
||
|
this.timer = null;
|
||
|
}
|
||
|
},
|
||
|
selectNextItem : function(offset)
|
||
|
{
|
||
|
var i, pos = 0,
|
||
|
mil = this.menuItems.length,
|
||
|
o = offset || 1;
|
||
|
|
||
|
//get current pos
|
||
|
for ( i = 0; i < mil; i++ )
|
||
|
{
|
||
|
if ( this.menuItems[i].active )
|
||
|
{
|
||
|
pos = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
this.menuItems[pos].hoverOut();
|
||
|
|
||
|
do //jump over the separators
|
||
|
{
|
||
|
pos += o;
|
||
|
if ( pos >= mil )
|
||
|
pos = 0;
|
||
|
else if ( pos < 0 )
|
||
|
pos = mil - 1;
|
||
|
} while ( this.menuItems[pos].separator );
|
||
|
this.menuItems[pos].hoverIn(true);
|
||
|
},
|
||
|
inMenuCollection : function()
|
||
|
{
|
||
|
var m = this;
|
||
|
while ( m.parentMenuItem )
|
||
|
m = m.parentMenuItem.parentMenu;
|
||
|
return m.menuCollection ? m : null;
|
||
|
},
|
||
|
destroy : function() //delete menu
|
||
|
{
|
||
|
var pos, item;
|
||
|
|
||
|
this.hide();
|
||
|
|
||
|
//unbind events
|
||
|
if ( !this.parentMenuItem )
|
||
|
$(this.target).unbind('click').unbind('mouseover').unbind('mouseout');
|
||
|
else
|
||
|
this.$eDIV.unbind('mouseover').unbind('mouseout');
|
||
|
|
||
|
//destroy all items
|
||
|
while ( this.menuItems.length )
|
||
|
{
|
||
|
item = this.menuItems[0];
|
||
|
item.destroy();
|
||
|
delete item;
|
||
|
}
|
||
|
|
||
|
if ( (pos = $.inArray(this, menus)) > -1 )
|
||
|
menus.splice(pos, 1);
|
||
|
|
||
|
if ( this.menuCollection )
|
||
|
{
|
||
|
if ( (pos = $.inArray(this, this.menuCollection.menus)) > -1 )
|
||
|
this.menuCollection.menus.splice(pos, 1);
|
||
|
}
|
||
|
|
||
|
this.$eDIV.remove();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
$.extend({
|
||
|
MenuItem : function(obj, options)
|
||
|
{
|
||
|
if ( typeof obj == 'string' )
|
||
|
obj = {src: obj};
|
||
|
|
||
|
this.src = obj.src || '';
|
||
|
this.url = obj.url || null;
|
||
|
this.urlTarget = obj.target || null;
|
||
|
this.addClass = obj.addClass || null;
|
||
|
this.data = obj.data || null;
|
||
|
|
||
|
this.$eLI = null;
|
||
|
this.parentMenu = null;
|
||
|
this.subMenu = null;
|
||
|
this.settings = $.extend({}, defaults, options);
|
||
|
this.active = false;
|
||
|
this.enabled = true;
|
||
|
this.separator = false;
|
||
|
|
||
|
this.init();
|
||
|
|
||
|
if ( obj.subMenu )
|
||
|
new $.Menu(this, obj.subMenu, options);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
$.extend($.MenuItem, {
|
||
|
prototype : {
|
||
|
init : function()
|
||
|
{
|
||
|
var i, isStr,
|
||
|
src = this.src,
|
||
|
self = this;
|
||
|
|
||
|
this.$eLI = $(menuItemElement.cloneNode(1));
|
||
|
|
||
|
if ( this.addClass )
|
||
|
this.$eLI[0].setAttribute('class', this.addClass);
|
||
|
|
||
|
if ( this.settings.addExpando && this.data )
|
||
|
this.$eLI[0].menuData = this.data;
|
||
|
|
||
|
if ( src == '' )
|
||
|
{
|
||
|
this.$eLI.addClass('menu-separator');
|
||
|
this.separator = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
isStr = typeof src == 'string';
|
||
|
if ( isStr && this.url ) //create a link node, when we have an url
|
||
|
src = $('<a href="' + this.url + '"' + (this.urlTarget ? 'target="' + this.urlTarget + '"' : '') + '>' + src + '</a>');
|
||
|
else if ( isStr || !src.length )
|
||
|
src = [src];
|
||
|
//go through the passed DOM-Elements (or jquery objects or text nodes.) and append them to the menus list item
|
||
|
//this.$eLI.append(this.src) is really slow when having a lot(!!!) of items
|
||
|
for ( i = 0; i < src.length; i++ )
|
||
|
{
|
||
|
if ( typeof src[i] == 'string' )
|
||
|
{
|
||
|
//we cant use createTextNode, as html entities won't be displayed correctly (eg. ©)
|
||
|
elem = document.createElement('span');
|
||
|
elem.innerHTML = src[i];
|
||
|
this.$eLI[0].firstChild.appendChild(elem);
|
||
|
}
|
||
|
else
|
||
|
this.$eLI[0].firstChild.appendChild(src[i].cloneNode(1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.$eLI.click(function(e){
|
||
|
self.click(e, this);
|
||
|
});
|
||
|
this.bindHover();
|
||
|
},
|
||
|
click : function(e, scope)
|
||
|
{
|
||
|
if ( this.enabled && this.settings.onClick )
|
||
|
this.settings.onClick.call(scope, e, this);
|
||
|
},
|
||
|
bindHover : function()
|
||
|
{
|
||
|
var self = this;
|
||
|
this.$eLI.hover(function(){
|
||
|
self.hoverIn();
|
||
|
}, function(){
|
||
|
self.hoverOut();
|
||
|
});
|
||
|
},
|
||
|
hoverIn : function(noSubMenu)
|
||
|
{
|
||
|
this.removeTimer();
|
||
|
|
||
|
var i,
|
||
|
pms = this.parentMenu.subMenus,
|
||
|
pmi = this.parentMenu.menuItems,
|
||
|
self = this;
|
||
|
|
||
|
//remove the timer from the parent item, when there is one (e.g. to close the menu)
|
||
|
if ( this.parentMenu.timer )
|
||
|
this.parentMenu.removeTimer();
|
||
|
|
||
|
if ( !this.enabled )
|
||
|
return;
|
||
|
|
||
|
//deactivate all menuItems on the same level
|
||
|
for ( i = 0; i < pmi.length; i++ )
|
||
|
{
|
||
|
if ( pmi[i].active )
|
||
|
pmi[i].setInactive();
|
||
|
}
|
||
|
|
||
|
this.setActive();
|
||
|
activeMenu = this.parentMenu;
|
||
|
|
||
|
//are there open submenus on the same level? close them!
|
||
|
for ( i = 0; i < pms.length; i++ )
|
||
|
{
|
||
|
if ( pms[i].visible && pms[i] != this.subMenu && !pms[i].timer ) //close if there is no closetimer running already
|
||
|
pms[i].addTimer(function(){
|
||
|
this.hide();
|
||
|
}, pms[i].settings.hideDelay);
|
||
|
}
|
||
|
|
||
|
if ( this.subMenu && !noSubMenu )
|
||
|
{
|
||
|
//set timeout to show menu
|
||
|
this.subMenu.addTimer(function(){
|
||
|
this.show();
|
||
|
}, this.subMenu.settings.showDelay);
|
||
|
}
|
||
|
},
|
||
|
hoverOut : function()
|
||
|
{
|
||
|
this.removeTimer();
|
||
|
|
||
|
if ( !this.enabled )
|
||
|
return;
|
||
|
|
||
|
if ( !this.subMenu || !this.subMenu.visible )
|
||
|
this.setInactive();
|
||
|
},
|
||
|
removeTimer : function()
|
||
|
{
|
||
|
if ( this.subMenu )
|
||
|
{
|
||
|
this.subMenu.removeTimer();
|
||
|
}
|
||
|
},
|
||
|
setActive : function()
|
||
|
{
|
||
|
this.active = true;
|
||
|
this.$eLI.addClass('active');
|
||
|
|
||
|
//set the parent menu item active too if necessary
|
||
|
var pmi = this.parentMenu.parentMenuItem;
|
||
|
if ( pmi && !pmi.active )
|
||
|
pmi.setActive();
|
||
|
|
||
|
activeItem = this;
|
||
|
},
|
||
|
setInactive : function()
|
||
|
{
|
||
|
this.active = false;
|
||
|
this.$eLI.removeClass('active');
|
||
|
if ( this == activeItem )
|
||
|
activeItem = null;
|
||
|
},
|
||
|
enable : function()
|
||
|
{
|
||
|
this.$eLI.removeClass('disabled');
|
||
|
this.enabled = true;
|
||
|
},
|
||
|
disable : function()
|
||
|
{
|
||
|
this.$eLI.addClass('disabled');
|
||
|
this.enabled = false;
|
||
|
},
|
||
|
destroy : function()
|
||
|
{
|
||
|
this.removeTimer();
|
||
|
|
||
|
this.$eLI.remove();
|
||
|
|
||
|
//unbind events
|
||
|
this.$eLI.unbind('mouseover').unbind('mouseout').unbind('click');
|
||
|
//delete submenu
|
||
|
if ( this.subMenu )
|
||
|
{
|
||
|
this.subMenu.destroy();
|
||
|
delete this.subMenu;
|
||
|
}
|
||
|
this.parentMenu.removeItem(this);
|
||
|
},
|
||
|
addSubMenu : function(menu)
|
||
|
{
|
||
|
if ( this.subMenu )
|
||
|
return;
|
||
|
this.subMenu = menu;
|
||
|
if ( this.parentMenu && $.inArray(menu, this.parentMenu.subMenus) == -1 )
|
||
|
this.parentMenu.subMenus.push(menu);
|
||
|
if ( this.settings.arrowSrc )
|
||
|
{
|
||
|
var a = arrowElement.cloneNode(0);
|
||
|
a.setAttribute('src', this.settings.arrowSrc);
|
||
|
this.$eLI[0].firstChild.appendChild(a);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
$.extend($.fn, {
|
||
|
menuFromElement : function(options, list, bar)
|
||
|
{
|
||
|
var createItems = function(ul)
|
||
|
{
|
||
|
var menuItems = [],
|
||
|
subItems,
|
||
|
menuItem,
|
||
|
lis, $li, i, subUL, submenu, target,
|
||
|
classNames = null;
|
||
|
|
||
|
lis = getAllChilds(ul, 'LI');
|
||
|
for ( i = 0; i < lis.length; i++ )
|
||
|
{
|
||
|
subItems = [];
|
||
|
|
||
|
if ( !lis[i].childNodes.length ) //empty item? add separator
|
||
|
{
|
||
|
menuItems.push(new $.MenuItem('', options));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( (subUL = getOneChild(lis[i], 'UL')) )
|
||
|
{
|
||
|
subItems = createItems(subUL);
|
||
|
//remove subUL from DOM
|
||
|
$(subUL).remove();
|
||
|
}
|
||
|
|
||
|
//select the target...get the elements inside the li
|
||
|
$li = $(lis[i]);
|
||
|
if ( $li[0].childNodes.length == 1 && $li[0].childNodes[0].nodeType == 3 )
|
||
|
target = $li[0].childNodes[0].nodeValue;
|
||
|
else
|
||
|
target = $li[0].childNodes;
|
||
|
|
||
|
if ( options && options.copyClassAttr )
|
||
|
classNames = $li.attr('class');
|
||
|
|
||
|
//create item
|
||
|
menuItem = new $.MenuItem({src: target, addClass: classNames}, options);
|
||
|
menuItems.push(menuItem);
|
||
|
//add submenu
|
||
|
if ( subItems.length )
|
||
|
new $.Menu(menuItem, subItems, options);
|
||
|
|
||
|
}
|
||
|
return menuItems;
|
||
|
};
|
||
|
return this.each(function()
|
||
|
{
|
||
|
var ul, m;
|
||
|
//get the list element
|
||
|
if ( list || (ul = getOneChild(this, 'UL')) )
|
||
|
{
|
||
|
//if a specific list element is used, clone it, as we probably need it more than once
|
||
|
ul = list ? $(list).clone(true)[0] : ul;
|
||
|
menuItems = createItems(ul);
|
||
|
if ( menuItems.length )
|
||
|
{
|
||
|
m = new $.Menu(this, menuItems, options);
|
||
|
if ( bar )
|
||
|
bar.addMenu(m);
|
||
|
}
|
||
|
$(ul).hide();
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
menuBarFromUL : function(options)
|
||
|
{
|
||
|
return this.each(function()
|
||
|
{
|
||
|
var i,
|
||
|
lis = getAllChilds(this, 'LI');
|
||
|
|
||
|
if ( lis.length )
|
||
|
{
|
||
|
bar = new $.MenuCollection();
|
||
|
for ( i = 0; i < lis.length; i++ )
|
||
|
$(lis[i]).menuFromElement(options, null, bar);
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
menuBar : function(options, items)
|
||
|
{
|
||
|
return this.each(function()
|
||
|
{
|
||
|
if ( items && items.constructor == Array )
|
||
|
new $.Menu(this, items, options);
|
||
|
else
|
||
|
{
|
||
|
if ( this.nodeName.toUpperCase() == 'UL' )
|
||
|
$(this).menuBarFromUL(options);
|
||
|
else
|
||
|
$(this).menuFromElement(options, items);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
//faster than using jquery
|
||
|
var getOneChild = function(elem, name)
|
||
|
{
|
||
|
if ( !elem )
|
||
|
return null;
|
||
|
|
||
|
var n = elem.firstChild;
|
||
|
for ( ; n; n = n.nextSibling )
|
||
|
{
|
||
|
if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name )
|
||
|
return n;
|
||
|
}
|
||
|
return null;
|
||
|
};
|
||
|
//faster than using jquery
|
||
|
var getAllChilds = function(elem, name)
|
||
|
{
|
||
|
if ( !elem )
|
||
|
return [];
|
||
|
|
||
|
var r = [],
|
||
|
n = elem.firstChild;
|
||
|
for ( ; n; n = n.nextSibling )
|
||
|
{
|
||
|
if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name )
|
||
|
r[r.length] = n;
|
||
|
}
|
||
|
return r;
|
||
|
};
|
||
|
|
||
|
})(jQuery);
|