458 lines
16 KiB
JavaScript
458 lines
16 KiB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
|
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
module.exports = factory(require("cose-base"));
|
|
else if(typeof define === 'function' && define.amd)
|
|
define(["cose-base"], factory);
|
|
else if(typeof exports === 'object')
|
|
exports["cytoscapeCoseBilkent"] = factory(require("cose-base"));
|
|
else
|
|
root["cytoscapeCoseBilkent"] = factory(root["coseBase"]);
|
|
})(this, function(__WEBPACK_EXTERNAL_MODULE_0__) {
|
|
return /******/ (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 = 1);
|
|
/******/ })
|
|
/************************************************************************/
|
|
/******/ ([
|
|
/* 0 */
|
|
/***/ (function(module, exports) {
|
|
|
|
module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
|
|
|
|
/***/ }),
|
|
/* 1 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
|
|
var LayoutConstants = __webpack_require__(0).layoutBase.LayoutConstants;
|
|
var FDLayoutConstants = __webpack_require__(0).layoutBase.FDLayoutConstants;
|
|
var CoSEConstants = __webpack_require__(0).CoSEConstants;
|
|
var CoSELayout = __webpack_require__(0).CoSELayout;
|
|
var CoSENode = __webpack_require__(0).CoSENode;
|
|
var PointD = __webpack_require__(0).layoutBase.PointD;
|
|
var DimensionD = __webpack_require__(0).layoutBase.DimensionD;
|
|
|
|
var defaults = {
|
|
// Called on `layoutready`
|
|
ready: function ready() {},
|
|
// Called on `layoutstop`
|
|
stop: function stop() {},
|
|
// 'draft', 'default' or 'proof"
|
|
// - 'draft' fast cooling rate
|
|
// - 'default' moderate cooling rate
|
|
// - "proof" slow cooling rate
|
|
quality: 'default',
|
|
// include labels in node dimensions
|
|
nodeDimensionsIncludeLabels: false,
|
|
// number of ticks per frame; higher is faster but more jerky
|
|
refresh: 30,
|
|
// Whether to fit the network view after when done
|
|
fit: true,
|
|
// Padding on fit
|
|
padding: 10,
|
|
// Whether to enable incremental mode
|
|
randomize: true,
|
|
// Node repulsion (non overlapping) multiplier
|
|
nodeRepulsion: 4500,
|
|
// Ideal edge (non nested) length
|
|
idealEdgeLength: 50,
|
|
// Divisor to compute edge forces
|
|
edgeElasticity: 0.45,
|
|
// Nesting factor (multiplier) to compute ideal edge length for nested edges
|
|
nestingFactor: 0.1,
|
|
// Gravity force (constant)
|
|
gravity: 0.25,
|
|
// Maximum number of iterations to perform
|
|
numIter: 2500,
|
|
// For enabling tiling
|
|
tile: true,
|
|
// Type of layout animation. The option set is {'during', 'end', false}
|
|
animate: 'end',
|
|
// Duration for animate:end
|
|
animationDuration: 500,
|
|
// Represents the amount of the vertical space to put between the zero degree members during the tiling operation(can also be a function)
|
|
tilingPaddingVertical: 10,
|
|
// Represents the amount of the horizontal space to put between the zero degree members during the tiling operation(can also be a function)
|
|
tilingPaddingHorizontal: 10,
|
|
// Gravity range (constant) for compounds
|
|
gravityRangeCompound: 1.5,
|
|
// Gravity force (constant) for compounds
|
|
gravityCompound: 1.0,
|
|
// Gravity range (constant)
|
|
gravityRange: 3.8,
|
|
// Initial cooling factor for incremental layout
|
|
initialEnergyOnIncremental: 0.5
|
|
};
|
|
|
|
function extend(defaults, options) {
|
|
var obj = {};
|
|
|
|
for (var i in defaults) {
|
|
obj[i] = defaults[i];
|
|
}
|
|
|
|
for (var i in options) {
|
|
obj[i] = options[i];
|
|
}
|
|
|
|
return obj;
|
|
};
|
|
|
|
function _CoSELayout(_options) {
|
|
this.options = extend(defaults, _options);
|
|
getUserOptions(this.options);
|
|
}
|
|
|
|
var getUserOptions = function getUserOptions(options) {
|
|
if (options.nodeRepulsion != null) CoSEConstants.DEFAULT_REPULSION_STRENGTH = FDLayoutConstants.DEFAULT_REPULSION_STRENGTH = options.nodeRepulsion;
|
|
if (options.idealEdgeLength != null) CoSEConstants.DEFAULT_EDGE_LENGTH = FDLayoutConstants.DEFAULT_EDGE_LENGTH = options.idealEdgeLength;
|
|
if (options.edgeElasticity != null) CoSEConstants.DEFAULT_SPRING_STRENGTH = FDLayoutConstants.DEFAULT_SPRING_STRENGTH = options.edgeElasticity;
|
|
if (options.nestingFactor != null) CoSEConstants.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR = FDLayoutConstants.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR = options.nestingFactor;
|
|
if (options.gravity != null) CoSEConstants.DEFAULT_GRAVITY_STRENGTH = FDLayoutConstants.DEFAULT_GRAVITY_STRENGTH = options.gravity;
|
|
if (options.numIter != null) CoSEConstants.MAX_ITERATIONS = FDLayoutConstants.MAX_ITERATIONS = options.numIter;
|
|
if (options.gravityRange != null) CoSEConstants.DEFAULT_GRAVITY_RANGE_FACTOR = FDLayoutConstants.DEFAULT_GRAVITY_RANGE_FACTOR = options.gravityRange;
|
|
if (options.gravityCompound != null) CoSEConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH = options.gravityCompound;
|
|
if (options.gravityRangeCompound != null) CoSEConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR = options.gravityRangeCompound;
|
|
if (options.initialEnergyOnIncremental != null) CoSEConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL = options.initialEnergyOnIncremental;
|
|
|
|
if (options.quality == 'draft') LayoutConstants.QUALITY = 0;else if (options.quality == 'proof') LayoutConstants.QUALITY = 2;else LayoutConstants.QUALITY = 1;
|
|
|
|
CoSEConstants.NODE_DIMENSIONS_INCLUDE_LABELS = FDLayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS = LayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS = options.nodeDimensionsIncludeLabels;
|
|
CoSEConstants.DEFAULT_INCREMENTAL = FDLayoutConstants.DEFAULT_INCREMENTAL = LayoutConstants.DEFAULT_INCREMENTAL = !options.randomize;
|
|
CoSEConstants.ANIMATE = FDLayoutConstants.ANIMATE = LayoutConstants.ANIMATE = options.animate;
|
|
CoSEConstants.TILE = options.tile;
|
|
CoSEConstants.TILING_PADDING_VERTICAL = typeof options.tilingPaddingVertical === 'function' ? options.tilingPaddingVertical.call() : options.tilingPaddingVertical;
|
|
CoSEConstants.TILING_PADDING_HORIZONTAL = typeof options.tilingPaddingHorizontal === 'function' ? options.tilingPaddingHorizontal.call() : options.tilingPaddingHorizontal;
|
|
};
|
|
|
|
_CoSELayout.prototype.run = function () {
|
|
var ready;
|
|
var frameId;
|
|
var options = this.options;
|
|
var idToLNode = this.idToLNode = {};
|
|
var layout = this.layout = new CoSELayout();
|
|
var self = this;
|
|
|
|
self.stopped = false;
|
|
|
|
this.cy = this.options.cy;
|
|
|
|
this.cy.trigger({ type: 'layoutstart', layout: this });
|
|
|
|
var gm = layout.newGraphManager();
|
|
this.gm = gm;
|
|
|
|
var nodes = this.options.eles.nodes();
|
|
var edges = this.options.eles.edges();
|
|
|
|
this.root = gm.addRoot();
|
|
this.processChildrenList(this.root, this.getTopMostNodes(nodes), layout);
|
|
|
|
for (var i = 0; i < edges.length; i++) {
|
|
var edge = edges[i];
|
|
var sourceNode = this.idToLNode[edge.data("source")];
|
|
var targetNode = this.idToLNode[edge.data("target")];
|
|
if (sourceNode !== targetNode && sourceNode.getEdgesBetween(targetNode).length == 0) {
|
|
var e1 = gm.add(layout.newEdge(), sourceNode, targetNode);
|
|
e1.id = edge.id();
|
|
}
|
|
}
|
|
|
|
var getPositions = function getPositions(ele, i) {
|
|
if (typeof ele === "number") {
|
|
ele = i;
|
|
}
|
|
var theId = ele.data('id');
|
|
var lNode = self.idToLNode[theId];
|
|
|
|
return {
|
|
x: lNode.getRect().getCenterX(),
|
|
y: lNode.getRect().getCenterY()
|
|
};
|
|
};
|
|
|
|
/*
|
|
* Reposition nodes in iterations animatedly
|
|
*/
|
|
var iterateAnimated = function iterateAnimated() {
|
|
// Thigs to perform after nodes are repositioned on screen
|
|
var afterReposition = function afterReposition() {
|
|
if (options.fit) {
|
|
options.cy.fit(options.eles, options.padding);
|
|
}
|
|
|
|
if (!ready) {
|
|
ready = true;
|
|
self.cy.one('layoutready', options.ready);
|
|
self.cy.trigger({ type: 'layoutready', layout: self });
|
|
}
|
|
};
|
|
|
|
var ticksPerFrame = self.options.refresh;
|
|
var isDone;
|
|
|
|
for (var i = 0; i < ticksPerFrame && !isDone; i++) {
|
|
isDone = self.stopped || self.layout.tick();
|
|
}
|
|
|
|
// If layout is done
|
|
if (isDone) {
|
|
// If the layout is not a sublayout and it is successful perform post layout.
|
|
if (layout.checkLayoutSuccess() && !layout.isSubLayout) {
|
|
layout.doPostLayout();
|
|
}
|
|
|
|
// If layout has a tilingPostLayout function property call it.
|
|
if (layout.tilingPostLayout) {
|
|
layout.tilingPostLayout();
|
|
}
|
|
|
|
layout.isLayoutFinished = true;
|
|
|
|
self.options.eles.nodes().positions(getPositions);
|
|
|
|
afterReposition();
|
|
|
|
// trigger layoutstop when the layout stops (e.g. finishes)
|
|
self.cy.one('layoutstop', self.options.stop);
|
|
self.cy.trigger({ type: 'layoutstop', layout: self });
|
|
|
|
if (frameId) {
|
|
cancelAnimationFrame(frameId);
|
|
}
|
|
|
|
ready = false;
|
|
return;
|
|
}
|
|
|
|
var animationData = self.layout.getPositionsData(); // Get positions of layout nodes note that all nodes may not be layout nodes because of tiling
|
|
|
|
// Position nodes, for the nodes whose id does not included in data (because they are removed from their parents and included in dummy compounds)
|
|
// use position of their ancestors or dummy ancestors
|
|
options.eles.nodes().positions(function (ele, i) {
|
|
if (typeof ele === "number") {
|
|
ele = i;
|
|
}
|
|
// If ele is a compound node, then its position will be defined by its children
|
|
if (!ele.isParent()) {
|
|
var theId = ele.id();
|
|
var pNode = animationData[theId];
|
|
var temp = ele;
|
|
// If pNode is undefined search until finding position data of its first ancestor (It may be dummy as well)
|
|
while (pNode == null) {
|
|
pNode = animationData[temp.data('parent')] || animationData['DummyCompound_' + temp.data('parent')];
|
|
animationData[theId] = pNode;
|
|
temp = temp.parent()[0];
|
|
if (temp == undefined) {
|
|
break;
|
|
}
|
|
}
|
|
if (pNode != null) {
|
|
return {
|
|
x: pNode.x,
|
|
y: pNode.y
|
|
};
|
|
} else {
|
|
return {
|
|
x: ele.position('x'),
|
|
y: ele.position('y')
|
|
};
|
|
}
|
|
}
|
|
});
|
|
|
|
afterReposition();
|
|
|
|
frameId = requestAnimationFrame(iterateAnimated);
|
|
};
|
|
|
|
/*
|
|
* Listen 'layoutstarted' event and start animated iteration if animate option is 'during'
|
|
*/
|
|
layout.addListener('layoutstarted', function () {
|
|
if (self.options.animate === 'during') {
|
|
frameId = requestAnimationFrame(iterateAnimated);
|
|
}
|
|
});
|
|
|
|
layout.runLayout(); // Run cose layout
|
|
|
|
/*
|
|
* If animate option is not 'during' ('end' or false) perform these here (If it is 'during' similar things are already performed)
|
|
*/
|
|
if (this.options.animate !== "during") {
|
|
self.options.eles.nodes().not(":parent").layoutPositions(self, self.options, getPositions); // Use layout positions to reposition the nodes it considers the options parameter
|
|
ready = false;
|
|
}
|
|
|
|
return this; // chaining
|
|
};
|
|
|
|
//Get the top most ones of a list of nodes
|
|
_CoSELayout.prototype.getTopMostNodes = function (nodes) {
|
|
var nodesMap = {};
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
nodesMap[nodes[i].id()] = true;
|
|
}
|
|
var roots = nodes.filter(function (ele, i) {
|
|
if (typeof ele === "number") {
|
|
ele = i;
|
|
}
|
|
var parent = ele.parent()[0];
|
|
while (parent != null) {
|
|
if (nodesMap[parent.id()]) {
|
|
return false;
|
|
}
|
|
parent = parent.parent()[0];
|
|
}
|
|
return true;
|
|
});
|
|
|
|
return roots;
|
|
};
|
|
|
|
_CoSELayout.prototype.processChildrenList = function (parent, children, layout) {
|
|
var size = children.length;
|
|
for (var i = 0; i < size; i++) {
|
|
var theChild = children[i];
|
|
var children_of_children = theChild.children();
|
|
var theNode;
|
|
|
|
var dimensions = theChild.layoutDimensions({
|
|
nodeDimensionsIncludeLabels: this.options.nodeDimensionsIncludeLabels
|
|
});
|
|
|
|
if (theChild.outerWidth() != null && theChild.outerHeight() != null) {
|
|
theNode = parent.add(new CoSENode(layout.graphManager, new PointD(theChild.position('x') - dimensions.w / 2, theChild.position('y') - dimensions.h / 2), new DimensionD(parseFloat(dimensions.w), parseFloat(dimensions.h))));
|
|
} else {
|
|
theNode = parent.add(new CoSENode(this.graphManager));
|
|
}
|
|
// Attach id to the layout node
|
|
theNode.id = theChild.data("id");
|
|
// Attach the paddings of cy node to layout node
|
|
theNode.paddingLeft = parseInt(theChild.css('padding'));
|
|
theNode.paddingTop = parseInt(theChild.css('padding'));
|
|
theNode.paddingRight = parseInt(theChild.css('padding'));
|
|
theNode.paddingBottom = parseInt(theChild.css('padding'));
|
|
|
|
//Attach the label properties to compound if labels will be included in node dimensions
|
|
if (this.options.nodeDimensionsIncludeLabels) {
|
|
if (theChild.isParent()) {
|
|
var labelWidth = theChild.boundingBox({ includeLabels: true, includeNodes: false }).w;
|
|
var labelHeight = theChild.boundingBox({ includeLabels: true, includeNodes: false }).h;
|
|
var labelPos = theChild.css("text-halign");
|
|
theNode.labelWidth = labelWidth;
|
|
theNode.labelHeight = labelHeight;
|
|
theNode.labelPos = labelPos;
|
|
}
|
|
}
|
|
|
|
// Map the layout node
|
|
this.idToLNode[theChild.data("id")] = theNode;
|
|
|
|
if (isNaN(theNode.rect.x)) {
|
|
theNode.rect.x = 0;
|
|
}
|
|
|
|
if (isNaN(theNode.rect.y)) {
|
|
theNode.rect.y = 0;
|
|
}
|
|
|
|
if (children_of_children != null && children_of_children.length > 0) {
|
|
var theNewGraph;
|
|
theNewGraph = layout.getGraphManager().add(layout.newGraph(), theNode);
|
|
this.processChildrenList(theNewGraph, children_of_children, layout);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief : called on continuous layouts to stop them before they finish
|
|
*/
|
|
_CoSELayout.prototype.stop = function () {
|
|
this.stopped = true;
|
|
|
|
return this; // chaining
|
|
};
|
|
|
|
var register = function register(cytoscape) {
|
|
// var Layout = getLayout( cytoscape );
|
|
|
|
cytoscape('layout', 'cose-bilkent', _CoSELayout);
|
|
};
|
|
|
|
// auto reg for globals
|
|
if (typeof cytoscape !== 'undefined') {
|
|
register(cytoscape);
|
|
}
|
|
|
|
module.exports = register;
|
|
|
|
/***/ })
|
|
/******/ ]);
|
|
}); |