Created
March 17, 2020 06:44
-
-
Save JamesHarker/16e709e88c63538db6ba7cd370597833 to your computer and use it in GitHub Desktop.
Javascript context menu
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Context.js | |
* Copyright Jacob Kelley | |
* MIT License | |
* | |
*/ | |
contextMenu = (function () { | |
var options = { | |
fadeSpeed: 100, | |
filter: function ($obj) { | |
// Modify $obj, Do not return | |
}, | |
above: 'auto', | |
left: 'auto', | |
preventDoubleContext: true, | |
compress: false | |
}; | |
var _menus = {}; | |
function initialize(opts) { | |
options = $.extend({}, options, opts); | |
$(document).on('click', function () { | |
$('.dropdown-context').fadeOut(options.fadeSpeed, function(){ | |
$('.dropdown-context').css({display:''}).find('.drop-left').removeClass('drop-left'); | |
}); | |
}); | |
if(options.preventDoubleContext){ | |
$(document).on('contextmenu', '.dropdown-context', function (e) { | |
e.preventDefault(); | |
}); | |
} | |
$(document).on('mouseenter', '.dropdown-submenu', function(){ | |
var $sub = $(this).find('.dropdown-context-sub:first'), | |
subWidth = $sub.width(), | |
subLeft = $sub.offset().left, | |
collision = (subWidth+subLeft) > window.innerWidth; | |
if(collision){ | |
$sub.addClass('drop-left'); | |
} | |
}); | |
} | |
function updateOptions(opts){ | |
options = $.extend({}, options, opts); | |
} | |
function buildMenu(data, id, subMenu) { | |
var subClass = (subMenu) ? ' dropdown-context-sub' : '', | |
compressed = options.compress ? ' compressed-context' : '', | |
$menu = $('<ul class="dropdown-menu dropdown-context' + subClass + compressed +'" id="dropdown-' + id + '"></ul>'); | |
return buildMenuItems($menu, data, id, subMenu); | |
} | |
function buildMenuItems($menu, data, id, subMenu, addDynamicTag) { | |
var linkTarget = ''; | |
for(var i = 0; i<data.length; i++) { | |
if (typeof data[i].divider !== 'undefined') { | |
var divider = '<li class="divider'; | |
divider += (addDynamicTag) ? ' dynamic-menu-item' : ''; | |
divider += '"></li>'; | |
$menu.append(divider); | |
} else if (typeof data[i].header !== 'undefined') { | |
var header = '<li title-"Close Menu" class="dropdown-title'; | |
header += (addDynamicTag) ? ' dynamic-menu-item' : ''; | |
header += '">' + data[i].header + '</li>'; | |
if (typeof data[i].action !== 'undefined') { | |
$action = data[i].action; | |
header = $(header); | |
header.on('click', createCallback($action)); | |
} | |
$menu.append(header); | |
} else if (typeof data[i].menu_item_src !== 'undefined') { | |
var funcName; | |
if (typeof data[i].menu_item_src === 'function') { | |
if (data[i].menu_item_src.name === "") { // The function is declared like "foo = function() {}" | |
for (var globalVar in window) { | |
if (data[i].menu_item_src == window[globalVar]) { | |
funcName = globalVar; | |
break; | |
} | |
} | |
} else { | |
funcName = data[i].menu_item_src.name; | |
} | |
} else { | |
funcName = data[i].menu_item_src; | |
} | |
$menu.append('<li class="dynamic-menu-src" data-src="' + funcName + '"></li>'); | |
} else { | |
if (typeof data[i].href == 'undefined') { | |
data[i].href = '#'; | |
} | |
if (typeof data[i].target !== 'undefined') { | |
linkTarget = ' target="'+data[i].target+'"'; | |
} | |
if (typeof data[i].subMenu !== 'undefined') { | |
var sub_menu = '<li class="dropdown-submenu'; | |
sub_menu += (addDynamicTag) ? ' dynamic-menu-item' : ''; | |
sub_menu += '"><a tabindex="-1" href="' + data[i].href + '">' + data[i].text + '</a></li>' | |
$sub = $(sub_menu); | |
} else { | |
var element = '<li'; | |
element += (addDynamicTag) ? ' class="dynamic-menu-item"' : ''; | |
element += '><a tabindex="-1" href="' + data[i].href + '"'+linkTarget+'>'; | |
if (typeof data[i].icon !== 'undefined') | |
element += '<span class="glyphicon ' + data[i].icon + '"></span> '; | |
element += data[i].text + '</a></li>'; | |
$sub = $(element); | |
} | |
if (typeof data[i].action !== 'undefined') { | |
$action = data[i].action; | |
$sub | |
.find('a') | |
.addClass('context-event') | |
.on('click', createCallback($action)); | |
} | |
$menu.append($sub); | |
if (typeof data[i].subMenu != 'undefined') { | |
var subMenuData = buildMenu(data[i].subMenu, id, true); | |
$menu.find('li:last').append(subMenuData); | |
} | |
} | |
if (typeof options.filter == 'function') { | |
options.filter($menu.find('li:last')); | |
} | |
} | |
return $menu; | |
} | |
function addContext(selector, data) { | |
if (typeof data.id !== 'undefined' && typeof data.data !== 'undefined') { | |
var id = data.id; | |
$menu = $('body').find('#dropdown-' + id)[0]; | |
if (typeof $menu === 'undefined') { | |
$menu = buildMenu(data.data, id); | |
$('body').append($menu); | |
} | |
} else { | |
var d = new Date(), | |
id = d.getTime(), | |
$menu = buildMenu(data, id); | |
$('body').append($menu); | |
} | |
this._menus[selector.attr("id")] = $menu; | |
$(selector).on('contextmenu', function (e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
currentContextSelector = $(this); | |
$('.dropdown-context:not(.dropdown-context-sub)').hide(); | |
$dd = $('#dropdown-' + id); | |
$dd.find('.dynamic-menu-item').remove(); // Destroy any old dynamic menu items | |
$dd.find('.dynamic-menu-src').each(function(idx, element) { | |
var menuItems = window[$(element).data('src')]($(selector)); | |
$parentMenu = $(element).closest('.dropdown-menu.dropdown-context'); | |
$parentMenu = buildMenuItems($parentMenu, menuItems, id, undefined, true); | |
}); | |
if (typeof options.above == 'boolean' && options.above) { | |
$dd.addClass('dropdown-context-up').css({ | |
top: e.pageY - 20 - $('#dropdown-' + id).height(), | |
left: e.pageX - 13 | |
}).fadeIn(options.fadeSpeed); | |
} else if (typeof options.above == 'string' && options.above == 'auto') { | |
$dd.removeClass('dropdown-context-up'); | |
var autoH = $dd.height() + 12; | |
if ((e.pageY + autoH) > $('html').height()) { | |
$dd.addClass('dropdown-context-up').css({ | |
top: e.pageY - 20 - autoH, | |
left: e.pageX - 13 | |
}).fadeIn(options.fadeSpeed); | |
} else { | |
$dd.css({ | |
top: e.pageY + 10, | |
left: e.pageX - 13 | |
}).fadeIn(options.fadeSpeed); | |
} | |
} | |
if (typeof options.left == 'boolean' && options.left) { | |
$dd.addClass('dropdown-context-left').css({ | |
left: e.pageX - $dd.width() | |
}).fadeIn(options.fadeSpeed); | |
} else if (typeof options.left == 'string' && options.left == 'auto') { | |
$dd.removeClass('dropdown-context-left'); | |
var autoL = $dd.width() - 12; | |
if ((e.pageX + autoL) > $('html').width()) { | |
$dd.addClass('dropdown-context-left').css({ | |
left: e.pageX - $dd.width() + 13 | |
}); | |
} | |
} | |
$dd.on('mouseleave', function() { | |
startMenuCloseCountdown($dd) | |
}) | |
startMenuCloseCountdown($dd) | |
}); | |
} | |
function startMenuCloseCountdown(menuSelector) { | |
setTimeout(function() { | |
beginMenuClose(menuSelector) | |
}, 2000); | |
} | |
function beginMenuClose(menuSelector) { | |
var checkClose = function() { | |
var mouseEl = $(this); | |
$("*").off("mouseenter", checkClose); | |
closeMenu(mouseEl, menuSelector) | |
} | |
$("*").on("mouseenter", checkClose); | |
} | |
function closeMenu(mouseEl, menuSelector) { | |
if (mouseEl.parents("#" + menuSelector[0].id).length === 0) { | |
if (!menuSelector) { return } | |
$(menuSelector).fadeOut(options.fadeSpeed, function(){ | |
$(menuSelector).css({display:''}).find('.drop-left').removeClass('drop-left'); | |
}) | |
} | |
} | |
function destroyContext(selector) { | |
var element; | |
$(selector).off('contextmenu'); | |
element = this._menus[selector.attr("id")]; | |
if (element) { | |
element.remove(); | |
delete this._menus[selector.attr("id")]; | |
} | |
} | |
return { | |
init: initialize, | |
settings: updateOptions, | |
attach: addContext, | |
destroy: destroyContext, | |
_menus: _menus | |
}; | |
})(); | |
var createCallback = function(func) { | |
return function(event) { func(event, currentContextSelector) }; | |
} | |
currentContextSelector = undefined; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment