123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /*!
- * 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 };
- if (this.isDisabled()) return;
- this.closemenu();
- if (!this.before.call(this,e,$(e.currentTarget))) return;
- $menu = this.getMenu();
- $menu.trigger(evt = $.Event('show.bs.context', relatedTarget));
- tp = this.getPosition(e, $menu);
- items = '.dropdown-item';
- $menu.attr('style', '')
- .css(tp)
- .addClass('show')
- .on('click.context.data-api', items, $.proxy(this.onItem, this, $(e.currentTarget)))
- .trigger('shown.bs.context', relatedTarget);
- $menu.children('.dropdown-menu').addClass('show');
- // Delegating the `closemenu` only on the currently showed menu.
- // This prevents other showed 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('show')) return;
- relatedTarget = { relatedTarget: this };
- $menu.trigger(evt = $.Event('hide.bs.context', relatedTarget));
- items = '.dropdown-item';
- $menu.removeClass('show')
- .off('click.context.data-api', items)
- .trigger('hidden.bs.context', relatedTarget);
- $menu.children('.dropdown-menu').removeClass('show');
- $('html')
- .off('click.context.data-api', $menu.selector);
- // Don't propagate click event so other currently
- // showed menus won't close.
- return false;
- }
- ,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));
|