(function(root, factory) { if (typeof module === 'object' && module.exports) { module.exports = function(d3) { d3.contextMenu = factory(d3); return d3.contextMenu; }; } else { root.d3.contextMenu = factory(root.d3); } }( this, function(d3) { return function (menu, opts) { var openCallback, closeCallback; if (typeof opts === 'function') { openCallback = opts; } else { opts = opts || {}; openCallback = opts.onOpen; closeCallback = opts.onClose; } // create the div element that will hold the context menu d3.selectAll('.d3-context-menu').data([1]) .enter() .append('div') .attr('class', 'd3-context-menu'); // close menu d3.select('body').on('click.d3-context-menu', function() { d3.select('.d3-context-menu').style('display', 'none'); if (closeCallback) { closeCallback(); } }); // this gets executed when a contextmenu event occurs return function(data, index) { var elm = this; d3.selectAll('.d3-context-menu').html(''); var list = d3.selectAll('.d3-context-menu').append('ul'); list.selectAll('li').data(typeof menu === 'function' ? menu(data) : menu).enter() .append('li') .html(function(d) { return (typeof d.title === 'string') ? d.title : d.title(data); }) .on('click', function(d, i) { d.action(elm, data, index); d3.select('.d3-context-menu').style('display', 'none'); if (closeCallback) { closeCallback(); } }); // the openCallback allows an action to fire before the menu is displayed // an example usage would be closing a tooltip if (openCallback) { if (openCallback(data, index) === false) { return; } } // display context menu d3.select('.d3-context-menu') .style('left', (d3.event.pageX - 2) + 'px') .style('top', (d3.event.pageY - 2) + 'px') .style('display', 'block'); d3.event.preventDefault(); d3.event.stopPropagation(); }; }; } ));