react-sigma-conglei
Version:
Lightweight but powerful library for drawing network graphs built on top of dunnock/react-sigma
409 lines (358 loc) • 12.7 kB
JavaScript
var Sigma =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 130);
/******/ })
/************************************************************************/
/******/ ({
/***/ 130:
/***/ (function(module, exports) {
/*** IMPORTS FROM imports-loader ***/
(function() {
/**
* This plugin provides a method to drag & drop nodes. Check the
* sigma.plugins.dragNodes function doc or the examples/basic.html &
* examples/api-candy.html code samples to know more.
*/
(function() {
'use strict';
if (typeof sigma === 'undefined')
throw 'sigma is not declared';
sigma.utils.pkg('sigma.plugins');
/**
* This function will add `mousedown`, `mouseup` & `mousemove` events to the
* nodes in the `overNode`event to perform drag & drop operations. It uses
* `linear interpolation` [http://en.wikipedia.org/wiki/Linear_interpolation]
* and `rotation matrix` [http://en.wikipedia.org/wiki/Rotation_matrix] to
* calculate the X and Y coordinates from the `cam` or `renderer` node
* attributes. These attributes represent the coordinates of the nodes in
* the real container, not in canvas.
*
* Fired events:
* *************
* startdrag Fired at the beginning of the drag.
* drag Fired while the node is dragged.
* drop Fired at the end of the drag if the node has been dragged.
* dragend Fired at the end of the drag.
*
* Recognized parameters:
* **********************
* @param {sigma} s The related sigma instance.
* @param {renderer} renderer The related renderer instance.
*/
function DragNodes(s, renderer) {
sigma.classes.dispatcher.extend(this);
// A quick hardcoded rule to prevent people from using this plugin with the
// WebGL renderer (which is impossible at the moment):
// if (
// sigma.renderers.webgl &&
// renderer instanceof sigma.renderers.webgl
// )
// throw new Error(
// 'The sigma.plugins.dragNodes is not compatible with the WebGL renderer'
// );
// Init variables:
var _self = this,
_s = s,
_body = document.body,
_renderer = renderer,
_mouse = renderer.container.lastChild,
_camera = renderer.camera,
_node = null,
_prefix = '',
_hoverStack = [],
_hoverIndex = {},
_isMouseDown = false,
_isMouseOverCanvas = false,
_drag = false;
if (renderer instanceof sigma.renderers.svg) {
_mouse = renderer.container.firstChild;
}
// It removes the initial substring ('read_') if it's a WegGL renderer.
if (renderer instanceof sigma.renderers.webgl) {
_prefix = renderer.options.prefix.substr(5);
} else {
_prefix = renderer.options.prefix;
}
renderer.bind('overNode', nodeMouseOver);
renderer.bind('outNode', treatOutNode);
renderer.bind('click', click);
_s.bind('kill', function() {
_self.unbindAll();
});
/**
* Unbind all event listeners.
*/
this.unbindAll = function() {
_mouse.removeEventListener('mousedown', nodeMouseDown);
_body.removeEventListener('mousemove', nodeMouseMove);
_body.removeEventListener('mouseup', nodeMouseUp);
_renderer.unbind('overNode', nodeMouseOver);
_renderer.unbind('outNode', treatOutNode);
}
// Calculates the global offset of the given element more accurately than
// element.offsetTop and element.offsetLeft.
function calculateOffset(element) {
var style = window.getComputedStyle(element);
var getCssProperty = function(prop) {
return parseInt(style.getPropertyValue(prop).replace('px', '')) || 0;
};
return {
left: element.getBoundingClientRect().left + getCssProperty('padding-left'),
top: element.getBoundingClientRect().top + getCssProperty('padding-top')
};
};
function click(event) {
// event triggered at the end of the click
_isMouseDown = false;
_body.removeEventListener('mousemove', nodeMouseMove);
_body.removeEventListener('mouseup', nodeMouseUp);
if (!_hoverStack.length) {
_node = null;
}
};
function nodeMouseOver(event) {
// Don't treat the node if it is already registered
if (_hoverIndex[event.data.node.id]) {
return;
}
// Add node to array of current nodes over
_hoverStack.push(event.data.node);
_hoverIndex[event.data.node.id] = true;
if(_hoverStack.length && ! _isMouseDown) {
// Set the current node to be the last one in the array
_node = _hoverStack[_hoverStack.length - 1];
_mouse.addEventListener('mousedown', nodeMouseDown);
}
};
function treatOutNode(event) {
// Remove the node from the array
var indexCheck = _hoverStack.map(function(e) { return e; }).indexOf(event.data.node);
_hoverStack.splice(indexCheck, 1);
delete _hoverIndex[event.data.node.id];
if(_hoverStack.length && ! _isMouseDown) {
// On out, set the current node to be the next stated in array
_node = _hoverStack[_hoverStack.length - 1];
} else {
_mouse.removeEventListener('mousedown', nodeMouseDown);
}
};
function nodeMouseDown(event) {
_isMouseDown = true;
var size = _s.graph.nodes().length;
// when there is only node in the graph, the plugin cannot apply
// linear interpolation. So treat it as if a user is dragging
// the graph
if (_node && size > 1) {
_mouse.removeEventListener('mousedown', nodeMouseDown);
_body.addEventListener('mousemove', nodeMouseMove);
_body.addEventListener('mouseup', nodeMouseUp);
// Do not refresh edgequadtree during drag:
var k,
c;
for (k in _s.cameras) {
c = _s.cameras[k];
if (c.edgequadtree !== undefined) {
c.edgequadtree._enabled = false;
}
}
// Deactivate drag graph.
_renderer.settings({mouseEnabled: false, enableHovering: false});
_s.refresh();
_self.dispatchEvent('startdrag', {
node: _node,
captor: event,
renderer: _renderer
});
}
};
function nodeMouseUp(event) {
_isMouseDown = false;
_mouse.addEventListener('mousedown', nodeMouseDown);
_body.removeEventListener('mousemove', nodeMouseMove);
_body.removeEventListener('mouseup', nodeMouseUp);
// Allow to refresh edgequadtree:
var k,
c;
for (k in _s.cameras) {
c = _s.cameras[k];
if (c.edgequadtree !== undefined) {
c.edgequadtree._enabled = true;
}
}
// Activate drag graph.
_renderer.settings({mouseEnabled: true, enableHovering: true});
_s.refresh();
if (_drag) {
_self.dispatchEvent('drop', {
node: _node,
captor: event,
renderer: _renderer
});
}
_self.dispatchEvent('dragend', {
node: _node,
captor: event,
renderer: _renderer
});
_drag = false;
_node = null;
};
function nodeMouseMove(event) {
if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
clearTimeout(timeOut);
var timeOut = setTimeout(executeNodeMouseMove, 0);
} else {
executeNodeMouseMove();
}
function executeNodeMouseMove() {
var offset = calculateOffset(_renderer.container),
x = event.clientX - offset.left,
y = event.clientY - offset.top,
cos = Math.cos(_camera.angle),
sin = Math.sin(_camera.angle),
nodes = _s.graph.nodes(),
ref = [];
// Getting and derotating the reference coordinates.
for (var i = 0; i < 2; i++) {
var n = nodes[i];
var aux = {
x: n.x * cos + n.y * sin,
y: n.y * cos - n.x * sin,
renX: n[_prefix + 'x'],
renY: n[_prefix + 'y'],
};
ref.push(aux);
}
// Applying linear interpolation.
// if the nodes are on top of each other, we use the camera ratio to interpolate
if (ref[0].x === ref[1].x && ref[0].y === ref[1].y) {
var xRatio = (ref[0].renX === 0) ? 1 : ref[0].renX;
var yRatio = (ref[0].renY === 0) ? 1 : ref[0].renY;
x = (ref[0].x / xRatio) * (x - ref[0].renX) + ref[0].x;
y = (ref[0].y / yRatio) * (y - ref[0].renY) + ref[0].y;
} else {
var xRatio = (ref[1].renX - ref[0].renX) / (ref[1].x - ref[0].x);
var yRatio = (ref[1].renY - ref[0].renY) / (ref[1].y - ref[0].y);
// if the coordinates are the same, we use the other ratio to interpolate
if (ref[1].x === ref[0].x) {
xRatio = yRatio;
}
if (ref[1].y === ref[0].y) {
yRatio = xRatio;
}
x = (x - ref[0].renX) / xRatio + ref[0].x;
y = (y - ref[0].renY) / yRatio + ref[0].y;
}
// Rotating the coordinates.
_node.x = x * cos - y * sin;
_node.y = y * cos + x * sin;
_s.refresh();
_drag = true;
_self.dispatchEvent('drag', {
node: _node,
captor: event,
renderer: _renderer
});
}
};
};
/**
* Interface
* ------------------
*
* > var dragNodesListener = sigma.plugins.dragNodes(s, s.renderers[0]);
*/
var _instance = {};
/**
* @param {sigma} s The related sigma instance.
* @param {renderer} renderer The related renderer instance.
*/
sigma.plugins.dragNodes = function(s, renderer) {
// Create object if undefined
if (!_instance[s.id]) {
_instance[s.id] = new DragNodes(s, renderer);
}
s.bind('kill', function() {
sigma.plugins.killDragNodes(s);
});
return _instance[s.id];
};
/**
* This method removes the event listeners and kills the dragNodes instance.
*
* @param {sigma} s The related sigma instance.
*/
sigma.plugins.killDragNodes = function(s) {
if (_instance[s.id] instanceof DragNodes) {
_instance[s.id].unbindAll();
delete _instance[s.id];
}
};
}).call(window);
}.call(window));
/***/ })
/******/ });