|
|
/*! * Bootstrap Context Menu * Author: @sydcanem * https://github.com/sydcanem/bootstrap-contextmenu
* * Inspired by Bootstrap's dropdown plugin. * Bootstrap (http://getbootstrap.com).
* * Licensed under MIT * ========================================================= */
;(function($) {
'use strict';
/* CONTEXTMENU CLASS DEFINITION * ============================ */ var toggle = '[data-toggle="context"]';
var ContextMenu = function (element, options) { this.$element = $(element);
this.before = options.before || this.before; this.onItem = options.onItem || this.onItem; this.scopes = options.scopes || null;
if (options.target) { this.$element.data('target', options.target); }
this.listen(); };
ContextMenu.prototype = {
constructor: ContextMenu ,show: function(e) {
var $menu , evt , tp , items , relatedTarget = { relatedTarget: this, target: e.currentTarget };
if (this.isDisabled()) return;
this.closemenu();
if (this.before.call(this,e,$(e.currentTarget)) === false) return;
$menu = this.getMenu(); $menu.trigger(evt = $.Event('show.bs.context', relatedTarget));
tp = this.getPosition(e, $menu); items = 'li:not(.divider)'; $menu.attr('style', '') .css(tp) .addClass('open') .on('click.context.data-api', items, $.proxy(this.onItem, this, $(e.currentTarget))) .trigger('shown.bs.context', relatedTarget);
// Delegating the `closemenu` only on the currently opened menu.
// This prevents other opened menus from closing.
$('html') .on('click.context.data-api', $menu.selector, $.proxy(this.closemenu, this));
return false; }
,closemenu: function(e) { var $menu , evt , items , relatedTarget;
$menu = this.getMenu();
if(!$menu.hasClass('open')) return;
relatedTarget = { relatedTarget: this }; $menu.trigger(evt = $.Event('hide.bs.context', relatedTarget));
items = 'li:not(.divider)'; $menu.removeClass('open') .off('click.context.data-api', items) .trigger('hidden.bs.context', relatedTarget);
$('html') .off('click.context.data-api', $menu.selector); // Don't propagate click event so other currently
// opened menus won't close.
if (e) { e.stopPropagation(); } }
,keydown: function(e) { if (e.which == 27) this.closemenu(e); }
,before: function(e) { return true; }
,onItem: function(e) { return true; }
,listen: function () { this.$element.on('contextmenu.context.data-api', this.scopes, $.proxy(this.show, this)); $('html').on('click.context.data-api', $.proxy(this.closemenu, this)); $('html').on('keydown.context.data-api', $.proxy(this.keydown, this)); }
,destroy: function() { this.$element.off('.context.data-api').removeData('context'); $('html').off('.context.data-api'); }
,isDisabled: function() { return this.$element.hasClass('disabled') || this.$element.attr('disabled'); }
,getMenu: function () { var selector = this.$element.data('target') , $menu;
if (!selector) { selector = this.$element.attr('href'); selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7
}
$menu = $(selector);
return $menu && $menu.length ? $menu : this.$element.find(selector); }
,getPosition: function(e, $menu) { var mouseX = e.clientX , mouseY = e.clientY , boundsX = $(window).width() , boundsY = $(window).height() , menuWidth = $menu.find('.dropdown-menu').outerWidth() , menuHeight = $menu.find('.dropdown-menu').outerHeight() , tp = {"position":"absolute","z-index":9999} , Y, X, parentOffset;
if (mouseY + menuHeight > boundsY) { Y = {"top": mouseY - menuHeight + $(window).scrollTop()}; } else { Y = {"top": mouseY + $(window).scrollTop()}; }
if ((mouseX + menuWidth > boundsX) && ((mouseX - menuWidth) > 0)) { X = {"left": mouseX - menuWidth + $(window).scrollLeft()}; } else { X = {"left": mouseX + $(window).scrollLeft()}; }
// If context-menu's parent is positioned using absolute or relative positioning,
// the calculated mouse position will be incorrect.
// Adjust the position of the menu by its offset parent position.
parentOffset = $menu.offsetParent().offset(); X.left = X.left - parentOffset.left; Y.top = Y.top - parentOffset.top;
return $.extend(tp, Y, X); }
};
/* CONTEXT MENU PLUGIN DEFINITION * ========================== */
$.fn.contextmenu = function (option,e) { return this.each(function () { var $this = $(this) , data = $this.data('context') , options = (typeof option == 'object') && option;
if (!data) $this.data('context', (data = new ContextMenu($this, options))); if (typeof option == 'string') data[option].call(data, e); }); };
$.fn.contextmenu.Constructor = ContextMenu;
/* APPLY TO STANDARD CONTEXT MENU ELEMENTS * =================================== */
$(document) .on('contextmenu.context.data-api', function() { $(toggle).each(function () { var data = $(this).data('context'); if (!data) return; data.closemenu(); }); }) .on('contextmenu.context.data-api', toggle, function(e) { $(this).contextmenu('show', e);
e.preventDefault(); e.stopPropagation(); });
}(jQuery));
|