/* This is a simple plugin to render action dropdown menus from html. * John Barlow - SugarCRM * add secondary popup implementation by Justin Park - SugarCRM * * The html structure it expects is as follows: * * * * By adding a class of "fancymenu" to the menu root, the plugin adds an additional "ab" class to the * dropdown handle, allowing you to make the menu "fancy" with additional css if you would like :) * * Functions: * * init: initializes things (called by default)... currently no options are passed * * Adds item to the menu at position index * addItem: (item, index) * item - created dom element or string that represents one * index(optional) - the position you want your new menuitem. If you leave this off, * the item is appended to the end of the list. * returns: nothing * * Finds an item in the menu (including the root node "outside" the ul structure). * findItem: (item) * item - string of the menu item you are looking for. * returns: index of element, or -1 if not found. */ (function($){ var methods = { init: function(options){ var menuNode = this; if(!this.hasClass("SugarActionMenu")){ //tag this element as a sugarmenu this.addClass("SugarActionMenu"); //Fix custom code buttons programatically to prevent metadata edits this.find("input[type='submit'], input[type='button']").each(function(idx, node){ var jNode = $(node); var parent = jNode.parent(); var _subnav = menuNode.find("ul.subnav"); var _timer_for_subnav = null; var disabled = $(this).prop('disabled'); var newItem = $(document.createElement("li")); var newItemA = $(document.createElement("a")); var accesskey = jNode.attr("accesskey"); var accesskey_el = $(""); newItemA.html(jNode.val()); if(!disabled ) { newItemA.click(function(event){ if($(this).hasClass("void") === false ){ jNode.click(); } }); } else { newItemA.addClass("disabled"); } newItemA.attr("id", jNode.attr("id")); accesskey_el.attr("id", jNode.attr("id") + "_accesskey"); jNode.attr("id", jNode.attr("id") + "_old"); if(accesskey !== undefined) { if($('#'+accesskey_el.attr('id')).length === 0) { accesskey_el.attr("accesskey", accesskey).click(function() { jNode.click(); }).appendTo("#content"); } jNode.attr("accesskey", ''); } //make sure the node we found isn't the main item of the list -- we don't want //to show it then. if(menuNode.sugarActionMenu("findItem", newItemA.html()) == -1){ parent.prepend(newItemA); } //make sub sliding menu jNode.siblings(".subnav-sub").each(function(idx, node) { var _menu = $(node); var _hide_menu = function() { if( _menu.hasClass("hover") === false ) _menu.hide(); }; var _hide_timer = null; var _delay = 300; _menu.mouseover(function(evt){ if( $(this).hasClass("hover") === false ) $(this).addClass("hover"); }).mouseout(function(evt){ if( $(this).hasClass("hover") ) $(this).removeClass("hover"); if(_hide_timer) clearTimeout(_hide_timer); _hide_timer = setTimeout(_hide_menu, _delay); }); newItemA.mouseover(function(evt) { $("ul.SugarActionMenu ul.subnav-sub").each(function(index, node){ $(node).removeClass("hover"); $(node).hide(); }); var _left = parent.offset().left + parent.width() - newItemA.css("paddingRight").replace("px", ""); var _top = parent.offset().top - _menu.css("paddingTop").replace("px", ""); _menu.css({ left: _left, top: _top }); if( _menu.hasClass("hover") === false ) _menu.addClass("hover"); if( _subnav.hasClass("subnav-sub-handler") === false ) _subnav.addClass("subnav-sub-handler"); _menu.show(); }).mouseout(function(evt) { _menu.removeClass("hover"); _subnav.removeClass("subnav-sub-handler") if(_hide_timer) clearTimeout(_hide_timer); _hide_timer = setTimeout(_hide_menu, _delay); }).click(function(evt){ if(_timer_for_subnav) clearTimeout(_timer_for_subnav); }).addClass("void"); menuNode.append(_menu); }); jNode.css("display", "none"); }); //look for all subnavs and set them up this.find("ul.subnav").each(function(index, node){ var jNode = $(node); var parent = jNode.parent(); var fancymenu = ""; var slideUpSpeed = 1; var slideDownSpeed = 1; var dropDownHandle; //if the dropdown handle doesn't exist, lets create it and //add it to the dom if(parent.find("span").length == 0){ //create dropdown handle dropDownHandle = $(document.createElement("span")); parent.append(dropDownHandle); } else { dropDownHandle = $(parent.find("span")); } if(menuNode.hasClass("fancymenu")){ dropDownHandle.addClass("ab"); dropDownHandle.tipTip({maxWidth: "auto", edgeOffset: 10, content: "More Actions", defaultPosition: "top"}); } //add click handler to handle dropDownHandle.click(function(event){ //close all other open menus //retore the dom elements back by handling iefix $("ul.SugarActionMenu > li").each(function() { $(this).sugarActionMenu('IEfix'); }); $("ul.SugarActionMenu ul.subnav").each(function(subIndex, node){ var subjNode = $(node); if(!(subjNode[0] === jNode[0])){ subjNode.slideUp(slideUpSpeed); subjNode.removeClass("ddopen"); } }); if(jNode.hasClass("ddopen")){ parent.sugarActionMenu('IEfix'); //Bug#50983: Popup the dropdown list above the arrow if the bottom part is cut off . var _animation = { 'height' : 0 }; if(jNode.hasClass("upper")) { _animation['top'] = (dropDownHandle.height() * -1); } jNode.animate(_animation, slideUpSpeed, function() { $(this).css({ 'height' : '', 'top' : '' }).hide().removeClass("upper ddopen"); }); } else{ //To support IE fixed size rendering, //parse out dom elements out of the fixed element parent.sugarActionMenu('IEfix', jNode); var _dropdown_height = jNode.height(), _animation = { 'height' : _dropdown_height }, _dropdown_bottom = dropDownHandle.offset().top + dropDownHandle.height() - $(document).scrollTop() + jNode.outerHeight(), _win_height = $(window).height(); if(dropDownHandle.offset().top > jNode.height() && _dropdown_bottom > $(window).height()) { jNode.css('top', (dropDownHandle.height() * -1)).addClass("upper"); _animation['top'] = (jNode.height() + dropDownHandle.height()) * -1; } jNode.height(0).show().animate(_animation,slideDownSpeed, function() { $(this).css('height', ''); }); jNode.addClass("ddopen"); } event.stopPropagation(); }); //add submenu click off to body var jBody = $("body"); var _hide_subnav_delay = 30; var _hide_subnav = function(subnav) { if( subnav.hasClass("subnav-sub-handler") === false ) { subnav.slideUp(slideUpSpeed); subnav.removeClass("ddopen"); } } if(jBody.data("sugarActionMenu") != true){ jBody.data("sugarActionMenu", true); jBody.bind("click", function(){ //retore the dom elements back by handling iefix $("ul.SugarActionMenu > li").each(function() { $(this).sugarActionMenu('IEfix'); }); $("ul.SugarActionMenu ul.subnav").each(function(subIndex, node){ //prevent hiding the submenu when user click the submenu which contains one more depth submenu var _hide = function() { _hide_subnav($(node)); } setTimeout(_hide, _hide_subnav_delay); }); //Hide second depth submenu $("ul.SugarActionMenu ul.subnav-sub").each(function(subIndex, node){ var _hide = function() { $(this).removeClass("hover"); $(this).hide(); } _timer_for_subnav = setTimeout(_hide, _hide_subnav_delay); }); }); } //add hover handler to handle dropDownHandle.hover(function(){ dropDownHandle.addClass("subhover"); }, function(){ dropDownHandle.removeClass("subhover"); }); //bind click event to submenu items to hide the menu on click jNode.find("li").each(function(index, subnode){ //prevent hiding the submenu when user click the submenu which contains one more depth submenu $(subnode).bind("click", function(evt){ var _hide = function() { _hide_subnav(jNode); } setTimeout(_hide, _hide_subnav_delay); }); }); //fix up text of tags so they span correctly jNode.find("a").each(function(index, subnode){ $(subnode).html(function(index, oldhtml){ return oldhtml.replace(" ", " "); }); }); }); //Bug#51579: delete li tags which contains empty due to access role this.find(".subnav > li").each(function(index, subnode) { if($(subnode).html().replace(/ /g, '') == '') { $(subnode).remove(); } }); //Bug#51579: If the first item is empty due to the access role, // replace the first button from the first of the sub-list items. this.find("li.sugar_action_button:first").each(function(index, node) { var _this = $(node); var _first_item = $(node).find("a:first").not($(node).find(".subnav > li a")); if(_first_item.length == 0) { var sub_items = $(node).find(".subnav > li:first").children(); if(sub_items.length == 0) menuNode.hide(); else _this.prepend(sub_items); } }); } return this; }, addItem : function(args){ if(args.index == null){ this.find("ul.subnav").each(function(index, node){ $(node).append(args.item); }) } else{ this.find("ul.subnav").find("li").each(function(index, node){ if(args.index == index+1){ $(node).before(args.item); } }); } return this; }, findItem: function(item){ var index = -1; this.find("a").each(function(idx, node){ var jNode = $(node); if($.trim(jNode.html()) == $.trim(item)){ index = idx; } }); return index; }, /** * To support IE fixed size rendering, * parse out dom elements out of the fixed element * * Prepare === *
* ... *
  • *
      * ... *
    *
  • *
    * * Application === *
    *
  • * ... *
  • *
    * * * @param this - element container which is inside the fixed box model * @param $ul - dropdown box model which needs to render out of the fixed box range * if $ul is not given, it will restore back to the original structure */ IEfix: function($ul) { if ($.browser.msie && $.browser.version > 6) { if($ul) { if( $ul.hasClass('iefixed') === false) return; this.each(function(){ SUGAR.themes.counter = SUGAR.themes.counter ? SUGAR.themes.counter++ : 1; var $$ = $(this), _id = $$.attr("ul-child-id") ? $$.attr("ul-child-id") : ($$.parent('.SugarActionMenu').attr("id")) ? $$.parent('.SugarActionMenu').attr("id") + 'Subnav' : 'sugaractionmenu' + SUGAR.themes.counter, _top = $$.position().top + $$.outerHeight(), _width = 'auto', //to obtain a certain size, apply min-width in css _css = { top: _top, width: _width, position: 'fixed' }, _right = $('body').width() - $$.offset().left - $$.width(), _left = $$.offset().left; //fixed positioning depends on the css property if($ul.css('right') != 'auto') { _css['right'] = _right; } else { _css['left'] = _left; } $('body').append($ul.attr("id", _id).addClass("SugarActionMenuIESub").css(_css)); $$.attr("ul-child-id", _id); }); } else { this.each(function(){ var _id = $(this).attr("ul-child-id"); $(this).append($("body>#"+_id).removeClass("SugarActionMenuIESub")); }); } } } } $.fn.sugarActionMenu = function(method) { if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call( arguments, 1)); } else if (typeof method === 'object' || !method) { return methods.init.apply(this, arguments); } else { $.error('Method ' + method + ' does not exist on jQuery.tooltip'); } } })(jQuery);