/* 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 = $('');
$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);