/******************************************************************************* jquery.mb.components Copyright (c) 2001-2010. Matteo Bicocchi (Pupunzi); Open lab srl, Firenze - Italy email: info@pupunzi.com site: http://pupunzi.com Licences: MIT, GPL http://www.opensource.org/licenses/mit-license.php http://www.gnu.org/licenses/gpl.html ******************************************************************************/ /* * Name:jquery.mb.menu * Version: 2.8.5rc5 * * added: boxMenu menu modality by: Sven Dowideit http://trunk.fosiki.com/Sandbox/WebSubMenu */ // to get the element that is fireing a contextMenu event you have $.mbMenu.lastContextMenuEl that returns an object. (function($) { $.mbMenu = { name:"mbMenu", author:"Matteo Bicocchi", version:"2.8.5rc5", actualMenuOpener:false, options: { template:"yourMenuVoiceTemplate", // the url that returns the menu voices via ajax. the data passed in the request is the "menu" attribute value as "menuId" realtemplate:"", additionalData:"", menuSelector:".menuContainer", menuWidth:0, openOnRight:false, containment:"window", iconPath:"ico/", hasImages:true, fadeInTime:100, fadeOutTime:200, menuTop:0, menuLeft:0, submenuTop:0, submenuLeft:4, opacity:1, openOnClick:true, closeOnMouseOut:false, closeAfter:500, minZindex:9900, // or number hoverIntent:0, //if you use jquery.hoverIntent.js set this to time in milliseconds; 0= false; submenuHoverIntent:200, //if you use jquery.hoverIntent.js set this to time in milliseconds; 0= false; onContextualMenu:function(){} //it pass 'o' (the menu you clicked on) and 'e' (the event) }, buildMenu : function (options){ return this.each (function () { var thisMenu =this; thisMenu.id = !this.id ? "menu_"+Math.floor (Math.random () * 1000): this.id; this.options = {}; $.extend (this.options, $.mbMenu.options); $.extend (this.options, options); $(".mbmenu").hide(); thisMenu.clicked = false; thisMenu.rootMenu=false; thisMenu.actualOpenedMenu=false; thisMenu.menuvoice=false; var root=$(this); var openOnClick=this.options.openOnClick; var closeOnMouseOut=this.options.closeOnMouseOut; //build roots $(root).each(function(){ /* *using metadata plugin you can add attribute writing them inside the class attr with a JSON sintax * for ex: class="rootVoice {menu:'menu_2'}" */ if ($.metadata){ $.metadata.setType("class"); thisMenu.menuvoice=$(this).find(".rootVoice"); $(thisMenu.menuvoice).each(function(){ if ($(this).metadata().menu) $(this).attr("menu",$(this).metadata().menu); if ($(this).metadata().disabled) $(this).attr("isDisable",$(this).metadata().disabled); }); } thisMenu.menuvoice=$(this).find("[menu]").add($(this).filter("[menu]")); thisMenu.menuvoice.filter("[isDisable]").addClass("disabled"); $(thisMenu.menuvoice).css("white-space","nowrap"); if(openOnClick){ $(thisMenu.menuvoice).bind("click",function(){ $(document).unbind("click.closeMbMenu"); if (!$(this).attr("isOpen")){ $(this).buildMbMenu(thisMenu,$(this).attr("menu")); $(this).attr("isOpen","true"); }else{ $(this).removeMbMenu(thisMenu,true); $(this).addClass("selected"); } //empty lyt if($(this).attr("menu")=="empty"){ if(thisMenu.actualOpenedMenu){ $("[isOpen]").removeAttr("isOpen"); } $(this).removeMbMenu(thisMenu); } $(document).unbind("click.closeMbMenu"); }); } var mouseOver=$.browser.msie?"mouseenter":"mouseover"; var mouseOut=$.browser.msie?"mouseleave":"mouseout"; $(thisMenu.menuvoice).mb_hover( this.options.hoverIntent, function(){ if(!$(this).attr("isOpen")) $("[isOpen]").removeAttr("isOpen"); if (closeOnMouseOut) clearTimeout($.mbMenu.deleteOnMouseOut); if (!openOnClick) $(thisMenu).find(".selected").removeClass("selected"); if(thisMenu.actualOpenedMenu){ $(thisMenu.actualOpenedMenu).removeClass("selected");} $(this).addClass("selected"); if((thisMenu.clicked || !openOnClick) && !$(this).attr("isOpen")){ $(this).removeMbMenu(thisMenu); $(this).buildMbMenu(thisMenu,$(this).attr("menu")); if ($(this).attr("menu")=="empty"){ $(this).removeMbMenu(thisMenu); } $(this).attr("isOpen","true"); } }, function(){ if (closeOnMouseOut) $.mbMenu.deleteOnMouseOut= setTimeout(function(){ $(this).removeMbMenu(thisMenu,true); $(document).unbind("click.closeMbMenu"); },$(root)[0].options.closeAfter); if ($(this).attr("menu")=="empty"){ $(this).removeClass("selected"); } if(!thisMenu.clicked) $(this).removeClass("selected"); $(document).one("click.closeMbMenu",function(){ $("[isOpen]").removeAttr("isOpen"); $(this).removeClass("selected"); $(this).removeMbMenu(thisMenu,true); thisMenu.rootMenu=false;thisMenu.clicked=false; }); } ); }); }); }, buildContextualMenu: function (options){ return this.each (function () { var thisMenu = this; thisMenu.options = {}; $.extend (thisMenu.options, $.mbMenu.options); $.extend (thisMenu.options, options); $(".mbmenu").hide(); thisMenu.clicked = false; thisMenu.rootMenu=false; thisMenu.actualOpenedMenu=false; thisMenu.menuvoice=false; /* *using metadata plugin you can add attribut writing them inside the class attr with a JSON sintax * for ex: class="rootVoice {menu:'menu_2'}" */ var cMenuEls; if ($.metadata){ $.metadata.setType("class"); cMenuEls= $(this).find(".cmVoice"); $(cMenuEls).each(function(){ if ($(this).metadata().cMenu) $(this).attr("cMenu",$(this).metadata().cMenu); }); } cMenuEls= $(this).find("[cMenu]").add($(this).filter("[cMenu]")); $(cMenuEls).each(function(){ $(this).css({"-webkit-user-select":"none","-moz-user-select":"none"}); var cm=this; cm.id = !cm.id ? "menu_"+Math.floor (Math.random () * 100): cm.id; $(cm).css({cursor:"default"}); $(cm).bind("contextmenu","mousedown",function(event){ event.preventDefault(); event.stopPropagation(); event.cancelBubble=true; $.mbMenu.lastContextMenuEl=cm; if ($.mbMenu.options.actualMenuOpener) { $(thisMenu).removeMbMenu($.mbMenu.options.actualMenuOpener); } /*add custom behavior to contextMenuEvent passing the el and the event *you can for example store to global var the obj that is fireing the event *mbActualContextualMenuObj=cm; * * you can for example create a function that manipulate the voices of the menu * you are opening according to a certain condition... */ thisMenu.options.onContextualMenu(this,event); $(this).buildMbMenu(thisMenu,$(this).attr("cMenu"),"cm",event); $(this).attr("isOpen","true"); }); }); }); } }; $.fn.extend({ buildMbMenu: function(op,m,type,e){ var msie6=$.browser.msie && $.browser.version=="6.0"; var mouseOver=$.browser.msie?"mouseenter":"mouseover"; var mouseOut=$.browser.msie?"mouseleave":"mouseout"; if (e) { this.mouseX=$(this).getMouseX(e); this.mouseY=$(this).getMouseY(e); } if ($.mbMenu.options.actualMenuOpener && $.mbMenu.options.actualMenuOpener!=op) $(op).removeMbMenu($.mbMenu.options.actualMenuOpener); $.mbMenu.options.actualMenuOpener=op; if(!type || type=="cm") { if (op.rootMenu) { $(op.rootMenu).removeMbMenu(op); $(op.actualOpenedMenu).removeAttr("isOpen"); $("[isOpen]").removeAttr("isOpen"); } op.clicked=true; op.actualOpenedMenu=this; $(op.actualOpenedMenu).attr("isOpen","true"); $(op.actualOpenedMenu).addClass("selected"); } //empty if($(this).attr("menu")=="empty"){ return; } var opener=this; var where=(!type|| type=="cm")?$(document.body):$(this).parent().parent(); var menuClass= op.options.menuSelector.replace(".",""); if(op.rootMenu) menuClass+=" submenuContainer"; if(!op.rootMenu && $(opener).attr("isDisable")) menuClass+=" disabled"; where.append("
"); this.menu = where.find(".menuDiv"); $(this.menu).css({width:0, height:0}); if (op.options.minZindex!="auto"){ $(this.menu).css({zIndex:op.options.minZindex++}); }else{ $(this.menu).mb_bringToFront(); } this.menuContainer = $(this.menu).find(op.options.menuSelector); $(this.menuContainer).bind(mouseOver,function(){ $(opener).addClass("selected"); }); $(this.menuContainer).css({ position:"absolute", opacity:op.options.opacity }); if ( op.options.realtemplate == ""){ op.options.realtemplate = op.options.template; } if (!$("#"+m).html()){ $.ajax({ type: "GET", url: op.options.realtemplate, cache: false, async: false, data:"menuId="+m+(op.options.additionalData!=""?"&"+op.options.additionalData:""), success: function(html){ $("body").append(html); $("#"+m).hide(); } }); } $(this.menuContainer).attr("id", "mb_"+m).hide(); //LITERAL MENU SUGGESTED BY SvenDowideit var isBoxmenu=$("#"+m).hasClass("boxMenu"); if (isBoxmenu) { this.voices = $("#"+m).clone(true); this.voices.css({display: "block"}); this.voices.attr("id", m+"_clone"); } else { //TODO this will break - if there are nested a's this.voices= $("#"+m).find("a").clone(true); } /* *using metadata plugin you can add attribut writing them inside the class attr with a JSON sintax * for ex: class="rootVoice {menu:'menu_2'}" */ if ($.metadata){ $.metadata.setType("class"); $(this.voices).each(function(){ if ($(this).metadata().disabled) $(this).attr("isdisable",$(this).metadata().disabled); if ($(this).metadata().img) $(this).attr("img",$(this).metadata().img); if ($(this).metadata().menu) $(this).attr("menu",$(this).metadata().menu); if ($(this).metadata().action) $(this).attr("action",$(this).metadata().action); }); } // build each voices of the menu $(this.voices).each(function(i){ var voice=this; var imgPlace=""; var isText=$(voice).attr("rel")=="text"; var isTitle=$(voice).attr("rel")=="title"; var isDisabled=$(voice).is("[isdisable]"); var isModalWindow=$(voice).attr("displaymode")=="modal"; if(!op.rootMenu && $(opener).attr("isDisable")) isDisabled=true; var isSeparator=$(voice).attr("rel")=="separator"; // boxMenu SUGGESTED by Sven Dowideit if (op.options.hasImages && !isText && !isBoxmenu){ var imgPath=$(voice).attr("img")?$(voice).attr("img"):"blank.gif"; // imgPath=(imgPath.length>3 && imgPath.indexOf(".")>-1)?"":imgPath; imgPath=(imgPath.length>3)?"":imgPath; imgPlace="