// jQuery Context Menu Plugin
//
// Version 1.00
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
//
// Visit http://abeautifulsite.net/notebook/80 for usage and more information
//
// Terms of Use
//
// This software is licensed under a Creative Commons License and is copyrighted
// (C)2008 by Cory S.N. LaViska.
//
// For details, visit http://creativecommons.org/licenses/by/3.0/us/
//
// 02.01.2009 Ossig o.leftButton like "Modified 11NOV08 By Bill Beckelman to include option to use left mouse button instead".
// 05.02.2010 Ossig o.initialOpen. Mix in prototype Function.bind, see http://www.jimmycuadra.com/blog/15-organizing-javascript-with-namespaces-and-function-prototypes.
// 05.02.2010 Ossig o.callbackClose.

if (jQuery) (function($) {
	$.extend($.fn, {

		contextMenu: function(o, callbackSelect) {
			// Defaults
			if (o.menu == undefined) return false;
			if (o.inSpeed == undefined) o.inSpeed = 150;
			if (o.outSpeed == undefined) o.outSpeed = 75;
			// 0 needs to be -1 for expected results (no fade)
			if (o.inSpeed == 0) o.inSpeed = -1;
			if (o.outSpeed == 0) o.outSpeed = -1;
			// Loop each context menu
			function triggerEvent(onEvent) {
				var evt = {};
				onEvent(evt);
			}
			var listenContextButtonMouseDown = o.initialOpen ? triggerEvent : $(this).mousedown.bind($(this));
			var listenContextButtonMouseUp = o.initialOpen ? triggerEvent : $(this).mouseup.bind($(this));
			$(this).each(function() {
				var el = $(this); // 20.01.2010 Ossig contextButton
				// var elOffset = $(el).offset(); 26.01.2010 Ossig May change between mouse clicks.
				// Add contextMenu class
				$('#' + o.menu).addClass('contextMenu');
				// Simulate a true right click
				// $(this).mousedown(function(e) {
				listenContextButtonMouseDown(function(e) {
					var evt = e;
					function onMouseUp(e) {
						var srcElement = $(this); // 20.01.2010 Ossig menuButton
						$(this).unbind('mouseup');
						if (!o.initialOpen) $(el).unbind("contextmenu"); // 05.03.2010 Ossig Disable disabling browser context menu. Has to be called AFTER bind.
						if (evt.button != 2 && !o.leftButton) return false;
						if ($(el).hasClass('disabled')) return true; // 20.10.2010 Ossig return true: Allow event bubble to button.
						// Get this context menu
						var menu = $('#' + o.menu); // 20.01.2010 Ossig Take active context menu.
						var menuIsHidden = menu.is(":hidden");
						// Hide context menus that may be showing
						$(".contextMenu").hide(); // 20.01.2010 Ossig Hide all contextMenu instances.
						if (!menuIsHidden) return true; // 20.10.2010 Ossig return true: Allow event bubble to button.

						var elOffset = $(el).offset();
						// Detect mouse position
						var d = {}, x, y;
						if (self.innerHeight) {
							d.pageYOffset = self.pageYOffset;
							d.pageXOffset = self.pageXOffset;
							d.innerHeight = self.innerHeight;
							d.innerWidth = self.innerWidth;
						} else if (document.documentElement &&
							document.documentElement.clientHeight) {
							d.pageYOffset = document.documentElement.scrollTop;
							d.pageXOffset = document.documentElement.scrollLeft;
							d.innerHeight = document.documentElement.clientHeight;
							d.innerWidth = document.documentElement.clientWidth;
						} else if (document.body) {
							d.pageYOffset = document.body.scrollTop;
							d.pageXOffset = document.body.scrollLeft;
							d.innerHeight = document.body.clientHeight;
							d.innerWidth = document.body.clientWidth;
						}
						/* 02.01.2010 Ossig
						(e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;
						(e.pageY) ? y = e.pageY : x = e.clientY + d.scrollTop;

						var position = el.position();
						x = position.left + el.outerWidth();
						y = position.top + el.outerHeight();

						x = elOffset.left + (el.outerWidth() - menu.outerWidth()) / 2;
						y = elOffset.top + (el.outerHeight() - menu.outerHeight()) / 2;
						*/

						var menuHeight = menu.outerHeight();
						if (evt.button != 2 && o.leftButton) { // 02.01.2010 Ossig left click.
							// var deltaWidth = el.outerWidth() - menu.outerWidth();
							// x = elOffset.left + (deltaWidth > 0 ? deltaWidth / 2 : 0);
							x = elOffset.left;
							var yBelow = elOffset.top + el.outerHeight();
							var yAbove = elOffset.top - menuHeight;
							// y = (d.innerHeight - yBelow - menuHeight >= yAbove) ? yBelow : yAbove; // 02.01.2010 Ossig More space eighter below or above.
							y = (yBelow + menuHeight > d.pageYOffset + d.innerHeight && yAbove >= d.pageYOffset) ? yAbove : yBelow;
							// y = (yAbove >= d.pageYOffset) ? yAbove : yBelow;
							// el.css({ width: menu.outerWidth() });
							if (el.outerWidth() > menu.outerWidth()) menu.css({ width: el.outerWidth() });
						} else {
							(e.pageX) ? x = e.pageX : x = e.clientX + d.pageXOffset;
							(e.pageY) ? y = e.pageY : x = e.clientY + d.pageYOffset;
							if (y + menuHeight > d.pageYOffset + d.innerHeight && y - menuHeight >= d.pageYOffset) y -= menuHeight;
						}

						// Show the menu
						$(document).unbind('click');
						// var menuOffset = menu.offsetParent().offset(); // 02.01.2010 Ossig menu coord always relative to offsetParent
						// $(menu).css({ top: y - menuOffset.top, left: x - menuOffset.left }).fadeIn(o.inSpeed); // 02.01.2010 Ossig offsetParent of menu has to be body.

						/* 25.01.2010 Ossig via prototype */
						var pMenuOffsetParent = $p(o.menu).getOffsetParent().cumulativeOffset();
						GUI.log("pMenuOffsetParent:", pMenuOffsetParent.left, pMenuOffsetParent.top, pMenuOffsetParent, x, y);
						$(menu).css({ top: y - pMenuOffsetParent.top, left: x - pMenuOffsetParent.left }).fadeIn(o.inSpeed);
						/* 25.01.2010 Ossig via jQuery
						var menuOffsetParent = menu.offsetParent().offset();
						$(menu).css({ top: y - menuOffsetParent.top, left: x - menuOffsetParent.left }).fadeIn(o.inSpeed);
						*/

						// Hover events
						$(menu).find('A').mouseover(function() {
							$(menu).find('LI.hover').removeClass('hover');
							$(this).parent().addClass('hover');
						}).mouseout(function() {
							$(menu).find('LI.hover').removeClass('hover');
						});

						// Keyboard
						$(document).keypress(function(e) {
							switch (e.keyCode) {
								case 38: // up
									if ($(menu).find('LI.hover').size() == 0) {
										$(menu).find('LI:last').addClass('hover');
									} else {
										$(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover');
										if ($(menu).find('LI.hover').size() == 0) $(menu).find('LI:last').addClass('hover');
									}
									break;
								case 40: // down
									if ($(menu).find('LI.hover').size() == 0) {
										$(menu).find('LI:first').addClass('hover');
									} else {
										$(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover');
										if ($(menu).find('LI.hover').size() == 0) $(menu).find('LI:first').addClass('hover');
									}
									break;
								case 13: // enter
									$(menu).find('LI.hover A').trigger('click');
									break;
								case 27: // esc
									$(document).trigger('click');
									break
							}
						});

						// When items are selected
						$('#' + o.menu).find('A').unbind('click');
						$('#' + o.menu).find('LI:not(.disabled) A').click(function() {
							$(document).unbind('click').unbind('keypress');
							$(".contextMenu").hide();
							// Callback
							if (callbackSelect) callbackSelect($(this).attr('href').substr(1), $(srcElement), { x: x - elOffset.left, y: y - elOffset.top, docX: x, docY: y });
							if (o.callbackClose) o.callbackClose();
							return false;
						});

						// Hide bindings
						setTimeout(function() { // Delay for Mozilla
							$(document).click(function() {
								$(document).unbind('click').unbind('keypress');
								$(menu).fadeOut(o.outSpeed);
								if (o.callbackClose) o.callbackClose();
								return false;
							});
						}, 0);
					}
					// $(this).mouseup(onMouseUp);
					listenContextButtonMouseUp(onMouseUp);
				});

				// Disable text selection
				if ($.browser.mozilla) {
					$('#' + o.menu).each(function() { $(this).css({ 'MozUserSelect': 'none' }); });
				} else if ($.browser.msie) {
					$('#' + o.menu).each(function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); });
				} else {
					$('#' + o.menu).each(function() { $(this).bind('mousedown.disableTextSelect', function() { return false; }); });
				}
				// Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome)
				if (!o.initialOpen) $(el).add('UL.contextMenu').bind('contextmenu', function() { return false; });
				// $(el).unbind("contextmenu"); // 05.03.2010 Ossig Disable disabling browser context menu.
			});
			return $(this);
		},

		// Disable context menu items on the fly
		disableContextMenuItems: function(o) {
			if (o == undefined) {
				// Disable all
				$(this).find('LI').addClass('disabled');
				return ($(this));
			}
			$(this).each(function() {
				if (o != undefined) {
					var d = o.split(',');
					for (var i = 0; i < d.length; i++) {
						$(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled');

					}
				}
			});
			return ($(this));
		},

		// Enable context menu items on the fly
		enableContextMenuItems: function(o) {
			if (o == undefined) {
				// Enable all
				$(this).find('LI.disabled').removeClass('disabled');
				return ($(this));
			}
			$(this).each(function() {
				if (o != undefined) {
					var d = o.split(',');
					for (var i = 0; i < d.length; i++) {
						$(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled');

					}
				}
			});
			return ($(this));
		},

		// Disable context menu(s)
		disableContextMenu: function() {
			$(this).each(function() {
				$(this).addClass('disabled');
			});
			return ($(this));
		},

		// Enable context menu(s)
		enableContextMenu: function() {
			$(this).each(function() {
				$(this).removeClass('disabled');
			});
			return ($(this));
		},

		// Destroy context menu(s)
		destroyContextMenu: function() {
			// Destroy specified context menus
			$(this).each(function() {
				// Disable action
				$(this).unbind('mousedown').unbind('mouseup');
				// var el = $(this);
				// $(el).unbind("contextmenu"); // 05.03.2010 Ossig Disable disabling browser context menu.
			});
			return ($(this));
		}

	});
})(jQuery);