/* jquery.cxtmenu.js */ /** * This file is part of cytoscape.js 2.0.2. * * Cytoscape.js is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Cytoscape.js is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License along with * cytoscape.js. If not, see . */ ;(function($){ var defaults = { menuRadius: 100, cytoscape: true, selector: undefined, commands: [], fillColor: 'rgba(0, 0, 0, 0.75)', activeFillColor: 'rgba(92, 194, 237, 0.75)', activePadding: 20, indicatorSize: 30, indicatorColor: 'black', separatorWidth: 3, spotlightPadding: 4, itemColor: 'white', itemTextShadowColor: 'black' }; $.fn.cxtmenu = function(params){ var options = $.extend(true, {}, defaults, params); var fn = params; var $container = $(this); var cy; $container.cytoscape(function(e){ cy = this; }); var functions = { destroy: function(){ }, init: function(){ var $parent = $('
'); var $canvas = $(''); var c2d = $canvas[0].getContext('2d'); var r = options.menuRadius; var offset = $container.offset(); var containerSize = (r + options.activePadding)*2; var activeCommandI = undefined; $container.append( $parent ); $parent.append( $canvas ); $parent.css({ width: containerSize + 'px', height: containerSize + 'px', position: 'fixed', zIndex: 999999, marginLeft: offset.left - options.activePadding + 'px', marginTop: offset.top - options.activePadding + 'px' }).hide(); $canvas[0].width = containerSize; $canvas[0].height = containerSize; var commands = options.commands; var dtheta = 2*Math.PI/(commands.length); var theta1 = commands.length % 2 !== 0 ? Math.PI/2 : 0; var theta2 = theta1 + dtheta; var $items = []; for( var i = 0; i < commands.length; i++ ){ var command = commands[i]; var midtheta = (theta1 + theta2)/2; var rx1 = 0.66 * r * Math.cos( midtheta ); var ry1 = 0.66 * r * Math.sin( midtheta ); // console.log(rx1, ry1, theta1, theta2) var $item = $('
'); $item.css({ color: options.itemColor, cursor: 'default', display: 'table', 'text-align': 'center', //background: 'red', position: 'absolute', 'text-shadow': '-1px -1px ' + options.itemTextShadowColor + ', 1px -1px ' + options.itemTextShadowColor + ', -1px 1px ' + options.itemTextShadowColor + ', 1px 1px ' + options.itemTextShadowColor, left: '50%', top: '50%', 'min-height': r * 0.66, width: r * 0.66, height: r * 0.66, marginLeft: rx1 - r * 0.33, marginTop: -ry1 -r * 0.33 }); var $content = $('
' + command.content + '
'); $content.css({ 'width': r * 0.66, 'height': r * 0.66, 'vertical-align': 'middle', 'display': 'table-cell' }); $parent.append( $item ); $item.append( $content ); theta1 += dtheta; theta2 += dtheta; } function drawBg( rspotlight ){ rspotlight = rspotlight !== undefined ? rspotlight : rs; c2d.globalCompositeOperation = 'source-over'; c2d.clearRect(0, 0, containerSize, containerSize); c2d.fillStyle = options.fillColor; c2d.beginPath(); c2d.arc(r + options.activePadding, r + options.activePadding, r, 0, Math.PI*2, true); c2d.closePath(); c2d.fill(); c2d.globalCompositeOperation = 'destination-out'; c2d.strokeStyle = 'white'; c2d.lineWidth = options.separatorWidth; var commands = options.commands; var dtheta = 2*Math.PI/(commands.length); var theta1 = commands.length % 2 !== 0 ? Math.PI/2 : 0; var theta2 = theta1 + dtheta; for( var i = 0; i < commands.length; i++ ){ var command = commands[i]; var rx1 = r * Math.cos(theta1); var ry1 = r * Math.sin(theta1); c2d.beginPath(); c2d.moveTo(r + options.activePadding, r + options.activePadding); c2d.lineTo(r + options.activePadding + rx1, r + options.activePadding - ry1); c2d.closePath(); c2d.stroke(); // var rx2 = r * Math.cos(theta2); // var ry2 = r * Math.sin(theta2); // c2d.moveTo(r, r); // c2d.lineTo(r + rx2, r + ry2); // c2d.stroke(); theta1 += dtheta; theta2 += dtheta; } c2d.fillStyle = 'white'; c2d.globalCompositeOperation = 'destination-out'; c2d.beginPath(); c2d.arc(r + options.activePadding, r + options.activePadding, rspotlight + options.spotlightPadding, 0, Math.PI*2, true); c2d.closePath(); c2d.fill(); c2d.globalCompositeOperation = 'source-over'; } var lastCallTime = 0; var minCallDelta = 1000/30; var endCallTimeout; var firstCall = true; function rateLimitedCall( fn ){ var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; var now = +new Date; clearTimeout( endCallTimeout ); if( firstCall || now >= lastCallTime + minCallDelta ){ requestAnimationFrame(fn); lastCallTime = now; firstCall = false; } else { endCallTimeout = setTimeout(function(){ requestAnimationFrame(fn); lastCallTime = now; }, minCallDelta * 2); } } var ctrx, ctry, rs; var tapendHandler; cy .on('cxttapstart', options.selector, function(e){ var ele = this; var rp = ele.renderedPosition(); var rw = ele.renderedWidth(); var rh = ele.renderedHeight(); var scrollLeft = $(window).scrollLeft(); var scrollTop = $(window).scrollTop(); ctrx = rp.x; ctry = rp.y; $parent.show().css({ 'left': rp.x - r - scrollLeft, 'top': rp.y - r - scrollTop }); rs = Math.max(rw, rh); rs = 32; drawBg(); activeCommandI = undefined; }) .on('cxtdrag', options.selector, function(e){ rateLimitedCall(function(){ var dx = e.originalEvent.pageX - $container.offset().left - ctrx; var dy = e.originalEvent.pageY - $container.offset().top - ctry; if( dx === 0 ){ dx = 0.01; } var d = Math.sqrt( dx*dx + dy*dy ); var cosTheta = (dy*dy - d*d - dx*dx)/(-2 * d * dx); var theta = Math.acos( cosTheta ); activeCommandI = undefined; if( d < rs + options.spotlightPadding ){ drawBg(); return; } drawBg(); var rx = dx*r / d; var ry = dy*r / d; if( dy > 0 ){ theta = Math.PI + Math.abs(theta - Math.PI); } var commands = options.commands; var dtheta = 2*Math.PI/(commands.length); var theta1 = commands.length % 2 !== 0 ? Math.PI/2 : 0; var theta2 = theta1 + dtheta; for( var i = 0; i < commands.length; i++ ){ var command = commands[i]; // console.log(i, theta1, theta, theta2); var inThisCommand = theta1 <= theta && theta <= theta2 || theta1 <= theta + 2*Math.PI && theta + 2*Math.PI <= theta2; if( inThisCommand ){ // console.log('in command ' + i) c2d.fillStyle = options.activeFillColor; c2d.strokeStyle = 'black'; c2d.lineWidth = 1; c2d.beginPath(); c2d.moveTo(r + options.activePadding, r + options.activePadding); c2d.arc(r + options.activePadding, r + options.activePadding, r + options.activePadding, 2*Math.PI - theta1, 2*Math.PI - theta2, true); c2d.closePath(); c2d.fill(); //c2d.stroke(); activeCommandI = i; break; } theta1 += dtheta; theta2 += dtheta; } c2d.fillStyle = 'white'; c2d.globalCompositeOperation = 'destination-out'; // clear the indicator c2d.beginPath(); //c2d.arc(r + rx/r*(rs + options.spotlightPadding), r + ry/r*(rs + options.spotlightPadding), options.indicatorSize, 0, 2*Math.PI, true); c2d.translate( r + options.activePadding + rx/r*(rs + options.spotlightPadding - options.indicatorSize/4), r + options.activePadding + ry/r*(rs + options.spotlightPadding - options.indicatorSize/4) ); c2d.rotate( Math.PI/4 - theta ); c2d.fillRect(-options.indicatorSize/2, -options.indicatorSize/2, options.indicatorSize, options.indicatorSize); c2d.closePath(); c2d.fill(); c2d.setTransform(1, 0, 0, 1, 0, 0); // clear the spotlight c2d.beginPath(); c2d.arc(r + options.activePadding, r + options.activePadding, rs + options.spotlightPadding, 0, Math.PI*2, true); c2d.closePath(); c2d.fill(); c2d.globalCompositeOperation = 'source-over'; }) }) .on('cxttapend', options.selector, function(e){ var ele = this; $parent.hide(); if( activeCommandI !== undefined ){ var select = options.commands[ activeCommandI ].select; if( select ){ select.apply( ele ); } } }) .on('cxttapend', function(e){ $parent.hide(); }) ; } }; if( functions[fn] ){ return functions[fn].apply(this, Array.prototype.slice.call( arguments, 1 )); } else if( typeof fn == 'object' || !fn ) { return functions.init.apply( this, arguments ); } else { $.error("No such function `"+ fn +"` for jquery.cxtmenu"); } return $(this); }; })(jQuery);