diff options
Diffstat (limited to 'trunk/etherpad/src/static/js/lib/jquery.contextmenu.js')
-rw-r--r-- | trunk/etherpad/src/static/js/lib/jquery.contextmenu.js | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/trunk/etherpad/src/static/js/lib/jquery.contextmenu.js b/trunk/etherpad/src/static/js/lib/jquery.contextmenu.js new file mode 100644 index 0000000..42cc17e --- /dev/null +++ b/trunk/etherpad/src/static/js/lib/jquery.contextmenu.js @@ -0,0 +1,284 @@ +/** + * Copyright (c)2005-2009 Matt Kruse (javascripttoolbox.com) + * + * Dual licensed under the MIT and GPL licenses. + * This basically means you can use this code however you want for + * free, but don't claim to have written it yourself! + * Donations always accepted: http://www.JavascriptToolbox.com/donate/ + * + * Please do not link to the .js files on javascripttoolbox.com from + * your site. Copy the files locally to your server instead. + * + */ +/** + * jquery.contextmenu.js + * jQuery Plugin for Context Menus + * http://www.JavascriptToolbox.com/lib/contextmenu/ + * + * Copyright (c) 2008 Matt Kruse (javascripttoolbox.com) + * Dual licensed under the MIT and GPL licenses. + * + * @version 1.0 + * @history 1.0 2008-10-20 Initial Release + * @todo slideUp doesn't work in IE - because of iframe? + * @todo Hide all other menus when contextmenu is shown? + * @todo More themes + * @todo Nested context menus + */ +;(function($){ + $.contextMenu = { + shadow:true, + shadowOffset:0, + shadowOffsetX:5, + shadowOffsetY:5, + shadowWidthAdjust:-3, + shadowHeightAdjust:-3, + shadowOpacity:.2, + shadowClass:'context-menu-shadow', + shadowColor:'black', + + offsetX:0, + offsetY:0, + appendTo:'body', + direction:'down', + constrainToScreen:true, + + showTransition:'show', + hideTransition:'hide', + showSpeed:'', + hideSpeed:'', + showCallback:null, + hideCallback:null, + + className:'context-menu', + itemClassName:'context-menu-item', + itemHoverClassName:'context-menu-item-hover', + disabledItemClassName:'context-menu-item-disabled', + disabledItemHoverClassName:'context-menu-item-disabled-hover', + separatorClassName:'context-menu-separator', + innerDivClassName:'context-menu-item-inner', + themePrefix:'context-menu-theme-', + theme:'default', + + separator:'context-menu-separator', // A specific key to identify a separator + target:null, // The target of the context click, to be populated when triggered + menu:null, // The jQuery object containing the HTML object that is the menu itself + shadowObj:null, // Shadow object + bgiframe:null, // The iframe object for IE6 + shown:false, // Currently being shown? + useIframe:/*@cc_on @*//*@if (@_win32) true, @else @*/false,/*@end @*/ // This is a better check than looking at userAgent! + + bindTarget: 'contextmenu', + + // Create the menu instance + create: function(menu,opts) { + var cmenu = $.extend({},this,opts); // Clone all default properties to created object + + // If a selector has been passed in, then use that as the menu + if (typeof menu=="string") { + cmenu.menu = $(menu); + } + // If a function has been passed in, call it each time the menu is shown to create the menu + else if (typeof menu=="function") { + cmenu.menuFunction = menu; + } + // Otherwise parse the Array passed in + else { + cmenu.menu = cmenu.createMenu(menu,cmenu); + } + if (cmenu.menu) { + cmenu.menu.css({display:'none'}); + $(cmenu.appendTo).append(cmenu.menu); + } + + // Create the shadow object if shadow is enabled + if (cmenu.shadow) { + cmenu.createShadow(cmenu); // Extracted to method for extensibility + if (cmenu.shadowOffset) { cmenu.shadowOffsetX = cmenu.shadowOffsetY = cmenu.shadowOffset; } + } + + // when to hide the menu: + // click anywhere in the body: + $('body').bind('click',function(){cmenu.hide();}); + // right-click anywhere in the body: + $('body').bind('contextmenu',function(){cmenu.hide();}); + // escape key: + $(document).keyup(function(e) { if (e.keyCode == 27) { cmenu.hide(); } }); + + return cmenu; + }, + + // Create an iframe object to go behind the menu + createIframe: function() { + return $('<iframe frameborder="0" tabindex="-1" src="javascript:false" style="display:block;position:absolute;z-index:-1;filter:Alpha(Opacity=0);"/>'); + }, + + // Accept an Array representing a menu structure and turn it into HTML + createMenu: function(menu,cmenu) { + var className = cmenu.className; + $.each(cmenu.theme.split(","),function(i,n){className+=' '+cmenu.themePrefix+n}); + var $t = $('<table cellspacing=0 cellpadding=0></table>').click(function(){cmenu.hide(); return false;}); // We wrap a table around it so width can be flexible + var $tr = $('<tr></tr>'); + var $td = $('<td></td>'); + var $div = $('<div class="'+className+'"></div>'); + + // Each menu item is specified as either: + // title:function + // or title: { property:value ... } + for (var i=0; i<menu.length; i++) { + var m = menu[i]; + if (m==$.contextMenu.separator) { + $div.append(cmenu.createSeparator()); + } + else { + for (var opt in menu[i]) { + $div.append(cmenu.createMenuItem(opt,menu[i][opt])); // Extracted to method for extensibility + } + } + } + if ( cmenu.useIframe ) { + $td.append(cmenu.createIframe()); + } + $t.append($tr.append($td.append($div))) + return $t; + }, + + // Create an individual menu item + createMenuItem: function(label,obj) { + var cmenu = this; + if (typeof obj=="function") { obj={onclick:obj}; } // If passed a simple function, turn it into a property of an object + // Default properties, extended in case properties are passed + var o = $.extend({ + onclick:function() { }, + className:'', + hoverClassName:cmenu.itemHoverClassName, + icon:'', + disabled:false, + title:'', + hoverItem:cmenu.hoverItem, + hoverItemOut:cmenu.hoverItemOut + },obj); + // If an icon is specified, hard-code the background-image style. Themes that don't show images should take this into account in their CSS + var iconStyle = (o.icon)?'background-image:url('+o.icon+');':''; + var $div = $('<div class="'+cmenu.itemClassName+' '+o.className+((o.disabled)?' '+cmenu.disabledItemClassName:'')+'" title="'+o.title+'"></div>') + // If the item is disabled, don't do anything when it is clicked + .click(function(e){if(cmenu.isItemDisabled(this)){return false;}else{return o.onclick.call(cmenu.target,this,cmenu,e)}}) + // Change the class of the item when hovered over + .hover( function(){ o.hoverItem.call(this,(cmenu.isItemDisabled(this))?cmenu.disabledItemHoverClassName:o.hoverClassName); } + ,function(){ o.hoverItemOut.call(this,(cmenu.isItemDisabled(this))?cmenu.disabledItemHoverClassName:o.hoverClassName); } + ); + var $idiv = $('<div class="'+cmenu.innerDivClassName+'" style="'+iconStyle+'">'+label+'</div>'); + $div.append($idiv); + return $div; + }, + + // Create a separator row + createSeparator: function() { + return $('<div class="'+this.separatorClassName+'"></div>'); + }, + + // Determine if an individual item is currently disabled. This is called each time the item is hovered or clicked because the disabled status may change at any time + isItemDisabled: function(item) { return $(item).is('.'+this.disabledItemClassName); }, + + // Functions to fire on hover. Extracted to methods for extensibility + hoverItem: function(c) { $(this).addClass(c); }, + hoverItemOut: function(c) { $(this).removeClass(c); }, + + // Create the shadow object + createShadow: function(cmenu) { + cmenu.shadowObj = $('<div class="'+cmenu.shadowClass+'"></div>').css( {display:'none',position:"absolute", zIndex:9998, opacity:cmenu.shadowOpacity, backgroundColor:cmenu.shadowColor } ); + $(cmenu.appendTo).append(cmenu.shadowObj); + }, + + // Display the shadow object, given the position of the menu itself + showShadow: function(x,y,e) { + var cmenu = this; + if (cmenu.shadow) { + cmenu.shadowObj.css( { + width:(cmenu.menu.width()+cmenu.shadowWidthAdjust)+"px", + height:(cmenu.menu.height()+cmenu.shadowHeightAdjust)+"px", + top:(y+cmenu.shadowOffsetY)+"px", + left:(x+cmenu.shadowOffsetX)+"px" + }).addClass(cmenu.shadowClass)[cmenu.showTransition](cmenu.showSpeed); + } + }, + + // A hook to call before the menu is shown, in case special processing needs to be done. + // Return false to cancel the default show operation + beforeShow: function() { return true; }, + + // Show the context menu + show: function(t,e) { + var cmenu=this, x=e.pageX, y=e.pageY; + cmenu.target = t; // Preserve the object that triggered this context menu so menu item click methods can see it + if (cmenu.beforeShow()!==false) { + // If the menu content is a function, call it to populate the menu each time it is displayed + if (cmenu.menuFunction) { + if (cmenu.menu) { $(cmenu.menu).remove(); } + cmenu.menu = cmenu.createMenu(cmenu.menuFunction(cmenu,t),cmenu); + cmenu.menu.css({display:'none'}); + $(cmenu.appendTo).append(cmenu.menu); + } + var $c = cmenu.menu; + x+=cmenu.offsetX; y+=cmenu.offsetY; + var pos = cmenu.getPosition(x,y,cmenu,e); // Extracted to method for extensibility + cmenu.showShadow(pos.x,pos.y,e); + // Resize the iframe if needed + if (cmenu.useIframe) { + $c.find('iframe').css({width:$c.width()+cmenu.shadowOffsetX+cmenu.shadowWidthAdjust,height:$c.height()+cmenu.shadowOffsetY+cmenu.shadowHeightAdjust}); + } + $c.css( {top:pos.y+"px", left:pos.x+"px", position:"absolute",zIndex:9999} )[cmenu.showTransition](cmenu.showSpeed,((cmenu.showCallback)?function(){cmenu.showCallback.call(cmenu);}:null)); + cmenu.shown=true; + $(document).one('click',null,function(){cmenu.hide()}); // Handle a single click to the document to hide the menu + } + }, + + // Find the position where the menu should appear, given an x,y of the click event + getPosition: function(clickX,clickY,cmenu,e) { + var x = clickX+cmenu.offsetX; + var y = clickY+cmenu.offsetY + var h = $(cmenu.menu).height(); + var w = $(cmenu.menu).width(); + var dir = cmenu.direction; + if (cmenu.constrainToScreen) { + var $w = $(window); + var wh = $w.height(); + var ww = $w.width(); + if (dir=="down" && (y+h-$w.scrollTop() > wh)) { dir = "up"; } + var maxRight = x+w-$w.scrollLeft(); + if (maxRight > ww) { x -= (maxRight-ww); } + } + if (dir=="up") { y -= h; } + return {'x':x,'y':y}; + }, + + // Hide the menu, of course + hide: function() { + var cmenu=this; + if (cmenu.shown) { + if (cmenu.iframe) { + $(cmenu.iframe).hide(); + } + if (cmenu.menu) { + cmenu.menu[cmenu.hideTransition](cmenu.hideSpeed,null); + } + if (cmenu.shadow) { + cmenu.shadowObj[cmenu.hideTransition](cmenu.hideSpeed); + } + if (cmenu.hideCallback) { + cmenu.hideCallback.call(cmenu); + } + } + cmenu.shown = false; + } + }; + + // This actually adds the .contextMenu() function to the jQuery namespace + $.fn.contextMenu = function(menu,options) { + var cmenu = $.contextMenu.create(menu,options); + return this.each(function(){ + $(this).bind(cmenu.bindTarget,function(e){cmenu.show(this,e);return false;}); + }); + }; +})(jQuery); + |