0
0
Fork 0
mirror of https://github.com/salesagility/SuiteCRM.git synced 2025-02-05 14:39:45 +00:00
salesagility_SuiteCRM/include/javascript/jquery/jquery.sugarMenu.js
2018-04-02 10:06:55 +01:00

429 lines
16 KiB
JavaScript
Executable file

/* 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:
*
* <ul> - Menu root
* <li> - First element in menu (visible)
* <ul class="subnav"> - Popout menu (should start hidden)
* <li></li> - \
* ... - Elements in popout menu
* <li></li> - /
* <li>
* <input></input> - element contains submenu
* <ul class="subnav-sub"> - sub-popout menu (shown when mouseover on the above element)
* <li></li> - \
* ... - Elements in sub-popout menu
* <li></li> - /
* </ul>
* </li>
* </ul>
* </li>
* </ul>
*
* 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 = $("<a></a>");
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
var foundSpanCount = parent.find("span.suitepicon-action-caret").length;
if (foundSpanCount === 0) {
//create dropdown handle
dropDownHandle = $(document.createElement("span"));
dropDownHandle.addClass('suitepicon suitepicon-action-caret');
parent.append(dropDownHandle);
} else if (foundSpanCount === 1) {
dropDownHandle = $(parent.find("span"));
} else {
dropDownHandle = $(parent.find("span").first());
}
var toggleDropDown = function () {
//close all other open menus
//restore 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', '');
setTimeout(function () {
// $('.subnav.ddopen').each(function (i, e) {
// if (!$(e).hasClass('upper')) {
// $(e).css('top', parseInt($(e).css('top')) + 10 + 'px');
// }
// })
}, 2);
});
jNode.addClass("ddopen");
}
};
//add click handler to handle
dropDownHandle.click(function (event) {
toggleDropDown();
event.stopPropagation();
});
var parentDropDownHandler = parent.find('a').first().hasClass('parent-dropdown-handler');
if (parentDropDownHandler) {
parent.click(function (event) {
dropDownHandle.click();
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 <a> tags so they span correctly
jNode.find("a").each(function (index, subnode) {
$(subnode).html(function (index, oldhtml) {
return oldhtml.replace(" ", "&nbsp;");
});
});
});
//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 ===
* <div style=position:fixed>
* ...
* <li jquery-attached>
* <ul style=position:absoulte>
* ...
* </ul>
* </li>
* </div>
*
* Application ===
* <div style=position:fixed>
* <li ul-child-id='auto-evaluted-id'>
* ...
* </li>
* </div>
*
* <ul id='auto-evaluted-id' style=position:fix;left/right/top-positioning:auto-calculated>
* ...
* </ul>
* @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 ($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);