Updates the plugin so that it can support multiple instances.

Multiple instances need to be applied to different elements in the DOM
otherwise multiple menus will be called for each click/right click.

e.g.

jQuery(document).ContextMenu(url);

would work for any form on a page. Using this would mean that multiple
instances couldn't be used though

jQuery('#content form').eq(0).ContextMenu(url);
jQuery('#content form').eq(3).ContextMenu(url);

Using the above 2 menus will be created for the first and 4th forms on
the page. Any of forms won't response to menu clicks.

jQuery(docuemnt).ContextMenu(url);
jQuery('#content form').eq(0).ContextMenu(url);

With the above any form on the page will respond to menu clicks but the
first form will send 2 requests for the context menu.
This commit is contained in:
Andrew Smith 2012-02-24 09:12:29 +10:00 committed by Felix Schäfer
parent 2b640f76ec
commit a510f0a85f

View File

@ -1,212 +1,217 @@
(function($) { (function($) {
var observingContextMenuClick; var ContextMenuClass = function(el, options) {
var url; var element = el;
var lastSelected; var opts = options;
var menu; var observingContextMenuClick;
var menuId = 'context-menu'; var lastSelected = null;
var selectorName = 'hascontextmenu'; var menu;
var contextMenuSelectionClass = 'context-menu-selection'; var menuId = 'context-menu';
var reverseXClass = 'reverse-x'; var selectorName = 'hascontextmenu';
var reverseYClass = 'reverse-y'; var contextMenuSelectionClass = 'context-menu-selection';
var reverseXClass = 'reverse-x';
var reverseYClass = 'reverse-y';
var methods = { var methods = {
createMenu: function() { createMenu: function() {
if(!menu) { if(!menu) {
$('#wrapper').append('<div id="' + menuId + '" style="display:none"></div>'); $('#wrapper').append('<div id="' + menuId + '" style="display:none"></div>');
menu = $('#' + menuId); menu = $('#' + menuId);
} }
}, },
click: function(e) { click: function(e) {
var target = $(e.target); var target = $(e.target);
if(target.is('a')) { if(target.is('a')) {
return;
}
switch(e.which) {
case 1:
if(e.type === 'click') {
methods.hideMenu();
methods.leftClick(e);
break;
}
case 3:
if(e.type === 'contextmenu') {
methods.hideMenu();
methods.rightClick(e);
break;
}
default:
return; return;
} }
},
leftClick: function(e) { switch(e.which) {
var target = $(e.target); case 1:
var tr = target.parents('tr'); if(e.type === 'click') {
if((tr.size() > 0) && tr.hasClass(selectorName)) methods.hideMenu();
{ methods.leftClick(e);
// a row was clicked, check if the click was on checkbox break;
if(target.is('input')) }
case 3:
if(e.type === 'contextmenu') {
methods.hideMenu();
methods.rightClick(e);
break;
}
default:
return;
}
},
leftClick: function(e) {
var target = $(e.target);
var tr = target.parents('tr');
if((tr.size() > 0) && tr.hasClass(selectorName))
{ {
// a checkbox may be clicked // a row was clicked, check if the click was on checkbox
if (target.is(':checked')) { if(target.is('input'))
tr.addClass(contextMenuSelectionClass); {
} else { // a checkbox may be clicked
tr.removeClass(contextMenuSelectionClass); if (target.is(':checked')) {
tr.addClass(contextMenuSelectionClass);
} else {
tr.removeClass(contextMenuSelectionClass);
}
}
else
{
if (e.ctrlKey || e.metaKey)
{
methods.toggleSelection(tr);
}
else if (e.shiftKey)
{
if (lastSelected !== null)
{
var toggling = false;
var rows = $(selectorName);
for (i = 0; i < rows.length; i++)
{
if (toggling || rows[i] == tr)
{
methods.addSelection(rows[i]);
}
if (rows[i] == tr || rows[i] == lastSelected)
{
toggling = !toggling;
}
}
} else {
methods.addSelection(tr);
}
} else {
methods.unselectAll();
methods.addSelection(tr);
}
lastSelected = tr;
} }
} }
else else
{ {
if (e.ctrlKey || e.metaKey) // click is outside the rows
{ if (target.is('a') === false) {
methods.toggleSelection(tr); this.unselectAll();
}
else if (e.shiftKey)
{
if (lastSelected !== null)
{
var toggling = false;
var rows = $(selectorName);
for (i = 0; i < rows.length; i++)
{
if (toggling || rows[i] == tr)
{
methods.addSelection(rows[i]);
}
if (rows[i] == tr || rows[i] == lastSelected)
{
toggling = !toggling;
}
}
} else {
methods.addSelection(tr);
}
} else { } else {
methods.unselectAll(); if (target.hasClass('disabled') || target.hasClass('submenu')) {
methods.addSelection(tr); e.preventDefault();
}
} }
}
},
rightClick: function(e) {
var target = $(e.target);
var tr = target.parents('tr');
if((tr.size() === 0) || !(tr.hasClass(selectorName))) {
return;
}
e.preventDefault();
if(!methods.isSelected(tr)) {
methods.unselectAll();
methods.addSelection(tr);
lastSelected = tr; lastSelected = tr;
} }
} methods.showMenu(e);
else },
{ unselectAll: function() {
// click is outside the rows var rows = $('.' + contextMenuSelectionClass);
if (target.is('a') === false) { rows.each(function() {
this.unselectAll(); methods.removeSelection($(this));
});
},
hideMenu: function() {
menu.hide();
},
showMenu: function(e) {
var target = $(e.target);
var params = target.parents('form').serialize();
var mouseX = e.pageX;
var mouseY = e.pageY;
var renderX = mouseX;
var renderY = mouseY;
$.ajax({
url: opts.url,
data: params,
success: function(response, success) {
menu.html(response);
var maxWidth = mouseX + (2 * menu.width());
var maxHeight = mouseY + menu.height();
if(maxWidth > $(window).width()) {
renderX -= menu.width();
menu.addClass(reverseXClass);
} else {
menu.removeClass(reverseXClass);
}
if(maxHeight > $(window).height()) {
renderY =+ menu.height();
menu.addClass(reverseYClass);
} else {
menu.removeClass(reverseYClass);
}
if(renderX <= 0) {
renderX = 1;
}
if(renderY <= 0) {
renderY = 1;
}
menu.css('top', renderY).css('left', renderX);
menu.show();
}
});
},
addSelection: function(element) {
element.addClass(contextMenuSelectionClass);
methods.checkSelectionBox(element, true);
},
isSelected: function(element) {
return element.hasClass(contextMenuSelectionClass);
},
toggleSelection: function(element) {
if(methods.isSelected(element)) {
methods.removeSelection(element);
} else { } else {
if (target.hasClass('disabled') || target.hasClass('submenu')) { methods.addSelection(element);
e.preventDefault();
}
} }
},
removeSelection: function(element) {
element.removeClass(contextMenuSelectionClass);
methods.checkSelectionBox(element, false);
},
checkSelectionBox: function(element, checked) {
var inputs = element.find('input');
inputs.each(function() {
inputs.attr('checked', checked ? 'checked' : false);
});
} }
}, };
rightClick: function(e) {
var target = $(e.target);
var tr = target.parents('tr');
if((tr.size() === 0) || !(tr.hasClass(selectorName))) {
return;
}
e.preventDefault();
if(!methods.isSelected(tr)) {
methods.unselectAll();
methods.addSelection(tr);
lastSelected = tr;
}
methods.showMenu(e);
},
unselectAll: function() {
var rows = $('.' + contextMenuSelectionClass);
rows.each(function() {
methods.removeSelection($(this));
});
},
hideMenu: function() {
menu.hide();
},
showMenu: function(e) {
var target = $(e.target);
var params = target.parents('form').serialize();
var mouseX = e.pageX;
var mouseY = e.pageY;
var renderX = mouseX;
var renderY = mouseY;
$.ajax({
url: url,
data: params,
success: function(response, success) {
menu.html(response);
var maxWidth = mouseX + (2 * menu.width());
var maxHeight = mouseY + menu.height();
if(maxWidth > $(window).width()) {
renderX -= menu.width();
menu.addClass(reverseXClass);
} else {
menu.removeClass(reverseXClass);
}
if(maxHeight > $(window).height()) {
renderY =+ menu.height();
menu.addClass(reverseYClass);
} else {
menu.removeClass(reverseYClass);
}
if(renderX <= 0) {
renderX = 1;
}
if(renderY <= 0) {
renderY = 1;
}
menu.css('top', renderY).css('left', renderX);
menu.show();
}
});
},
addSelection: function(element) {
element.addClass(contextMenuSelectionClass);
methods.checkSelectionBox(element, true);
},
isSelected: function(element) {
return element.hasClass(contextMenuSelectionClass);
},
toggleSelection: function(element) {
if(methods.isSelected(element)) {
methods.removeSelection(element);
} else {
methods.addSelection(element);
}
},
removeSelection: function(element) {
element.removeClass(contextMenuSelectionClass);
methods.checkSelectionBox(element, false);
},
checkSelectionBox: function(element, checked) {
var inputs = element.find('input');
inputs.each(function() {
inputs.attr('checked', checked ? 'checked' : false);
});
}
};
$.fn.ContextMenu = function(u) {
url = u;
methods.createMenu(); methods.createMenu();
if(!observingContextMenuClick) { if(!observingContextMenuClick) {
$(document).bind('click.contextMenu', methods.click); element.bind('click.contextMenu', methods.click);
$(document).bind('contextmenu.contextMenu', methods.click); element.bind('contextmenu.contextMenu', methods.click);
observingContextMenuClick = true; observingContextMenuClick = true;
} }
methods.unselectAll(); methods.unselectAll();
lastSelected = null; };
$.fn.ContextMenu = function(u) {
return this.each(function() {
new ContextMenuClass($(this), {url: u});
});
}; };
})(jQuery); })(jQuery);