diff --git a/.gitignore b/.gitignore
index fe58676577..64a6d7fa38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ build/**
documentation/slimerjs-*/**
documentation/api/**
documentation/download/*.zip
+benchmark/suite/cytoscape.js
diff --git a/benchmark/old-version.json b/benchmark/old-version.json
new file mode 100644
index 0000000000..65d3405b08
--- /dev/null
+++ b/benchmark/old-version.json
@@ -0,0 +1 @@
+"2.7.15"
diff --git a/benchmark/suite/cytoscape.js b/benchmark/suite/cytoscape.js
deleted file mode 100644
index 71b6aecc0f..0000000000
--- a/benchmark/suite/cytoscape.js
+++ /dev/null
@@ -1,24517 +0,0 @@
-/*!
- * This file is part of Cytoscape.js 2.5.3.
- *
- * 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(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.cytoscape = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
- var minPos = findMin(openSet, fScore);
- var cMin = cy.getElementById( openSet[minPos] );
- steps++;
-
- // If we've found our goal, then we are done
- if (cMin.id() == target.id()) {
- var rPath = reconstructPath(source.id(), target.id(), cameFrom, []);
- rPath.reverse();
- return {
- found : true,
- distance : gScore[cMin.id()],
- path : eles.spawn(rPath),
- steps : steps
- };
- }
-
- // Add cMin to processed nodes
- closedSet.push(cMin.id());
- // Remove cMin from boundary nodes
- openSet.splice(minPos, 1);
-
- // Update scores for neighbors of cMin
- // Take into account if graph is directed or not
- var vwEdges = cMin.connectedEdges();
- if( directed ){ vwEdges = vwEdges.stdFilter(function(ele){ return ele.data('source') === cMin.id(); }); }
- vwEdges = vwEdges.intersect(edges);
-
- for (var i = 0; i < vwEdges.length; i++) {
- var e = vwEdges[i];
- var w = e.connectedNodes().stdFilter(function(n){ return n.id() !== cMin.id(); }).intersect(nodes);
-
- // if node is in closedSet, ignore it
- if (closedSet.indexOf(w.id()) != -1) {
- continue;
- }
-
- // New tentative score for node w
- var tempScore = gScore[cMin.id()] + weightFn.apply(e, [e]);
-
- // Update gScore for node w if:
- // w not present in openSet
- // OR
- // tentative gScore is less than previous value
-
- // w not in openSet
- if (openSet.indexOf(w.id()) == -1) {
- gScore[w.id()] = tempScore;
- fScore[w.id()] = tempScore + heuristic(w);
- openSet.push(w.id()); // Add node to openSet
- cameFrom[w.id()] = cMin.id();
- cameFromEdge[w.id()] = e.id();
- continue;
- }
- // w already in openSet, but with greater gScore
- if (tempScore < gScore[w.id()]) {
- gScore[w.id()] = tempScore;
- fScore[w.id()] = tempScore + heuristic(w);
- cameFrom[w.id()] = cMin.id();
- }
-
- } // End of neighbors update
-
- } // End of main loop
-
- // If we've reached here, then we've not reached our goal
- return {
- found : false,
- distance : undefined,
- path : undefined,
- steps : steps
- };
- }
-
-}); // elesfn
-
-
-module.exports = elesfn;
-
-},{"../../is":77}],3:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../is');
-var util = _dereq_('../../util');
-
-var elesfn = ({
-
- // Implemented from pseudocode from wikipedia
- bellmanFord: function(options) {
- var eles = this;
-
- options = options || {};
-
- // Weight function - optional
- if (options.weight != null && is.fn(options.weight)) {
- var weightFn = options.weight;
- } else {
- // If not specified, assume each edge has equal weight (1)
- var weightFn = function(e) {return 1;};
- }
-
- // directed - optional
- if (options.directed != null) {
- var directed = options.directed;
- } else {
- var directed = false;
- }
-
- // root - mandatory!
- if (options.root != null) {
- if (is.string(options.root)) {
- // use it as a selector, e.g. "#rootID
- var source = this.filter(options.root)[0];
- } else {
- var source = options.root[0];
- }
- } else {
- return undefined;
- }
-
- var cy = this._private.cy;
- var edges = this.edges().stdFilter(function(e){ return !e.isLoop(); });
- var nodes = this.nodes();
- var numNodes = nodes.length;
-
- // mapping: node id -> position in nodes array
- var id2position = {};
- for (var i = 0; i < numNodes; i++) {
- id2position[nodes[i].id()] = i;
- }
-
- // Initializations
- var cost = [];
- var predecessor = [];
- var predEdge = [];
-
- for (var i = 0; i < numNodes; i++) {
- if (nodes[i].id() === source.id()) {
- cost[i] = 0;
- } else {
- cost[i] = Infinity;
- }
- predecessor[i] = undefined;
- }
-
- // Edges relaxation
- var flag = false;
- for (var i = 1; i < numNodes; i++) {
- flag = false;
- for (var e = 0; e < edges.length; e++) {
- var sourceIndex = id2position[edges[e].source().id()];
- var targetIndex = id2position[edges[e].target().id()];
- var weight = weightFn.apply(edges[e], [edges[e]]);
-
- var temp = cost[sourceIndex] + weight;
- if (temp < cost[targetIndex]) {
- cost[targetIndex] = temp;
- predecessor[targetIndex] = sourceIndex;
- predEdge[targetIndex] = edges[e];
- flag = true;
- }
-
- // If undirected graph, we need to take into account the 'reverse' edge
- if (!directed) {
- var temp = cost[targetIndex] + weight;
- if (temp < cost[sourceIndex]) {
- cost[sourceIndex] = temp;
- predecessor[sourceIndex] = targetIndex;
- predEdge[sourceIndex] = edges[e];
- flag = true;
- }
- }
- }
-
- if (!flag) {
- break;
- }
- }
-
- if (flag) {
- // Check for negative weight cycles
- for (var e = 0; e < edges.length; e++) {
- var sourceIndex = id2position[edges[e].source().id()];
- var targetIndex = id2position[edges[e].target().id()];
- var weight = weightFn.apply(edges[e], [edges[e]]);
-
- if (cost[sourceIndex] + weight < cost[targetIndex]) {
- util.error("Graph contains a negative weight cycle for Bellman-Ford");
- return { pathTo: undefined,
- distanceTo: undefined,
- hasNegativeWeightCycle: true};
- }
- }
- }
-
- // Build result object
- var position2id = [];
- for (var i = 0; i < numNodes; i++) {
- position2id.push(nodes[i].id());
- }
-
-
- var res = {
- distanceTo : function(to) {
- if (is.string(to)) {
- // to is a selector string
- var toId = (cy.filter(to)[0]).id();
- } else {
- // to is a node
- var toId = to.id();
- }
-
- return cost[id2position[toId]];
- },
-
- pathTo : function(to) {
-
- var reconstructPathAux = function(predecessor, fromPos, toPos, position2id, acumPath, predEdge) {
- for(;;){
- // Add toId to path
- acumPath.push( cy.getElementById(position2id[toPos]) );
- acumPath.push( predEdge[toPos] );
-
- if (fromPos === toPos) {
- // reached starting node
- return acumPath;
- }
-
- // If no path exists, discart acumulated path and return undefined
- var predPos = predecessor[toPos];
- if (typeof predPos === "undefined") {
- return undefined;
- }
-
- toPos = predPos;
- }
-
- };
-
- if (is.string(to)) {
- // to is a selector string
- var toId = (cy.filter(to)[0]).id();
- } else {
- // to is a node
- var toId = to.id();
- }
- var path = [];
-
- // This returns a reversed path
- var res = reconstructPathAux(predecessor,
- id2position[source.id()],
- id2position[toId],
- position2id,
- path,
- predEdge);
-
- // Get it in the correct order and return it
- if (res != null) {
- res.reverse();
- }
-
- return eles.spawn(res);
- },
-
- hasNegativeWeightCycle: false
- };
-
- return res;
-
- } // bellmanFord
-
-}); // elesfn
-
-module.exports = elesfn;
-
-},{"../../is":77,"../../util":94}],4:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../is');
-
-var elesfn = ({
-
- // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
- betweennessCentrality: function (options) {
- options = options || {};
-
- // Weight - optional
- if (options.weight != null && is.fn(options.weight)) {
- var weightFn = options.weight;
- var weighted = true;
- } else {
- var weighted = false;
- }
-
- // Directed - default false
- if (options.directed != null && is.bool(options.directed)) {
- var directed = options.directed;
- } else {
- var directed = false;
- }
-
- var priorityInsert = function (queue, ele) {
- queue.unshift(ele);
- for (var i = 0; d[queue[i]] < d[queue[i + 1]] && i < queue.length - 1; i++) {
- var tmp = queue[i];
- queue[i] = queue[i + 1];
- queue[i + 1] = tmp;
- }
- };
-
- var cy = this._private.cy;
-
- // starting
- var V = this.nodes();
- var A = {};
- var C = {};
-
- // A contains the neighborhoods of every node
- for (var i = 0; i < V.length; i++) {
- if (directed) {
- A[V[i].id()] = V[i].outgoers("node"); // get outgoers of every node
- } else {
- A[V[i].id()] = V[i].openNeighborhood("node"); // get neighbors of every node
- }
- }
-
- // C contains the betweenness values
- for (var i = 0; i < V.length; i++) {
- C[V[i].id()] = 0;
- }
-
- for (var s = 0; s < V.length; s++) {
- var S = []; // stack
- var P = {};
- var g = {};
- var d = {};
- var Q = []; // queue
-
- // init dictionaries
- for (var i = 0; i < V.length; i++) {
- P[V[i].id()] = [];
- g[V[i].id()] = 0;
- d[V[i].id()] = Number.POSITIVE_INFINITY;
- }
-
- g[V[s].id()] = 1; // sigma
- d[V[s].id()] = 0; // distance to s
-
- Q.unshift(V[s].id());
-
- while (Q.length > 0) {
- var v = Q.pop();
- S.push(v);
- if (weighted) {
- A[v].forEach(function (w) {
- if (cy.$('#' + v).edgesTo(w).length > 0) {
- var edge = cy.$('#' + v).edgesTo(w)[0];
- } else {
- var edge = w.edgesTo('#' + v)[0];
- }
-
- var edgeWeight = weightFn.apply(edge, [edge]);
-
- if (d[w.id()] > d[v] + edgeWeight) {
- d[w.id()] = d[v] + edgeWeight;
- if (Q.indexOf(w.id()) < 0) { //if w is not in Q
- priorityInsert(Q, w.id());
- } else { // update position if w is in Q
- Q.splice(Q.indexOf(w.id()), 1);
- priorityInsert(Q, w.id());
- }
- g[w.id()] = 0;
- P[w.id()] = [];
- }
- if (d[w.id()] == d[v] + edgeWeight) {
- g[w.id()] = g[w.id()] + g[v];
- P[w.id()].push(v);
- }
- });
- } else {
- A[v].forEach(function (w) {
- if (d[w.id()] == Number.POSITIVE_INFINITY) {
- Q.unshift(w.id());
- d[w.id()] = d[v] + 1;
- }
- if (d[w.id()] == d[v] + 1) {
- g[w.id()] = g[w.id()] + g[v];
- P[w.id()].push(v);
- }
- });
- }
- }
-
- var e = {};
- for (var i = 0; i < V.length; i++) {
- e[V[i].id()] = 0;
- }
-
- while (S.length > 0) {
- var w = S.pop();
- P[w].forEach(function (v) {
- e[v] = e[v] + (g[v] / g[w]) * (1 + e[w]);
- if (w != V[s].id())
- C[w] = C[w] + e[w];
- });
- }
- }
-
- var max = 0;
- for (var key in C) {
- if (max < C[key])
- max = C[key];
- }
-
- var ret = {
- betweenness: function (node) {
- if (is.string(node)) {
- var node = (cy.filter(node)[0]).id();
- } else {
- var node = node.id();
- }
-
- return C[node];
- },
-
- betweennessNormalized: function (node) {
- if (is.string(node)) {
- var node = (cy.filter(node)[0]).id();
- } else {
- var node = node.id();
- }
-
- return C[node] / max;
- }
- };
-
- // alias
- ret.betweennessNormalised = ret.betweennessNormalized;
-
- return ret;
- } // betweennessCentrality
-
-}); // elesfn
-
-// nice, short mathemathical alias
-elesfn.bc = elesfn.betweennessCentrality;
-
-module.exports = elesfn;
-
-},{"../../is":77}],5:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../is');
-var Heap = _dereq_('../../heap');
-
-var defineSearch = function( params ){
- params = {
- bfs: params.bfs || !params.dfs,
- dfs: params.dfs || !params.bfs
- };
-
- // from pseudocode on wikipedia
- return function searchFn( roots, fn, directed ){
- var options;
- var std;
- var thisArg;
- if( is.plainObject(roots) && !is.elementOrCollection(roots) ){
- options = roots;
- roots = options.roots || options.root;
- fn = options.visit;
- directed = options.directed;
- std = options.std;
- thisArg = options.thisArg;
- }
-
- directed = arguments.length === 2 && !is.fn(fn) ? fn : directed;
- fn = is.fn(fn) ? fn : function(){};
-
- var cy = this._private.cy;
- var v = roots = is.string(roots) ? this.filter(roots) : roots;
- var Q = [];
- var connectedNodes = [];
- var connectedBy = {};
- var id2depth = {};
- var V = {};
- var j = 0;
- var found;
- var nodes = this.nodes();
- var edges = this.edges();
-
- // enqueue v
- for( var i = 0; i < v.length; i++ ){
- if( v[i].isNode() ){
- Q.unshift( v[i] );
-
- if( params.bfs ){
- V[ v[i].id() ] = true;
-
- connectedNodes.push( v[i] );
- }
-
- id2depth[ v[i].id() ] = 0;
- }
- }
-
- while( Q.length !== 0 ){
- var v = params.bfs ? Q.shift() : Q.pop();
-
- if( params.dfs ){
- if( V[ v.id() ] ){ continue; }
-
- V[ v.id() ] = true;
-
- connectedNodes.push( v );
- }
-
- var depth = id2depth[ v.id() ];
- var prevEdge = connectedBy[ v.id() ];
- var prevNode = prevEdge == null ? undefined : prevEdge.connectedNodes().not( v )[0];
- var ret;
-
- if( std ){
- ret = fn.call(thisArg, v, prevEdge, prevNode, j++, depth);
- } else {
- ret = fn.call(v, j++, depth, v, prevEdge, prevNode);
- }
-
- if( ret === true ){
- found = v;
- break;
- }
-
- if( ret === false ){
- break;
- }
-
- var vwEdges = v.connectedEdges(directed ? function(){ return this.data('source') === v.id(); } : undefined).intersect( edges );
- for( var i = 0; i < vwEdges.length; i++ ){
- var e = vwEdges[i];
- var w = e.connectedNodes(function(){ return this.id() !== v.id(); }).intersect( nodes );
-
- if( w.length !== 0 && !V[ w.id() ] ){
- w = w[0];
-
- Q.push( w );
-
- if( params.bfs ){
- V[ w.id() ] = true;
-
- connectedNodes.push( w );
- }
-
- connectedBy[ w.id() ] = e;
-
- id2depth[ w.id() ] = id2depth[ v.id() ] + 1;
- }
- }
-
- }
-
- var connectedEles = [];
-
- for( var i = 0; i < connectedNodes.length; i++ ){
- var node = connectedNodes[i];
- var edge = connectedBy[ node.id() ];
-
- if( edge ){
- connectedEles.push( edge );
- }
-
- connectedEles.push( node );
- }
-
- return {
- path: cy.collection( connectedEles, { unique: true } ),
- found: cy.collection( found )
- };
- };
-};
-
-// search, spanning trees, etc
-var elesfn = ({
-
- breadthFirstSearch: defineSearch({ bfs: true }),
- depthFirstSearch: defineSearch({ dfs: true }),
-
- // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
- // implemented from pseudocode from wikipedia
- kruskal: function( weightFn ){
- var cy = this.cy();
-
- weightFn = is.fn(weightFn) ? weightFn : function(){ return 1; }; // if not specified, assume each edge has equal weight (1)
-
- function findSet(ele){
- for( var i = 0; i < forest.length; i++ ){
- var eles = forest[i];
-
- if( eles.anySame(ele) ){
- return {
- eles: eles,
- index: i
- };
- }
- }
- }
-
- var A = cy.collection(cy, []);
- var forest = [];
- var nodes = this.nodes();
-
- for( var i = 0; i < nodes.length; i++ ){
- forest.push( nodes[i].collection() );
- }
-
- var edges = this.edges();
- var S = edges.toArray().sort(function(a, b){
- var weightA = weightFn.call(a, a);
- var weightB = weightFn.call(b, b);
-
- return weightA - weightB;
- });
-
- for(var i = 0; i < S.length; i++){
- var edge = S[i];
- var u = edge.source()[0];
- var v = edge.target()[0];
- var setU = findSet(u);
- var setV = findSet(v);
-
- if( setU.index !== setV.index ){
- A = A.add( edge );
-
- // combine forests for u and v
- forest[ setU.index ] = setU.eles.add( setV.eles );
- forest.splice( setV.index, 1 );
- }
- }
-
- return nodes.add( A );
-
- },
-
- dijkstra: function( root, weightFn, directed ){
- var options;
- if( is.plainObject(root) && !is.elementOrCollection(root) ){
- options = root;
- root = options.root;
- weightFn = options.weight;
- directed = options.directed;
- }
-
- var cy = this._private.cy;
- weightFn = is.fn(weightFn) ? weightFn : function(){ return 1; }; // if not specified, assume each edge has equal weight (1)
-
- var source = is.string(root) ? this.filter(root)[0] : root[0];
- var dist = {};
- var prev = {};
- var knownDist = {};
-
- var edges = this.edges().filter(function(){ return !this.isLoop(); });
- var nodes = this.nodes();
-
- var getDist = function(node){
- return dist[ node.id() ];
- };
-
- var setDist = function(node, d){
- dist[ node.id() ] = d;
-
- Q.updateItem( node );
- };
-
- var Q = new Heap(function( a, b ){
- return getDist(a) - getDist(b);
- });
-
- for( var i = 0; i < nodes.length; i++ ){
- var node = nodes[i];
-
- dist[ node.id() ] = node.same( source ) ? 0 : Infinity;
- Q.push( node );
- }
-
- var distBetween = function(u, v){
- var uvs = ( directed ? u.edgesTo(v) : u.edgesWith(v) ).intersect(edges);
- var smallestDistance = Infinity;
- var smallestEdge;
-
- for( var i = 0; i < uvs.length; i++ ){
- var edge = uvs[i];
- var weight = weightFn.apply( edge, [edge] );
-
- if( weight < smallestDistance || !smallestEdge ){
- smallestDistance = weight;
- smallestEdge = edge;
- }
- }
-
- return {
- edge: smallestEdge,
- dist: smallestDistance
- };
- };
-
- while( Q.size() > 0 ){
- var u = Q.pop();
- var smalletsDist = getDist(u);
- var uid = u.id();
-
- knownDist[uid] = smalletsDist;
-
- if( smalletsDist === Math.Infinite ){
- break;
- }
-
- var neighbors = u.neighborhood().intersect(nodes);
- for( var i = 0; i < neighbors.length; i++ ){
- var v = neighbors[i];
- var vid = v.id();
- var vDist = distBetween(u, v);
-
- var alt = smalletsDist + vDist.dist;
-
- if( alt < getDist(v) ){
- setDist(v, alt);
-
- prev[ vid ] = {
- node: u,
- edge: vDist.edge
- };
- }
- } // for
- } // while
-
- return {
- distanceTo: function(node){
- var target = is.string(node) ? nodes.filter(node)[0] : node[0];
-
- return knownDist[ target.id() ];
- },
-
- pathTo: function(node){
- var target = is.string(node) ? nodes.filter(node)[0] : node[0];
- var S = [];
- var u = target;
-
- if( target.length > 0 ){
- S.unshift( target );
-
- while( prev[ u.id() ] ){
- var p = prev[ u.id() ];
-
- S.unshift( p.edge );
- S.unshift( p.node );
-
- u = p.node;
- }
- }
-
- return cy.collection( S );
- }
- };
- }
-});
-
-// nice, short mathemathical alias
-elesfn.bfs = elesfn.breadthFirstSearch;
-elesfn.dfs = elesfn.depthFirstSearch;
-
-module.exports = elesfn;
-
-},{"../../heap":75,"../../is":77}],6:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../is');
-
-var elesfn = ({
-
- closenessCentralityNormalized: function (options) {
- options = options || {};
-
- var cy = this.cy();
-
- var harmonic = options.harmonic;
- if( harmonic === undefined ){
- harmonic = true;
- }
-
- var closenesses = {};
- var maxCloseness = 0;
- var nodes = this.nodes();
- var fw = this.floydWarshall({ weight: options.weight, directed: options.directed });
-
- // Compute closeness for every node and find the maximum closeness
- for(var i = 0; i < nodes.length; i++){
- var currCloseness = 0;
- for (var j = 0; j < nodes.length; j++) {
- if (i != j) {
- var d = fw.distance(nodes[i], nodes[j]);
-
- if( harmonic ){
- currCloseness += 1 / d;
- } else {
- currCloseness += d;
- }
- }
- }
-
- if( !harmonic ){
- currCloseness = 1 / currCloseness;
- }
-
- if (maxCloseness < currCloseness){
- maxCloseness = currCloseness;
- }
-
- closenesses[nodes[i].id()] = currCloseness;
- }
-
- return {
- closeness: function (node) {
- if (is.string(node)) {
- // from is a selector string
- var node = (cy.filter(node)[0]).id();
- } else {
- // from is a node
- var node = node.id();
- }
-
- return closenesses[node] / maxCloseness;
- }
- };
- },
-
- // Implemented from pseudocode from wikipedia
- closenessCentrality: function (options) {
- options = options || {};
-
- // root - mandatory!
- if (options.root != null) {
- if (is.string(options.root)) {
- // use it as a selector, e.g. "#rootID
- var root = this.filter(options.root)[0];
- } else {
- var root = options.root[0];
- }
- } else {
- return undefined;
- }
-
- // weight - optional
- if (options.weight != null && is.fn(options.weight)) {
- var weight = options.weight;
- } else {
- var weight = function(){return 1;};
- }
-
- // directed - optional
- if (options.directed != null && is.bool(options.directed)) {
- var directed = options.directed;
- } else {
- var directed = false;
- }
-
- var harmonic = options.harmonic;
- if( harmonic === undefined ){
- harmonic = true;
- }
-
- // we need distance from this node to every other node
- var dijkstra = this.dijkstra({
- root: root,
- weight: weight,
- directed: directed
- });
- var totalDistance = 0;
-
- var nodes = this.nodes();
- for (var i = 0; i < nodes.length; i++){
- if (nodes[i].id() != root.id()){
- var d = dijkstra.distanceTo(nodes[i]);
-
- if( harmonic ){
- totalDistance += 1 / d;
- } else {
- totalDistance += d;
- }
- }
- }
-
- return harmonic ? totalDistance : 1 / totalDistance;
- } // closenessCentrality
-
-}); // elesfn
-
-// nice, short mathemathical alias
-elesfn.cc = elesfn.closenessCentrality;
-elesfn.ccn = elesfn.closenessCentralityNormalised = elesfn.closenessCentralityNormalized;
-
-module.exports = elesfn;
-
-},{"../../is":77}],7:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../is');
-var util = _dereq_('../../util');
-
-var elesfn = ({
-
- degreeCentralityNormalized: function (options) {
- options = options || {};
-
- var cy = this.cy();
-
- // directed - optional
- if (options.directed != null) {
- var directed = options.directed;
- } else {
- var directed = false;
- }
-
- var nodes = this.nodes();
- var numNodes = nodes.length;
-
- if (!directed) {
- var degrees = {};
- var maxDegree = 0;
-
- for (var i = 0; i < numNodes; i++) {
- var node = nodes[i];
- // add current node to the current options object and call degreeCentrality
- var currDegree = this.degreeCentrality(util.extend({}, options, {root: node}));
- if (maxDegree < currDegree.degree)
- maxDegree = currDegree.degree;
-
- degrees[node.id()] = currDegree.degree;
- }
-
- return {
- degree: function (node) {
- if (is.string(node)) {
- // from is a selector string
- var node = (cy.filter(node)[0]).id();
- } else {
- // from is a node
- var node = node.id();
- }
-
- return degrees[node] / maxDegree;
- }
- };
- } else {
- var indegrees = {};
- var outdegrees = {};
- var maxIndegree = 0;
- var maxOutdegree = 0;
-
- for (var i = 0; i < numNodes; i++) {
- var node = nodes[i];
- // add current node to the current options object and call degreeCentrality
- var currDegree = this.degreeCentrality(util.extend({}, options, {root: node}));
-
- if (maxIndegree < currDegree.indegree)
- maxIndegree = currDegree.indegree;
-
- if (maxOutdegree < currDegree.outdegree)
- maxOutdegree = currDegree.outdegree;
-
- indegrees[node.id()] = currDegree.indegree;
- outdegrees[node.id()] = currDegree.outdegree;
- }
-
- return {
- indegree: function (node) {
- if (is.string(node)) {
- // from is a selector string
- var node = (cy.filter(node)[0]).id();
- } else {
- // from is a node
- var node = node.id();
- }
-
- return indegrees[node] / maxIndegree;
- },
- outdegree: function (node) {
- if (is.string(node)) {
- // from is a selector string
- var node = (cy.filter(node)[0]).id();
- } else {
- // from is a node
- var node = node.id();
- }
-
- return outdegrees[node] / maxOutdegree;
- }
-
- };
- }
-
- }, // degreeCentralityNormalized
-
- // Implemented from the algorithm in Opsahl's paper
- // "Node centrality in weighted networks: Generalizing degree and shortest paths"
- // check the heading 2 "Degree"
- degreeCentrality: function (options) {
- options = options || {};
-
- var callingEles = this;
-
- // root - mandatory!
- if (options != null && options.root != null) {
- var root = is.string(options.root) ? this.filter(options.root)[0] : options.root[0];
- } else {
- return undefined;
- }
-
- // weight - optional
- if (options.weight != null && is.fn(options.weight)) {
- var weightFn = options.weight;
- } else {
- // If not specified, assume each edge has equal weight (1)
- var weightFn = function (e) {
- return 1;
- };
- }
-
- // directed - optional
- if (options.directed != null) {
- var directed = options.directed;
- } else {
- var directed = false;
- }
-
- // alpha - optional
- if (options.alpha != null && is.number(options.alpha)) {
- var alpha = options.alpha;
- } else {
- alpha = 0;
- }
-
-
- if (!directed) {
- var connEdges = root.connectedEdges().intersection( callingEles );
- var k = connEdges.length;
- var s = 0;
-
- // Now, sum edge weights
- for (var i = 0; i < connEdges.length; i++) {
- var edge = connEdges[i];
- s += weightFn.apply(edge, [edge]);
- }
-
- return {
- degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
- };
- } else {
- var incoming = root.connectedEdges('edge[target = "' + root.id() + '"]').intersection( callingEles );
- var outgoing = root.connectedEdges('edge[source = "' + root.id() + '"]').intersection( callingEles );
- var k_in = incoming.length;
- var k_out = outgoing.length;
- var s_in = 0;
- var s_out = 0;
-
- // Now, sum incoming edge weights
- for (var i = 0; i < incoming.length; i++) {
- var edge = incoming[i];
- s_in += weightFn.apply(edge, [edge]);
- }
-
- // Now, sum outgoing edge weights
- for (var i = 0; i < outgoing.length; i++) {
- var edge = outgoing[i];
- s_out += weightFn.apply(edge, [edge]);
- }
-
- return {
- indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
- outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
- };
- }
- } // degreeCentrality
-
-}); // elesfn
-
-// nice, short mathemathical alias
-elesfn.dc = elesfn.degreeCentrality;
-elesfn.dcn = elesfn.degreeCentralityNormalised = elesfn.degreeCentralityNormalized;
-
-module.exports = elesfn;
-
-},{"../../is":77,"../../util":94}],8:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../is');
-
-var elesfn = ({
-
- // Implemented from pseudocode from wikipedia
- floydWarshall: function(options) {
- options = options || {};
-
- var cy = this.cy();
-
- // Weight function - optional
- if (options.weight != null && is.fn(options.weight)) {
- var weightFn = options.weight;
- } else {
- // If not specified, assume each edge has equal weight (1)
- var weightFn = function(e) {return 1;};
- }
-
- // directed - optional
- if (options.directed != null) {
- var directed = options.directed;
- } else {
- var directed = false;
- }
-
- var edges = this.edges().stdFilter(function(e){ return !e.isLoop(); });
- var nodes = this.nodes();
- var numNodes = nodes.length;
-
- // mapping: node id -> position in nodes array
- var id2position = {};
- for (var i = 0; i < numNodes; i++) {
- id2position[nodes[i].id()] = i;
- }
-
- // Initialize distance matrix
- var dist = [];
- for (var i = 0; i < numNodes; i++) {
- var newRow = new Array(numNodes);
- for (var j = 0; j < numNodes; j++) {
- if (i == j) {
- newRow[j] = 0;
- } else {
- newRow[j] = Infinity;
- }
- }
- dist.push(newRow);
- }
-
- // Initialize matrix used for path reconstruction
- // Initialize distance matrix
- var next = [];
- var edgeNext = [];
-
- var initMatrix = function(next){
- for (var i = 0; i < numNodes; i++) {
- var newRow = new Array(numNodes);
- for (var j = 0; j < numNodes; j++) {
- newRow[j] = undefined;
- }
- next.push(newRow);
- }
- };
-
- initMatrix(next);
- initMatrix(edgeNext);
-
- // Process edges
- for (var i = 0; i < edges.length ; i++) {
- var sourceIndex = id2position[edges[i].source().id()];
- var targetIndex = id2position[edges[i].target().id()];
- var weight = weightFn.apply(edges[i], [edges[i]]);
-
- // Check if already process another edge between same 2 nodes
- if (dist[sourceIndex][targetIndex] > weight) {
- dist[sourceIndex][targetIndex] = weight;
- next[sourceIndex][targetIndex] = targetIndex;
- edgeNext[sourceIndex][targetIndex] = edges[i];
- }
- }
-
- // If undirected graph, process 'reversed' edges
- if (!directed) {
- for (var i = 0; i < edges.length ; i++) {
- var sourceIndex = id2position[edges[i].target().id()];
- var targetIndex = id2position[edges[i].source().id()];
- var weight = weightFn.apply(edges[i], [edges[i]]);
-
- // Check if already process another edge between same 2 nodes
- if (dist[sourceIndex][targetIndex] > weight) {
- dist[sourceIndex][targetIndex] = weight;
- next[sourceIndex][targetIndex] = targetIndex;
- edgeNext[sourceIndex][targetIndex] = edges[i];
- }
- }
- }
-
- // Main loop
- for (var k = 0; k < numNodes; k++) {
- for (var i = 0; i < numNodes; i++) {
- for (var j = 0; j < numNodes; j++) {
- if (dist[i][k] + dist[k][j] < dist[i][j]) {
- dist[i][j] = dist[i][k] + dist[k][j];
- next[i][j] = next[i][k];
- }
- }
- }
- }
-
- // Build result object
- var position2id = [];
- for (var i = 0; i < numNodes; i++) {
- position2id.push(nodes[i].id());
- }
-
- var res = {
- distance: function(from, to) {
- if (is.string(from)) {
- // from is a selector string
- var fromId = (cy.filter(from)[0]).id();
- } else {
- // from is a node
- var fromId = from.id();
- }
-
- if (is.string(to)) {
- // to is a selector string
- var toId = (cy.filter(to)[0]).id();
- } else {
- // to is a node
- var toId = to.id();
- }
-
- return dist[id2position[fromId]][id2position[toId]];
- },
-
- path: function(from, to) {
- var reconstructPathAux = function(from, to, next, position2id, edgeNext) {
- if (from === to) {
- return cy.getElementById( position2id[from] );
- }
- if (next[from][to] === undefined) {
- return undefined;
- }
-
- var path = [ cy.getElementById(position2id[from]) ];
- var prev = from;
- while (from !== to) {
- prev = from;
- from = next[from][to];
-
- var edge = edgeNext[prev][from];
- path.push( edge );
-
- path.push( cy.getElementById(position2id[from]) );
- }
- return path;
- };
-
- if (is.string(from)) {
- // from is a selector string
- var fromId = (cy.filter(from)[0]).id();
- } else {
- // from is a node
- var fromId = from.id();
- }
-
- if (is.string(to)) {
- // to is a selector string
- var toId = (cy.filter(to)[0]).id();
- } else {
- // to is a node
- var toId = to.id();
- }
-
- var pathArr = reconstructPathAux(id2position[fromId],
- id2position[toId],
- next,
- position2id,
- edgeNext);
-
- return cy.collection( pathArr );
- }
- };
-
- return res;
-
- } // floydWarshall
-
-}); // elesfn
-
-module.exports = elesfn;
-
-},{"../../is":77}],9:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-
-var elesfn = {};
-
-[
- _dereq_('./bfs-dfs'),
- _dereq_('./a-star'),
- _dereq_('./floyd-warshall'),
- _dereq_('./bellman-ford'),
- _dereq_('./kerger-stein'),
- _dereq_('./page-rank'),
- _dereq_('./degree-centrality'),
- _dereq_('./closeness-centrality'),
- _dereq_('./betweenness-centrality')
-].forEach(function( props ){
- util.extend( elesfn, props );
-});
-
-module.exports = elesfn;
-
-},{"../../util":94,"./a-star":2,"./bellman-ford":3,"./betweenness-centrality":4,"./bfs-dfs":5,"./closeness-centrality":6,"./degree-centrality":7,"./floyd-warshall":8,"./kerger-stein":10,"./page-rank":11}],10:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-
-var elesfn = ({
-
- // Computes the minimum cut of an undirected graph
- // Returns the correct answer with high probability
- kargerStein: function(options) {
- var eles = this;
-
- options = options || {};
-
- // Function which colapses 2 (meta) nodes into one
- // Updates the remaining edge lists
- // Receives as a paramater the edge which causes the collapse
- var colapse = function(edgeIndex, nodeMap, remainingEdges) {
- var edgeInfo = remainingEdges[edgeIndex];
- var sourceIn = edgeInfo[1];
- var targetIn = edgeInfo[2];
- var partition1 = nodeMap[sourceIn];
- var partition2 = nodeMap[targetIn];
-
- // Delete all edges between partition1 and partition2
- var newEdges = remainingEdges.filter(function(edge) {
- if (nodeMap[edge[1]] === partition1 && nodeMap[edge[2]] === partition2) {
- return false;
- }
- if (nodeMap[edge[1]] === partition2 && nodeMap[edge[2]] === partition1) {
- return false;
- }
- return true;
- });
-
- // All edges pointing to partition2 should now point to partition1
- for (var i = 0; i < newEdges.length; i++) {
- var edge = newEdges[i];
- if (edge[1] === partition2) { // Check source
- newEdges[i] = edge.slice(0);
- newEdges[i][1] = partition1;
- } else if (edge[2] === partition2) { // Check target
- newEdges[i] = edge.slice(0);
- newEdges[i][2] = partition1;
- }
- }
-
- // Move all nodes from partition2 to partition1
- for (var i = 0; i < nodeMap.length; i++) {
- if (nodeMap[i] === partition2) {
- nodeMap[i] = partition1;
- }
- }
-
- return newEdges;
- };
-
-
- // Contracts a graph until we reach a certain number of meta nodes
- var contractUntil = function(metaNodeMap,
- remainingEdges,
- size,
- sizeLimit) {
- // Stop condition
- if (size <= sizeLimit) {
- return remainingEdges;
- }
-
- // Choose an edge randomly
- var edgeIndex = Math.floor((Math.random() * remainingEdges.length));
-
- // Colapse graph based on edge
- var newEdges = colapse(edgeIndex, metaNodeMap, remainingEdges);
-
- return contractUntil(metaNodeMap,
- newEdges,
- size - 1,
- sizeLimit);
- };
-
- var cy = this._private.cy;
- var edges = this.edges().stdFilter(function(e){ return !e.isLoop(); });
- var nodes = this.nodes();
- var numNodes = nodes.length;
- var numEdges = edges.length;
- var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
- var stopSize = Math.floor(numNodes / Math.sqrt(2));
-
- if (numNodes < 2) {
- util.error("At least 2 nodes are required for Karger-Stein algorithm");
- return undefined;
- }
-
- // Create numerical identifiers for each node
- // mapping: node id -> position in nodes array
- // for reverse mapping, simply use nodes array
- var id2position = {};
- for (var i = 0; i < numNodes; i++) {
- id2position[nodes[i].id()] = i;
- }
-
- // Now store edge destination as indexes
- // Format for each edge (edge index, source node index, target node index)
- var edgeIndexes = [];
- for (var i = 0; i < numEdges; i++) {
- var e = edges[i];
- edgeIndexes.push([i, id2position[e.source().id()], id2position[e.target().id()]]);
- }
-
- // We will store the best cut found here
- var minCutSize = Infinity;
- var minCut;
-
- // Initial meta node partition
- var originalMetaNode = [];
- for (var i = 0; i < numNodes; i++) {
- originalMetaNode.push(i);
- }
-
- // Main loop
- for (var iter = 0; iter <= numIter; iter++) {
- // Create new meta node partition
- var metaNodeMap = originalMetaNode.slice(0);
-
- // Contract until stop point (stopSize nodes)
- var edgesState = contractUntil(metaNodeMap, edgeIndexes, numNodes, stopSize);
-
- // Create a copy of the colapsed nodes state
- var metaNodeMap2 = metaNodeMap.slice(0);
-
- // Run 2 iterations starting in the stop state
- var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
- var res2 = contractUntil(metaNodeMap2, edgesState, stopSize, 2);
-
- // Is any of the 2 results the best cut so far?
- if (res1.length <= res2.length && res1.length < minCutSize) {
- minCutSize = res1.length;
- minCut = [res1, metaNodeMap];
- } else if (res2.length <= res1.length && res2.length < minCutSize) {
- minCutSize = res2.length;
- minCut = [res2, metaNodeMap2];
- }
- } // end of main loop
-
-
- // Construct result
- var resEdges = (minCut[0]).map(function(e){ return edges[e[0]]; });
- var partition1 = [];
- var partition2 = [];
-
- // traverse metaNodeMap for best cut
- var witnessNodePartition = minCut[1][0];
- for (var i = 0; i < minCut[1].length; i++) {
- var partitionId = minCut[1][i];
- if (partitionId === witnessNodePartition) {
- partition1.push(nodes[i]);
- } else {
- partition2.push(nodes[i]);
- }
- }
-
- var ret = {
- cut: eles.spawn(cy, resEdges),
- partition1: eles.spawn(partition1),
- partition2: eles.spawn(partition2)
- };
-
- return ret;
- }
-}); // elesfn
-
-
-module.exports = elesfn;
-
-},{"../../util":94}],11:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../is');
-
-var elesfn = ({
-
- pageRank: function(options) {
- options = options || {};
-
- var normalizeVector = function(vector) {
- var length = vector.length;
-
- // First, get sum of all elements
- var total = 0;
- for (var i = 0; i < length; i++) {
- total += vector[i];
- }
-
- // Now, divide each by the sum of all elements
- for (var i = 0; i < length; i++) {
- vector[i] = vector[i] / total;
- }
- };
-
- // dampingFactor - optional
- if (options != null &&
- options.dampingFactor != null) {
- var dampingFactor = options.dampingFactor;
- } else {
- var dampingFactor = 0.8; // Default damping factor
- }
-
- // desired precision - optional
- if (options != null &&
- options.precision != null) {
- var epsilon = options.precision;
- } else {
- var epsilon = 0.000001; // Default precision
- }
-
- // Max number of iterations - optional
- if (options != null &&
- options.iterations != null) {
- var numIter = options.iterations;
- } else {
- var numIter = 200; // Default number of iterations
- }
-
- // Weight function - optional
- if (options != null &&
- options.weight != null &&
- is.fn(options.weight)) {
- var weightFn = options.weight;
- } else {
- // If not specified, assume each edge has equal weight (1)
- var weightFn = function(e) {return 1;};
- }
-
- var cy = this._private.cy;
- var edges = this.edges().stdFilter(function(e){ return !e.isLoop(); });
- var nodes = this.nodes();
- var numNodes = nodes.length;
- var numEdges = edges.length;
-
- // Create numerical identifiers for each node
- // mapping: node id -> position in nodes array
- // for reverse mapping, simply use nodes array
- var id2position = {};
- for (var i = 0; i < numNodes; i++) {
- id2position[nodes[i].id()] = i;
- }
-
- // Construct transposed adjacency matrix
- // First lets have a zeroed matrix of the right size
- // We'll also keep track of the sum of each column
- var matrix = [];
- var columnSum = [];
- var additionalProb = (1 - dampingFactor) / numNodes;
-
- // Create null matric
- for (var i = 0; i < numNodes; i++) {
- var newRow = [];
- for (var j = 0; j < numNodes; j++) {
- newRow.push(0.0);
- }
- matrix.push(newRow);
- columnSum.push(0.0);
- }
-
- // Now, process edges
- for (var i = 0; i < numEdges; i++) {
- var edge = edges[i];
- var s = id2position[edge.source().id()];
- var t = id2position[edge.target().id()];
- var w = weightFn.apply(edge, [edge]);
-
- // Update matrix
- matrix[t][s] += w;
-
- // Update column sum
- columnSum[s] += w;
- }
-
- // Add additional probability based on damping factor
- // Also, take into account columns that have sum = 0
- var p = 1.0 / numNodes + additionalProb; // Shorthand
- // Traverse matrix, column by column
- for (var j = 0; j < numNodes; j++) {
- if (columnSum[j] === 0) {
- // No 'links' out from node jth, assume equal probability for each possible node
- for (var i = 0; i < numNodes; i++) {
- matrix[i][j] = p;
- }
- } else {
- // Node jth has outgoing link, compute normalized probabilities
- for (var i = 0; i < numNodes; i++) {
- matrix[i][j] = matrix[i][j] / columnSum[j] + additionalProb;
- }
- }
- }
-
- // Compute dominant eigenvector using power method
- var eigenvector = [];
- var nullVector = [];
- var previous;
-
- // Start with a vector of all 1's
- // Also, initialize a null vector which will be used as shorthand
- for (var i = 0; i < numNodes; i++) {
- eigenvector.push(1.0);
- nullVector.push(0.0);
- }
-
- for (var iter = 0; iter < numIter; iter++) {
- // New array with all 0's
- var temp = nullVector.slice(0);
-
- // Multiply matrix with previous result
- for (var i = 0; i < numNodes; i++) {
- for (var j = 0; j < numNodes; j++) {
- temp[i] += matrix[i][j] * eigenvector[j];
- }
- }
-
- normalizeVector(temp);
- previous = eigenvector;
- eigenvector = temp;
-
- var diff = 0;
- // Compute difference (squared module) of both vectors
- for (var i = 0; i < numNodes; i++) {
- diff += Math.pow(previous[i] - eigenvector[i], 2);
- }
-
- // If difference is less than the desired threshold, stop iterating
- if (diff < epsilon) {
- break;
- }
- }
-
- // Construct result
- var res = {
- rank : function(node) {
- if (is.string(node)) {
- // is a selector string
- var nodeId = (cy.filter(node)[0]).id();
- } else {
- // is a node object
- var nodeId = node.id();
- }
- return eigenvector[id2position[nodeId]];
- }
- };
-
-
- return res;
- } // pageRank
-
-}); // elesfn
-
-module.exports = elesfn;
-
-},{"../../is":77}],12:[function(_dereq_,module,exports){
-'use strict';
-
-var define = _dereq_('../define');
-
-var elesfn = ({
- animate: define.animate(),
- animation: define.animation(),
- animated: define.animated(),
- clearQueue: define.clearQueue(),
- delay: define.delay(),
- delayAnimation: define.delayAnimation(),
- stop: define.stop()
-});
-
-module.exports = elesfn;
-
-},{"../define":41}],13:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-
-var elesfn = ({
- classes: function( classes ){
- classes = classes.match(/\S+/g) || [];
- var self = this;
- var changed = [];
- var classesMap = {};
-
- // fill in classes map
- for( var i = 0; i < classes.length; i++ ){
- var cls = classes[i];
-
- classesMap[ cls ] = true;
- }
-
- // check and update each ele
- for( var j = 0; j < self.length; j++ ){
- var ele = self[j];
- var _p = ele._private;
- var eleClasses = _p.classes;
- var changedEle = false;
-
- // check if ele has all of the passed classes
- for( var i = 0; i < classes.length; i++ ){
- var cls = classes[i];
- var eleHasClass = eleClasses[ cls ];
-
- if( !eleHasClass ){
- changedEle = true;
- break;
- }
- }
-
- // check if ele has classes outside of those passed
- if( !changedEle ){ for( var eleCls in eleClasses ){
- var eleHasClass = eleClasses[ eleCls ];
- var specdClass = classesMap[ eleCls ]; // i.e. this class is passed to the function
-
- if( eleHasClass && !specdClass ){
- changedEle = true;
- break;
- }
- } }
-
- if( changedEle ){
- _p.classes = util.copy( classesMap );
-
- changed.push( ele );
- }
- }
-
- // trigger update style on those eles that had class changes
- if( changed.length > 0 ){
- this.spawn(changed)
- .updateStyle()
- .trigger('class')
- ;
- }
-
- return self;
- },
-
- addClass: function( classes ){
- return this.toggleClass( classes, true );
- },
-
- hasClass: function( className ){
- var ele = this[0];
- return ( ele != null && ele._private.classes[className] ) ? true : false;
- },
-
- toggleClass: function( classesStr, toggle ){
- var classes = classesStr.match(/\S+/g) || [];
- var self = this;
- var changed = []; // eles who had classes changed
-
- for( var i = 0, il = self.length; i < il; i++ ){
- var ele = self[i];
- var changedEle = false;
-
- for( var j = 0; j < classes.length; j++ ){
- var cls = classes[j];
- var eleClasses = ele._private.classes;
- var hasClass = eleClasses[cls];
- var shouldAdd = toggle || (toggle === undefined && !hasClass);
-
- if( shouldAdd ){
- eleClasses[cls] = true;
-
- if( !hasClass && !changedEle ){
- changed.push(ele);
- changedEle = true;
- }
- } else { // then remove
- eleClasses[cls] = false;
-
- if( hasClass && !changedEle ){
- changed.push(ele);
- changedEle = true;
- }
- }
-
- } // for j classes
- } // for i eles
-
- // trigger update style on those eles that had class changes
- if( changed.length > 0 ){
- this.spawn(changed)
- .updateStyle()
- .trigger('class')
- ;
- }
-
- return self;
- },
-
- removeClass: function( classes ){
- return this.toggleClass( classes, false );
- },
-
- flashClass: function( classes, duration ){
- var self = this;
-
- if( duration == null ){
- duration = 250;
- } else if( duration === 0 ){
- return self; // nothing to do really
- }
-
- self.addClass( classes );
- setTimeout(function(){
- self.removeClass( classes );
- }, duration);
-
- return self;
- }
-});
-
-module.exports = elesfn;
-
-},{"../util":94}],14:[function(_dereq_,module,exports){
-'use strict';
-
-var elesfn = ({
- allAre: function( selector ){
- return this.filter(selector).length === this.length;
- },
-
- is: function( selector ){
- return this.filter(selector).length > 0;
- },
-
- some: function( fn, thisArg ){
- for( var i = 0; i < this.length; i++ ){
- var ret = !thisArg ? fn( this[i], i, this ) : fn.apply( thisArg, [ this[i], i, this ] );
-
- if( ret ){
- return true;
- }
- }
-
- return false;
- },
-
- every: function( fn, thisArg ){
- for( var i = 0; i < this.length; i++ ){
- var ret = !thisArg ? fn( this[i], i, this ) : fn.apply( thisArg, [ this[i], i, this ] );
-
- if( !ret ){
- return false;
- }
- }
-
- return true;
- },
-
- same: function( collection ){
- collection = this.cy().collection( collection );
-
- // cheap extra check
- if( this.length !== collection.length ){
- return false;
- }
-
- return this.intersect( collection ).length === this.length;
- },
-
- anySame: function( collection ){
- collection = this.cy().collection( collection );
-
- return this.intersect( collection ).length > 0;
- },
-
- allAreNeighbors: function( collection ){
- collection = this.cy().collection( collection );
-
- return this.neighborhood().intersect( collection ).length === collection.length;
- }
-});
-
-elesfn.allAreNeighbours = elesfn.allAreNeighbors;
-
-module.exports = elesfn;
-
-},{}],15:[function(_dereq_,module,exports){
-'use strict';
-
-var elesfn = ({
- parent: function( selector ){
- var parents = [];
- var cy = this._private.cy;
-
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
- var parent = cy.getElementById( ele._private.data.parent );
-
- if( parent.size() > 0 ){
- parents.push( parent );
- }
- }
-
- return this.spawn( parents, { unique: true } ).filter( selector );
- },
-
- parents: function( selector ){
- var parents = [];
-
- var eles = this.parent();
- while( eles.nonempty() ){
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- parents.push( ele );
- }
-
- eles = eles.parent();
- }
-
- return this.spawn( parents, { unique: true } ).filter( selector );
- },
-
- commonAncestors: function( selector ){
- var ancestors;
-
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
- var parents = ele.parents();
-
- ancestors = ancestors || parents;
-
- ancestors = ancestors.intersect( parents ); // current list must be common with current ele parents set
- }
-
- return ancestors.filter( selector );
- },
-
- orphans: function( selector ){
- return this.stdFilter(function( ele ){
- return ele.isNode() && ele.parent().empty();
- }).filter( selector );
- },
-
- nonorphans: function( selector ){
- return this.stdFilter(function( ele ){
- return ele.isNode() && ele.parent().nonempty();
- }).filter( selector );
- },
-
- children: function( selector ){
- var children = [];
-
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
- children = children.concat( ele._private.children );
- }
-
- return this.spawn( children, { unique: true } ).filter( selector );
- },
-
- siblings: function( selector ){
- return this.parent().children().not( this ).filter( selector );
- },
-
- isParent: function(){
- var ele = this[0];
-
- if( ele ){
- return ele._private.children.length !== 0;
- }
- },
-
- isChild: function(){
- var ele = this[0];
-
- if( ele ){
- return ele._private.data.parent !== undefined && ele.parent().length !== 0;
- }
- },
-
- descendants: function( selector ){
- var elements = [];
-
- function add( eles ){
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
-
- elements.push( ele );
-
- if( ele.children().nonempty() ){
- add( ele.children() );
- }
- }
- }
-
- add( this.children() );
-
- return this.spawn( elements, { unique: true } ).filter( selector );
- }
-});
-
-// aliases
-elesfn.ancestors = elesfn.parents;
-
-module.exports = elesfn;
-
-},{}],16:[function(_dereq_,module,exports){
-'use strict';
-
-var define = _dereq_('../define');
-var fn, elesfn;
-
-fn = elesfn = ({
-
- data: define.data({
- field: 'data',
- bindingEvent: 'data',
- allowBinding: true,
- allowSetting: true,
- settingEvent: 'data',
- settingTriggersEvent: true,
- triggerFnName: 'trigger',
- allowGetting: true,
- immutableKeys: {
- 'id': true,
- 'source': true,
- 'target': true,
- 'parent': true
- },
- updateStyle: true
- }),
-
- removeData: define.removeData({
- field: 'data',
- event: 'data',
- triggerFnName: 'trigger',
- triggerEvent: true,
- immutableKeys: {
- 'id': true,
- 'source': true,
- 'target': true,
- 'parent': true
- },
- updateStyle: true
- }),
-
- scratch: define.data({
- field: 'scratch',
- bindingEvent: 'scratch',
- allowBinding: true,
- allowSetting: true,
- settingEvent: 'scratch',
- settingTriggersEvent: true,
- triggerFnName: 'trigger',
- allowGetting: true,
- updateStyle: true
- }),
-
- removeScratch: define.removeData({
- field: 'scratch',
- event: 'scratch',
- triggerFnName: 'trigger',
- triggerEvent: true,
- updateStyle: true
- }),
-
- rscratch: define.data({
- field: 'rscratch',
- allowBinding: false,
- allowSetting: true,
- settingTriggersEvent: false,
- allowGetting: true
- }),
-
- removeRscratch: define.removeData({
- field: 'rscratch',
- triggerEvent: false
- }),
-
- id: function(){
- var ele = this[0];
-
- if( ele ){
- return ele._private.data.id;
- }
- }
-
-});
-
-// aliases
-fn.attr = fn.data;
-fn.removeAttr = fn.removeData;
-
-module.exports = elesfn;
-
-},{"../define":41}],17:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-
-var elesfn = {};
-
-function defineDegreeFunction(callback){
- return function( includeLoops ){
- var self = this;
-
- if( includeLoops === undefined ){
- includeLoops = true;
- }
-
- if( self.length === 0 ){ return; }
-
- if( self.isNode() && !self.removed() ){
- var degree = 0;
- var node = self[0];
- var connectedEdges = node._private.edges;
-
- for( var i = 0; i < connectedEdges.length; i++ ){
- var edge = connectedEdges[i];
-
- if( !includeLoops && edge.isLoop() ){
- continue;
- }
-
- degree += callback( node, edge );
- }
-
- return degree;
- } else {
- return;
- }
- };
-}
-
-util.extend(elesfn, {
- degree: defineDegreeFunction(function(node, edge){
- if( edge.source().same( edge.target() ) ){
- return 2;
- } else {
- return 1;
- }
- }),
-
- indegree: defineDegreeFunction(function(node, edge){
- if( edge.target().same(node) ){
- return 1;
- } else {
- return 0;
- }
- }),
-
- outdegree: defineDegreeFunction(function(node, edge){
- if( edge.source().same(node) ){
- return 1;
- } else {
- return 0;
- }
- })
-});
-
-function defineDegreeBoundsFunction(degreeFn, callback){
- return function( includeLoops ){
- var ret;
- var nodes = this.nodes();
-
- for( var i = 0; i < nodes.length; i++ ){
- var ele = nodes[i];
- var degree = ele[degreeFn]( includeLoops );
- if( degree !== undefined && (ret === undefined || callback(degree, ret)) ){
- ret = degree;
- }
- }
-
- return ret;
- };
-}
-
-util.extend(elesfn, {
- minDegree: defineDegreeBoundsFunction('degree', function(degree, min){
- return degree < min;
- }),
-
- maxDegree: defineDegreeBoundsFunction('degree', function(degree, max){
- return degree > max;
- }),
-
- minIndegree: defineDegreeBoundsFunction('indegree', function(degree, min){
- return degree < min;
- }),
-
- maxIndegree: defineDegreeBoundsFunction('indegree', function(degree, max){
- return degree > max;
- }),
-
- minOutdegree: defineDegreeBoundsFunction('outdegree', function(degree, min){
- return degree < min;
- }),
-
- maxOutdegree: defineDegreeBoundsFunction('outdegree', function(degree, max){
- return degree > max;
- })
-});
-
-util.extend(elesfn, {
- totalDegree: function( includeLoops ){
- var total = 0;
- var nodes = this.nodes();
-
- for( var i = 0; i < nodes.length; i++ ){
- total += nodes[i].degree( includeLoops );
- }
-
- return total;
- }
-});
-
-module.exports = elesfn;
-
-},{"../util":94}],18:[function(_dereq_,module,exports){
-'use strict';
-
-var define = _dereq_('../define');
-var is = _dereq_('../is');
-var util = _dereq_('../util');
-var fn, elesfn;
-
-fn = elesfn = ({
-
- position: define.data({
- field: 'position',
- bindingEvent: 'position',
- allowBinding: true,
- allowSetting: true,
- settingEvent: 'position',
- settingTriggersEvent: true,
- triggerFnName: 'rtrigger',
- allowGetting: true,
- validKeys: ['x', 'y'],
- onSet: function( eles ){
- var updatedEles = eles.updateCompoundBounds();
- updatedEles.rtrigger('position');
- },
- canSet: function( ele ){
- return !ele.locked() && !ele.isParent();
- }
- }),
-
- // position but no notification to renderer
- silentPosition: define.data({
- field: 'position',
- bindingEvent: 'position',
- allowBinding: false,
- allowSetting: true,
- settingEvent: 'position',
- settingTriggersEvent: false,
- triggerFnName: 'trigger',
- allowGetting: true,
- validKeys: ['x', 'y'],
- onSet: function( eles ){
- eles.updateCompoundBounds();
- },
- canSet: function( ele ){
- return !ele.locked() && !ele.isParent();
- }
- }),
-
- positions: function( pos, silent ){
- if( is.plainObject(pos) ){
- this.position(pos);
-
- } else if( is.fn(pos) ){
- var fn = pos;
-
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
-
- var pos = fn.apply(ele, [i, ele]);
-
- if( pos && !ele.locked() && !ele.isParent() ){
- var elePos = ele._private.position;
- elePos.x = pos.x;
- elePos.y = pos.y;
- }
- }
-
- var updatedEles = this.updateCompoundBounds();
- var toTrigger = updatedEles.length > 0 ? this.add( updatedEles ) : this;
-
- if( silent ){
- toTrigger.trigger('position');
- } else {
- toTrigger.rtrigger('position');
- }
- }
-
- return this; // chaining
- },
-
- silentPositions: function( pos ){
- return this.positions( pos, true );
- },
-
- // get/set the rendered (i.e. on screen) positon of the element
- renderedPosition: function( dim, val ){
- var ele = this[0];
- var cy = this.cy();
- var zoom = cy.zoom();
- var pan = cy.pan();
- var rpos = is.plainObject( dim ) ? dim : undefined;
- var setting = rpos !== undefined || ( val !== undefined && is.string(dim) );
-
- if( ele && ele.isNode() ){ // must have an element and must be a node to return position
- if( setting ){
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
-
- if( val !== undefined ){ // set one dimension
- ele._private.position[dim] = ( val - pan[dim] )/zoom;
- } else if( rpos !== undefined ){ // set whole position
- ele._private.position = {
- x: ( rpos.x - pan.x ) /zoom,
- y: ( rpos.y - pan.y ) /zoom
- };
- }
- }
-
- this.rtrigger('position');
- } else { // getting
- var pos = ele._private.position;
- rpos = {
- x: pos.x * zoom + pan.x,
- y: pos.y * zoom + pan.y
- };
-
- if( dim === undefined ){ // then return the whole rendered position
- return rpos;
- } else { // then return the specified dimension
- return rpos[ dim ];
- }
- }
- } else if( !setting ){
- return undefined; // for empty collection case
- }
-
- return this; // chaining
- },
-
- // get/set the position relative to the parent
- relativePosition: function( dim, val ){
- var ele = this[0];
- var cy = this.cy();
- var ppos = is.plainObject( dim ) ? dim : undefined;
- var setting = ppos !== undefined || ( val !== undefined && is.string(dim) );
- var hasCompoundNodes = cy.hasCompoundNodes();
-
- if( ele && ele.isNode() ){ // must have an element and must be a node to return position
- if( setting ){
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
- var parent = hasCompoundNodes ? ele.parent() : null;
- var hasParent = parent && parent.length > 0;
- var relativeToParent = hasParent;
-
- if( hasParent ){
- parent = parent[0];
- }
-
- var origin = relativeToParent ? parent._private.position : { x: 0, y: 0 };
-
- if( val !== undefined ){ // set one dimension
- ele._private.position[dim] = val + origin[dim];
- } else if( ppos !== undefined ){ // set whole position
- ele._private.position = {
- x: ppos.x + origin.x,
- y: ppos.y + origin.y
- };
- }
- }
-
- this.rtrigger('position');
-
- } else { // getting
- var pos = ele._private.position;
- var parent = hasCompoundNodes ? ele.parent() : null;
- var hasParent = parent && parent.length > 0;
- var relativeToParent = hasParent;
-
- if( hasParent ){
- parent = parent[0];
- }
-
- var origin = relativeToParent ? parent._private.position : { x: 0, y: 0 };
-
- ppos = {
- x: pos.x - origin.x,
- y: pos.y - origin.y
- };
-
- if( dim === undefined ){ // then return the whole rendered position
- return ppos;
- } else { // then return the specified dimension
- return ppos[ dim ];
- }
- }
- } else if( !setting ){
- return undefined; // for empty collection case
- }
-
- return this; // chaining
- },
-
- renderedBoundingBox: function( options ){
- var bb = this.boundingBox( options );
- var cy = this.cy();
- var zoom = cy.zoom();
- var pan = cy.pan();
-
- var x1 = bb.x1 * zoom + pan.x;
- var x2 = bb.x2 * zoom + pan.x;
- var y1 = bb.y1 * zoom + pan.y;
- var y2 = bb.y2 * zoom + pan.y;
-
- return {
- x1: x1,
- x2: x2,
- y1: y1,
- y2: y2,
- w: x2 - x1,
- h: y2 - y1
- };
- },
-
- updateCompoundBounds: function(){
- var cy = this.cy();
-
- if( !cy.styleEnabled() || !cy.hasCompoundNodes() ){ return cy.collection(); } // save cycles for non compound graphs or when style disabled
-
- var updated = [];
-
- function update( parent ){
- var children = parent.children();
- var style = parent._private.style;
- var includeLabels = style['compound-sizing-wrt-labels'].value === 'include';
- var bb = children.boundingBox({ includeLabels: includeLabels, includeEdges: true });
- var padding = {
- top: style['padding-top'].pfValue,
- bottom: style['padding-bottom'].pfValue,
- left: style['padding-left'].pfValue,
- right: style['padding-right'].pfValue
- };
- var pos = parent._private.position;
- var didUpdate = false;
-
- if( style['width'].value === 'auto' ){
- parent._private.autoWidth = bb.w;
- pos.x = (bb.x1 + bb.x2 - padding.left + padding.right)/2;
- didUpdate = true;
- }
-
- if( style['height'].value === 'auto' ){
- parent._private.autoHeight = bb.h;
- pos.y = (bb.y1 + bb.y2 - padding.top + padding.bottom)/2;
- didUpdate = true;
- }
-
- if( didUpdate ){
- updated.push( parent );
- }
- }
-
- // go up, level by level
- var eles = this.parent();
- while( eles.nonempty() ){
-
- // update each parent node in this level
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
-
- update( ele );
- }
-
- // next level
- eles = eles.parent();
- }
-
- // return changed
- return this.spawn( updated );
- },
-
- // get the bounding box of the elements (in raw model position)
- boundingBox: function( options ){
- var eles = this;
- var cy = eles._private.cy;
- var cy_p = cy._private;
- var styleEnabled = cy_p.styleEnabled;
-
- options = options || util.staticEmptyObject();
-
- var includeNodes = options.includeNodes === undefined ? true : options.includeNodes;
- var includeEdges = options.includeEdges === undefined ? true : options.includeEdges;
- var includeLabels = options.includeLabels === undefined ? true : options.includeLabels;
-
- // recalculate projections etc
- if( styleEnabled ){
- cy_p.renderer.recalculateRenderedStyle( this );
- }
-
- var x1 = Infinity;
- var x2 = -Infinity;
- var y1 = Infinity;
- var y2 = -Infinity;
-
- // find bounds of elements
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var _p = ele._private;
- var style = _p.style;
- var display = styleEnabled ? _p.style['display'].value : 'element';
- var isNode = _p.group === 'nodes';
- var ex1, ex2, ey1, ey2, x, y;
- var includedEle = false;
-
- if( display === 'none' ){ continue; } // then ele doesn't take up space
-
- if( isNode && includeNodes ){
- includedEle = true;
-
- var pos = _p.position;
- x = pos.x;
- y = pos.y;
- var w = ele.outerWidth();
- var halfW = w/2;
- var h = ele.outerHeight();
- var halfH = h/2;
-
- // handle node dimensions
- /////////////////////////
-
- ex1 = x - halfW;
- ex2 = x + halfW;
- ey1 = y - halfH;
- ey2 = y + halfH;
-
- x1 = ex1 < x1 ? ex1 : x1;
- x2 = ex2 > x2 ? ex2 : x2;
- y1 = ey1 < y1 ? ey1 : y1;
- y2 = ey2 > y2 ? ey2 : y2;
-
- } else if( ele.isEdge() && includeEdges ){
- includedEle = true;
-
- var n1 = _p.source;
- var n1_p = n1._private;
- var n1pos = n1_p.position;
-
- var n2 = _p.target;
- var n2_p = n2._private;
- var n2pos = n2_p.position;
-
-
- // handle edge dimensions (rough box estimate)
- //////////////////////////////////////////////
-
- var rstyle = _p.rstyle || {};
- var w = 0;
- var wHalf = 0;
-
- if( styleEnabled ){
- w = style['width'].pfValue;
- wHalf = w/2;
- }
-
- ex1 = n1pos.x;
- ex2 = n2pos.x;
- ey1 = n1pos.y;
- ey2 = n2pos.y;
-
- if( ex1 > ex2 ){
- var temp = ex1;
- ex1 = ex2;
- ex2 = temp;
- }
-
- if( ey1 > ey2 ){
- var temp = ey1;
- ey1 = ey2;
- ey2 = temp;
- }
-
- // take into account edge width
- ex1 -= wHalf;
- ex2 += wHalf;
- ey1 -= wHalf;
- ey2 += wHalf;
-
- x1 = ex1 < x1 ? ex1 : x1;
- x2 = ex2 > x2 ? ex2 : x2;
- y1 = ey1 < y1 ? ey1 : y1;
- y2 = ey2 > y2 ? ey2 : y2;
-
- // handle points along edge (sanity check)
- //////////////////////////////////////////
-
- if( styleEnabled ){
- var pts = rstyle.bezierPts || rstyle.linePts || [];
-
- for( var j = 0; j < pts.length; j++ ){
- var pt = pts[j];
-
- ex1 = pt.x - wHalf;
- ex2 = pt.x + wHalf;
- ey1 = pt.y - wHalf;
- ey2 = pt.y + wHalf;
-
- x1 = ex1 < x1 ? ex1 : x1;
- x2 = ex2 > x2 ? ex2 : x2;
- y1 = ey1 < y1 ? ey1 : y1;
- y2 = ey2 > y2 ? ey2 : y2;
- }
- }
-
- // precise haystacks (sanity check)
- ///////////////////////////////////
-
- if( styleEnabled && style['curve-style'].strValue === 'haystack' ){
- var hpts = rstyle.haystackPts;
-
- ex1 = hpts[0].x;
- ey1 = hpts[0].y;
- ex2 = hpts[1].x;
- ey2 = hpts[1].y;
-
- if( ex1 > ex2 ){
- var temp = ex1;
- ex1 = ex2;
- ex2 = temp;
- }
-
- if( ey1 > ey2 ){
- var temp = ey1;
- ey1 = ey2;
- ey2 = temp;
- }
-
- x1 = ex1 < x1 ? ex1 : x1;
- x2 = ex2 > x2 ? ex2 : x2;
- y1 = ey1 < y1 ? ey1 : y1;
- y2 = ey2 > y2 ? ey2 : y2;
- }
-
- } // edges
-
-
- // handle label dimensions
- //////////////////////////
-
- if( styleEnabled ){
-
- var _p = ele._private;
- var style = _p.style;
- var rstyle = _p.rstyle;
- var label = style['label'].strValue;
- var fontSize = style['font-size'];
- var halign = style['text-halign'];
- var valign = style['text-valign'];
- var labelWidth = rstyle.labelWidth;
- var labelHeight = rstyle.labelHeight;
- var labelX = rstyle.labelX;
- var labelY = rstyle.labelY;
- var isEdge = ele.isEdge();
- var autorotate = style['edge-text-rotation'].strValue === 'autorotate';
-
- if( includeLabels && label && fontSize && labelHeight != null && labelWidth != null && labelX != null && labelY != null && halign && valign ){
- var lh = labelHeight;
- var lw = labelWidth;
- var lx1, lx2, ly1, ly2;
-
- if( isEdge ){
- lx1 = labelX - lw/2;
- lx2 = labelX + lw/2;
- ly1 = labelY - lh/2;
- ly2 = labelY + lh/2;
-
- if( autorotate ){
- var theta = _p.rscratch.labelAngle;
- var cos = Math.cos( theta );
- var sin = Math.sin( theta );
-
- var rotate = function( x, y ){
- x = x - labelX;
- y = y - labelY;
-
- return {
- x: x*cos - y*sin + labelX,
- y: x*sin + y*cos + labelY
- };
- };
-
- var px1y1 = rotate( lx1, ly1 );
- var px1y2 = rotate( lx1, ly2 );
- var px2y1 = rotate( lx2, ly1 );
- var px2y2 = rotate( lx2, ly2 );
-
- lx1 = Math.min( px1y1.x, px1y2.x, px2y1.x, px2y2.x );
- lx2 = Math.max( px1y1.x, px1y2.x, px2y1.x, px2y2.x );
- ly1 = Math.min( px1y1.y, px1y2.y, px2y1.y, px2y2.y );
- ly2 = Math.max( px1y1.y, px1y2.y, px2y1.y, px2y2.y );
- }
- } else {
- switch( halign.value ){
- case 'left':
- lx1 = labelX - lw;
- lx2 = labelX;
- break;
-
- case 'center':
- lx1 = labelX - lw/2;
- lx2 = labelX + lw/2;
- break;
-
- case 'right':
- lx1 = labelX;
- lx2 = labelX + lw;
- break;
- }
-
- switch( valign.value ){
- case 'top':
- ly1 = labelY - lh;
- ly2 = labelY;
- break;
-
- case 'center':
- ly1 = labelY - lh/2;
- ly2 = labelY + lh/2;
- break;
-
- case 'bottom':
- ly1 = labelY;
- ly2 = labelY + lh;
- break;
- }
- }
-
- x1 = lx1 < x1 ? lx1 : x1;
- x2 = lx2 > x2 ? lx2 : x2;
- y1 = ly1 < y1 ? ly1 : y1;
- y2 = ly2 > y2 ? ly2 : y2;
- }
- } // style enabled for labels
- } // for
-
- var noninf = function(x){
- if( x === Infinity || x === -Infinity ){
- return 0;
- }
-
- return x;
- };
-
- x1 = noninf(x1);
- x2 = noninf(x2);
- y1 = noninf(y1);
- y2 = noninf(y2);
-
- return {
- x1: x1,
- x2: x2,
- y1: y1,
- y2: y2,
- w: x2 - x1,
- h: y2 - y1
- };
- }
-});
-
-var defineDimFns = function( opts ){
- opts.uppercaseName = util.capitalize( opts.name );
- opts.autoName = 'auto' + opts.uppercaseName;
- opts.labelName = 'label' + opts.uppercaseName;
- opts.outerName = 'outer' + opts.uppercaseName;
- opts.uppercaseOuterName = util.capitalize( opts.outerName );
-
- fn[ opts.name ] = function dimImpl(){
- var ele = this[0];
- var _p = ele._private;
- var cy = _p.cy;
- var styleEnabled = cy._private.styleEnabled;
-
- if( ele ){
- if( styleEnabled ){
- var d = _p.style[ opts.name ];
-
- switch( d.strValue ){
- case 'auto':
- return _p[ opts.autoName ] || 0;
- case 'label':
- return _p.rstyle[ opts.labelName ] || 0;
- default:
- return d.pfValue;
- }
- } else {
- return 1;
- }
- }
- };
-
- fn[ 'outer' + opts.uppercaseName ] = function outerDimImpl(){
- var ele = this[0];
- var _p = ele._private;
- var cy = _p.cy;
- var styleEnabled = cy._private.styleEnabled;
-
- if( ele ){
- if( styleEnabled ){
- var style = _p.style;
- var dim = ele[ opts.name ]();
- var border = style['border-width'].pfValue;
- var padding = style[ opts.paddings[0] ].pfValue + style[ opts.paddings[1] ].pfValue;
-
- return dim + border + padding;
- } else {
- return 1;
- }
- }
- };
-
- fn[ 'rendered' + opts.uppercaseName ] = function renderedDimImpl(){
- var ele = this[0];
-
- if( ele ){
- var d = ele[ opts.name ]();
- return d * this.cy().zoom();
- }
- };
-
- fn[ 'rendered' + opts.uppercaseOuterName ] = function renderedOuterDimImpl(){
- var ele = this[0];
-
- if( ele ){
- var od = ele[ opts.outerName ]();
- return od * this.cy().zoom();
- }
- };
-};
-
-defineDimFns({
- name: 'width',
- paddings: ['padding-left', 'padding-right']
-});
-
-defineDimFns({
- name: 'height',
- paddings: ['padding-top', 'padding-bottom']
-});
-
-// aliases
-fn.modelPosition = fn.point = fn.position;
-fn.modelPositions = fn.points = fn.positions;
-fn.renderedPoint = fn.renderedPosition;
-fn.relativePoint = fn.relativePosition;
-fn.boundingbox = fn.boundingBox;
-fn.renderedBoundingbox = fn.renderedBoundingBox;
-
-module.exports = elesfn;
-
-},{"../define":41,"../is":77,"../util":94}],19:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-var is = _dereq_('../is');
-
-// represents a node or an edge
-var Element = function(cy, params, restore){
- if( !(this instanceof Element) ){
- return new Element(cy, params, restore);
- }
-
- var self = this;
- restore = (restore === undefined || restore ? true : false);
-
- if( cy === undefined || params === undefined || !is.core(cy) ){
- util.error('An element must have a core reference and parameters set');
- return;
- }
-
- var group = params.group;
-
- // try to automatically infer the group if unspecified
- if( group == null ){
- if( params.data.source != null && params.data.target != null ){
- group = 'edges';
- } else {
- group = 'nodes';
- }
- }
-
- // validate group
- if( group !== 'nodes' && group !== 'edges' ){
- util.error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
- return;
- }
-
- // make the element array-like, just like a collection
- this.length = 1;
- this[0] = this;
-
- // NOTE: when something is added here, add also to ele.json()
- this._private = {
- cy: cy,
- single: true, // indicates this is an element
- data: params.data || {}, // data object
- position: params.position || {}, // (x, y) position pair
- autoWidth: undefined, // width and height of nodes calculated by the renderer when set to special 'auto' value
- autoHeight: undefined,
- listeners: [], // array of bound listeners
- group: group, // string; 'nodes' or 'edges'
- style: {}, // properties as set by the style
- rstyle: {}, // properties for style sent from the renderer to the core
- styleCxts: [], // applied style contexts from the styler
- removed: true, // whether it's inside the vis; true if removed (set true here since we call restore)
- selected: params.selected ? true : false, // whether it's selected
- selectable: params.selectable === undefined ? true : ( params.selectable ? true : false ), // whether it's selectable
- locked: params.locked ? true : false, // whether the element is locked (cannot be moved)
- grabbed: false, // whether the element is grabbed by the mouse; renderer sets this privately
- grabbable: params.grabbable === undefined ? true : ( params.grabbable ? true : false ), // whether the element can be grabbed
- active: false, // whether the element is active from user interaction
- classes: {}, // map ( className => true )
- animation: { // object for currently-running animations
- current: [],
- queue: []
- },
- rscratch: {}, // object in which the renderer can store information
- scratch: params.scratch || {}, // scratch objects
- edges: [], // array of connected edges
- children: [] // array of children
- };
-
- // renderedPosition overrides if specified
- if( params.renderedPosition ){
- var rpos = params.renderedPosition;
- var pan = cy.pan();
- var zoom = cy.zoom();
-
- this._private.position = {
- x: (rpos.x - pan.x)/zoom,
- y: (rpos.y - pan.y)/zoom
- };
- }
-
- if( is.string(params.classes) ){
- var classes = params.classes.split(/\s+/);
- for( var i = 0, l = classes.length; i < l; i++ ){
- var cls = classes[i];
- if( !cls || cls === '' ){ continue; }
-
- self._private.classes[cls] = true;
- }
- }
-
- if( params.style || params.css ){
- cy.style().applyBypass( this, params.style || params.css );
- }
-
- if( restore === undefined || restore ){
- this.restore();
- }
-
-};
-
-module.exports = Element;
-
-},{"../is":77,"../util":94}],20:[function(_dereq_,module,exports){
-'use strict';
-
-var define = _dereq_('../define');
-
-var elesfn = ({
- on: define.on(), // .on( events [, selector] [, data], handler)
- one: define.on({ unbindSelfOnTrigger: true }),
- once: define.on({ unbindAllBindersOnTrigger: true }),
- off: define.off(), // .off( events [, selector] [, handler] )
- trigger: define.trigger(), // .trigger( events [, extraParams] )
-
- rtrigger: function(event, extraParams){ // for internal use only
- if( this.length === 0 ){ return; } // empty collections don't need to notify anything
-
- // notify renderer
- this.cy().notify({
- type: event,
- collection: this
- });
-
- this.trigger(event, extraParams);
- return this;
- }
-});
-
-// aliases:
-define.eventAliasesOn( elesfn );
-
-module.exports = elesfn;
-
-},{"../define":41}],21:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var Selector = _dereq_('../selector');
-
-var elesfn = ({
- nodes: function( selector ){
- return this.filter(function(i, element){
- return element.isNode();
- }).filter(selector);
- },
-
- edges: function( selector ){
- return this.filter(function(i, element){
- return element.isEdge();
- }).filter(selector);
- },
-
- filter: function( filter ){
- if( is.fn(filter) ){
- var elements = [];
-
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
-
- if( filter.apply(ele, [i, ele]) ){
- elements.push(ele);
- }
- }
-
- return this.spawn(elements);
-
- } else if( is.string(filter) || is.elementOrCollection(filter) ){
- return Selector(filter).filter(this);
-
- } else if( filter === undefined ){
- return this;
- }
-
- return this.spawn(); // if not handled by above, give 'em an empty collection
- },
-
- not: function( toRemove ){
- if( !toRemove ){
- return this;
- } else {
-
- if( is.string( toRemove ) ){
- toRemove = this.filter( toRemove );
- }
-
- var elements = [];
-
- for( var i = 0; i < this.length; i++ ){
- var element = this[i];
-
- var remove = toRemove._private.ids[ element.id() ];
- if( !remove ){
- elements.push( element );
- }
- }
-
- return this.spawn( elements );
- }
-
- },
-
- absoluteComplement: function(){
- var cy = this._private.cy;
-
- return cy.elements().not( this );
- },
-
- intersect: function( other ){
- // if a selector is specified, then filter by it instead
- if( is.string(other) ){
- var selector = other;
- return this.filter( selector );
- }
-
- var elements = [];
- var col1 = this;
- var col2 = other;
- var col1Smaller = this.length < other.length;
- // var ids1 = col1Smaller ? col1._private.ids : col2._private.ids;
- var ids2 = col1Smaller ? col2._private.ids : col1._private.ids;
- var col = col1Smaller ? col1 : col2;
-
- for( var i = 0; i < col.length; i++ ){
- var id = col[i]._private.data.id;
- var ele = ids2[ id ];
-
- if( ele ){
- elements.push( ele );
- }
- }
-
- return this.spawn( elements );
- },
-
- xor: function( other ){
- var cy = this._private.cy;
-
- if( is.string(other) ){
- other = cy.$( other );
- }
-
- var elements = [];
- var col1 = this;
- var col2 = other;
-
- var add = function( col, other ){
-
- for( var i = 0; i < col.length; i++ ){
- var ele = col[i];
- var id = ele._private.data.id;
- var inOther = other._private.ids[ id ];
-
- if( !inOther ){
- elements.push( ele );
- }
- }
-
- };
-
- add( col1, col2 );
- add( col2, col1 );
-
- return this.spawn( elements );
- },
-
- diff: function( other ){
- var cy = this._private.cy;
-
- if( is.string(other) ){
- other = cy.$( other );
- }
-
- var left = [];
- var right = [];
- var both = [];
- var col1 = this;
- var col2 = other;
-
- var add = function( col, other, retEles ){
-
- for( var i = 0; i < col.length; i++ ){
- var ele = col[i];
- var id = ele._private.data.id;
- var inOther = other._private.ids[ id ];
-
- if( inOther ){
- both.push( ele );
- } else {
- retEles.push( ele );
- }
- }
-
- };
-
- add( col1, col2, left );
- add( col2, col1, right );
-
- return {
- left: this.spawn( left, { unique: true } ),
- right: this.spawn( right, { unique: true } ),
- both: this.spawn( both, { unique: true } )
- };
- },
-
- add: function( toAdd ){
- var cy = this._private.cy;
-
- if( !toAdd ){
- return this;
- }
-
- if( is.string(toAdd) ){
- var selector = toAdd;
- toAdd = cy.elements(selector);
- }
-
- var elements = [];
-
- for( var i = 0; i < this.length; i++ ){
- elements.push( this[i] );
- }
-
- for( var i = 0; i < toAdd.length; i++ ){
-
- var add = !this._private.ids[ toAdd[i].id() ];
- if( add ){
- elements.push( toAdd[i] );
- }
- }
-
- return this.spawn(elements);
- },
-
- // in place merge on calling collection
- merge: function( toAdd ){
- var _p = this._private;
- var cy = _p.cy;
-
- if( !toAdd ){
- return this;
- }
-
- if( is.string(toAdd) ){
- var selector = toAdd;
- toAdd = cy.elements(selector);
- }
-
- for( var i = 0; i < toAdd.length; i++ ){
- var toAddEle = toAdd[i];
- var id = toAddEle.id();
- var add = !_p.ids[ id ];
-
- if( add ){
- var index = this.length++;
-
- this[ index ] = toAddEle;
- _p.ids[ id ] = toAddEle;
- _p.indexes[ id ] = index;
- }
- }
-
- return this; // chaining
- },
-
- // remove single ele in place in calling collection
- unmergeOne: function( ele ){
- ele = ele[0];
-
- var _p = this._private;
- var id = ele.id();
- var i = _p.indexes[ id ];
-
- if( i == null ){
- return this; // no need to remove
- }
-
- // remove ele
- this[i] = undefined;
- _p.ids[ id ] = undefined;
- _p.indexes[ id ] = undefined;
-
- var unmergedLastEle = i === this.length - 1;
-
- // replace empty spot with last ele in collection
- if( this.length > 1 && !unmergedLastEle ){
- var lastEleI = this.length - 1;
- var lastEle = this[ lastEleI ];
-
- this[ lastEleI ] = undefined;
- this[i] = lastEle;
- _p.indexes[ lastEle.id() ] = i;
- }
-
- // the collection is now 1 ele smaller
- this.length--;
-
- return this;
- },
-
- // remove eles in place on calling collection
- unmerge: function( toRemove ){
- var cy = this._private.cy;
-
- if( !toRemove ){
- return this;
- }
-
- if( is.string(toRemove) ){
- var selector = toRemove;
- toRemove = cy.elements(selector);
- }
-
- for( var i = 0; i < toRemove.length; i++ ){
- this.unmergeOne( toRemove[i] );
- }
-
- return this; // chaining
- },
-
- map: function( mapFn, thisArg ){
- var arr = [];
- var eles = this;
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var ret = thisArg ? mapFn.apply( thisArg, [ele, i, eles] ) : mapFn( ele, i, eles );
-
- arr.push( ret );
- }
-
- return arr;
- },
-
- stdFilter: function( fn, thisArg ){
- var filterEles = [];
- var eles = this;
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var include = thisArg ? fn.apply( thisArg, [ele, i, eles] ) : fn( ele, i, eles );
-
- if( include ){
- filterEles.push( ele );
- }
- }
-
- return this.spawn( filterEles );
- },
-
- max: function( valFn, thisArg ){
- var max = -Infinity;
- var maxEle;
- var eles = this;
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var val = thisArg ? valFn.apply( thisArg, [ ele, i, eles ] ) : valFn( ele, i, eles );
-
- if( val > max ){
- max = val;
- maxEle = ele;
- }
- }
-
- return {
- value: max,
- ele: maxEle
- };
- },
-
- min: function( valFn, thisArg ){
- var min = Infinity;
- var minEle;
- var eles = this;
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var val = thisArg ? valFn.apply( thisArg, [ ele, i, eles ] ) : valFn( ele, i, eles );
-
- if( val < min ){
- min = val;
- minEle = ele;
- }
- }
-
- return {
- value: min,
- ele: minEle
- };
- }
-});
-
-// aliases
-var fn = elesfn;
-fn['u'] = fn['|'] = fn['+'] = fn.union = fn.or = fn.add;
-fn['\\'] = fn['!'] = fn['-'] = fn.difference = fn.relativeComplement = fn.subtract = fn.not;
-fn['n'] = fn['&'] = fn['.'] = fn.and = fn.intersection = fn.intersect;
-fn['^'] = fn['(+)'] = fn['(-)'] = fn.symmetricDifference = fn.symdiff = fn.xor;
-fn.fnFilter = fn.filterFn = fn.stdFilter;
-fn.complement = fn.abscomp = fn.absoluteComplement;
-
-module.exports = elesfn;
-
-},{"../is":77,"../selector":81}],22:[function(_dereq_,module,exports){
-'use strict';
-
-var elesfn = ({
- isNode: function(){
- return this.group() === 'nodes';
- },
-
- isEdge: function(){
- return this.group() === 'edges';
- },
-
- isLoop: function(){
- return this.isEdge() && this.source().id() === this.target().id();
- },
-
- isSimple: function(){
- return this.isEdge() && this.source().id() !== this.target().id();
- },
-
- group: function(){
- var ele = this[0];
-
- if( ele ){
- return ele._private.group;
- }
- }
-});
-
-
-module.exports = elesfn;
-
-},{}],23:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-var is = _dereq_('../is');
-
-var Element = _dereq_('./element');
-
-// factory for generating edge ids when no id is specified for a new element
-var idFactory = {
- prefix: 'ele',
- id: 0,
- generate: function(cy, element, tryThisId){
- var json = is.element( element ) ? element._private : element;
- var id = tryThisId != null ? tryThisId : this.prefix + this.id;
-
- if( cy.getElementById(id).empty() ){
- this.id++; // we've used the current id, so move it up
- } else { // otherwise keep trying successive unused ids
- while( !cy.getElementById(id).empty() ){
- id = this.prefix + ( ++this.id );
- }
- }
-
- return id;
- }
-};
-
-// represents a set of nodes, edges, or both together
-var Collection = function(cy, elements, options){
- if( !(this instanceof Collection) ){
- return new Collection(cy, elements, options);
- }
-
- if( cy === undefined || !is.core(cy) ){
- util.error('A collection must have a reference to the core');
- return;
- }
-
- var ids = {};
- var indexes = {};
- var createdElements = false;
-
- if( !elements ){
- elements = [];
- } else if( elements.length > 0 && is.plainObject( elements[0] ) && !is.element( elements[0] ) ){
- createdElements = true;
-
- // make elements from json and restore all at once later
- var eles = [];
- var elesIds = {};
-
- for( var i = 0, l = elements.length; i < l; i++ ){
- var json = elements[i];
-
- if( json.data == null ){
- json.data = {};
- }
-
- var data = json.data;
-
- // make sure newly created elements have valid ids
- if( data.id == null ){
- data.id = idFactory.generate( cy, json );
- } else if( cy.getElementById( data.id ).length !== 0 || elesIds[ data.id ] ){
- continue; // can't create element if prior id already exists
- }
-
- var ele = new Element( cy, json, false );
- eles.push( ele );
- elesIds[ data.id ] = true;
- }
-
- elements = eles;
- }
-
- this.length = 0;
-
- for( var i = 0, l = elements.length; i < l; i++ ){
- var element = elements[i];
- if( !element ){ continue; }
-
- var id = element._private.data.id;
-
- if( !options || (options.unique && !ids[ id ] ) ){
- ids[ id ] = element;
- indexes[ id ] = this.length;
-
- this[ this.length ] = element;
- this.length++;
- }
- }
-
- this._private = {
- cy: cy,
- ids: ids,
- indexes: indexes
- };
-
- // restore the elements if we created them from json
- if( createdElements ){
- this.restore();
- }
-};
-
-// Functions
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// keep the prototypes in sync (an element has the same functions as a collection)
-// and use elefn and elesfn as shorthands to the prototypes
-var elesfn = Element.prototype = Collection.prototype;
-
-elesfn.instanceString = function(){
- return 'collection';
-};
-
-elesfn.spawn = function( cy, eles, opts ){
- if( !is.core(cy) ){ // cy is optional
- opts = eles;
- eles = cy;
- cy = this.cy();
- }
-
- return new Collection( cy, eles, opts );
-};
-
-elesfn.cy = function(){
- return this._private.cy;
-};
-
-elesfn.element = function(){
- return this[0];
-};
-
-elesfn.collection = function(){
- if( is.collection(this) ){
- return this;
- } else { // an element
- return new Collection( this._private.cy, [this] );
- }
-};
-
-elesfn.unique = function(){
- return new Collection( this._private.cy, this, { unique: true } );
-};
-
-elesfn.getElementById = function( id ){
- var cy = this._private.cy;
- var ele = this._private.ids[ id ];
-
- return ele ? ele : new Collection(cy); // get ele or empty collection
-};
-
-elesfn.json = function( obj ){
- var ele = this.element();
- var cy = this.cy();
-
- if( ele == null && obj ){ return this; } // can't set to no eles
-
- if( ele == null ){ return undefined; } // can't get from no eles
-
- var p = ele._private;
-
- if( is.plainObject(obj) ){ // set
-
- cy.startBatch();
-
- if( obj.data ){
- ele.data( obj.data );
- }
-
- if( obj.position ){
- ele.position( obj.position );
- }
-
- // ignore group -- immutable
-
- var checkSwitch = function( k, trueFnName, falseFnName ){
- var obj_k = obj[k];
-
- if( obj_k != null && obj_k !== p[k] ){
- if( obj_k ){
- ele[ trueFnName ]();
- } else {
- ele[ falseFnName ]();
- }
- }
- };
-
- checkSwitch( 'removed', 'remove', 'restore' );
-
- checkSwitch( 'selected', 'select', 'unselect' );
-
- checkSwitch( 'selectable', 'selectify', 'unselectify' );
-
- checkSwitch( 'locked', 'lock', 'unlock' );
-
- checkSwitch( 'grabbable', 'grabify', 'ungrabify' );
-
- if( obj.classes != null ){
- ele.classes( obj.classes );
- }
-
- cy.endBatch();
-
- return this;
-
- } else if( obj === undefined ){ // get
-
- var json = {
- data: util.copy( p.data ),
- position: util.copy( p.position ),
- group: p.group,
- removed: p.removed,
- selected: p.selected,
- selectable: p.selectable,
- locked: p.locked,
- grabbable: p.grabbable,
- classes: null
- };
-
- var classes = [];
- for( var cls in p.classes ){
- if( p.classes[cls] ){
- classes.push(cls);
- }
- }
- json.classes = classes.join(' ');
-
- return json;
- }
-};
-
-elesfn.jsons = function(){
- var jsons = [];
-
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
- var json = ele.json();
-
- jsons.push( json );
- }
-
- return jsons;
-};
-
-elesfn.clone = function(){
- var cy = this.cy();
- var elesArr = [];
-
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
- var json = ele.json();
- var clone = new Element(cy, json, false); // NB no restore
-
- elesArr.push( clone );
- }
-
- return new Collection( cy, elesArr );
-};
-elesfn.copy = elesfn.clone;
-
-elesfn.restore = function( notifyRenderer ){
- var self = this;
- var restored = [];
- var cy = self.cy();
-
- if( notifyRenderer === undefined ){
- notifyRenderer = true;
- }
-
- // create arrays of nodes and edges, since we need to
- // restore the nodes first
- var elements = [];
- var nodes = [], edges = [];
- var numNodes = 0;
- var numEdges = 0;
- for( var i = 0, l = self.length; i < l; i++ ){
- var ele = self[i];
-
- // keep nodes first in the array and edges after
- if( ele.isNode() ){ // put to front of array if node
- nodes.push( ele );
- numNodes++;
- } else { // put to end of array if edge
- edges.push( ele );
- numEdges++;
- }
- }
-
- elements = nodes.concat( edges );
-
- // now, restore each element
- for( var i = 0, l = elements.length; i < l; i++ ){
- var ele = elements[i];
-
- if( !ele.removed() ){
- // don't need to do anything
- continue;
- }
-
- var _private = ele._private;
- var data = _private.data;
-
- // set id and validate
- if( data.id === undefined ){
- data.id = idFactory.generate( cy, ele );
-
- } else if( is.number(data.id) ){
- data.id = '' + data.id; // now it's a string
-
- } else if( is.emptyString(data.id) || !is.string(data.id) ){
- util.error('Can not create element with invalid string ID `' + data.id + '`');
-
- // can't create element if it has empty string as id or non-string id
- continue;
- } else if( cy.getElementById( data.id ).length !== 0 ){
- util.error('Can not create second element with ID `' + data.id + '`');
-
- // can't create element if one already has that id
- continue;
- }
-
- var id = data.id; // id is finalised, now let's keep a ref
-
- if( ele.isNode() ){ // extra checks for nodes
- var node = ele;
- var pos = _private.position;
-
- // make sure the nodes have a defined position
-
- if( pos.x == null ){
- pos.x = 0;
- }
-
- if( pos.y == null ){
- pos.y = 0;
- }
- }
-
- if( ele.isEdge() ){ // extra checks for edges
-
- var edge = ele;
- var fields = ['source', 'target'];
- var fieldsLength = fields.length;
- var badSourceOrTarget = false;
- for(var j = 0; j < fieldsLength; j++){
-
- var field = fields[j];
- var val = data[field];
-
- if( is.number(val) ){
- val = data[field] = '' + data[field]; // now string
- }
-
- if( val == null || val === '' ){
- // can't create if source or target is not defined properly
- util.error('Can not create edge `' + id + '` with unspecified ' + field);
- badSourceOrTarget = true;
- } else if( cy.getElementById(val).empty() ){
- // can't create edge if one of its nodes doesn't exist
- util.error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
- badSourceOrTarget = true;
- }
- }
-
- if( badSourceOrTarget ){ continue; } // can't create this
-
- var src = cy.getElementById( data.source );
- var tgt = cy.getElementById( data.target );
-
- src._private.edges.push( edge );
- tgt._private.edges.push( edge );
-
- edge._private.source = src;
- edge._private.target = tgt;
-
- } // if is edge
-
- // create mock ids map for element so it can be used like collections
- _private.ids = {};
- _private.ids[ id ] = ele;
-
- _private.removed = false;
- cy.addToPool( ele );
-
- restored.push( ele );
- } // for each element
-
- // do compound node sanity checks
- for( var i = 0; i < numNodes; i++ ){ // each node
- var node = elements[i];
- var data = node._private.data;
-
- if( is.number(data.parent) ){ // then automake string
- data.parent = '' + data.parent;
- }
-
- var parentId = data.parent;
-
- var specifiedParent = parentId != null;
-
- if( specifiedParent ){
- var parent = cy.getElementById( parentId );
-
- if( parent.empty() ){
- // non-existant parent; just remove it
- data.parent = undefined;
- } else {
- var selfAsParent = false;
- var ancestor = parent;
- while( !ancestor.empty() ){
- if( node.same(ancestor) ){
- // mark self as parent and remove from data
- selfAsParent = true;
- data.parent = undefined; // remove parent reference
-
- // exit or we loop forever
- break;
- }
-
- ancestor = ancestor.parent();
- }
-
- if( !selfAsParent ){
- // connect with children
- parent[0]._private.children.push( node );
- node._private.parent = parent[0];
-
- // let the core know we have a compound graph
- cy._private.hasCompoundNodes = true;
- }
- } // else
- } // if specified parent
- } // for each node
-
- restored = new Collection( cy, restored );
- if( restored.length > 0 ){
-
- var toUpdateStyle = restored.add( restored.connectedNodes() ).add( restored.parent() );
- toUpdateStyle.updateStyle( notifyRenderer );
-
- if( notifyRenderer ){
- restored.rtrigger('add');
- } else {
- restored.trigger('add');
- }
- }
-
- return self; // chainability
-};
-
-elesfn.removed = function(){
- var ele = this[0];
- return ele && ele._private.removed;
-};
-
-elesfn.inside = function(){
- var ele = this[0];
- return ele && !ele._private.removed;
-};
-
-elesfn.remove = function( notifyRenderer ){
- var self = this;
- var removed = [];
- var elesToRemove = [];
- var elesToRemoveIds = {};
- var cy = self._private.cy;
-
- if( notifyRenderer === undefined ){
- notifyRenderer = true;
- }
-
- // add connected edges
- function addConnectedEdges(node){
- var edges = node._private.edges;
- for( var i = 0; i < edges.length; i++ ){
- add( edges[i] );
- }
- }
-
-
- // add descendant nodes
- function addChildren(node){
- var children = node._private.children;
-
- for( var i = 0; i < children.length; i++ ){
- add( children[i] );
- }
- }
-
- function add( ele ){
- var alreadyAdded = elesToRemoveIds[ ele.id() ];
- if( alreadyAdded ){
- return;
- } else {
- elesToRemoveIds[ ele.id() ] = true;
- }
-
- if( ele.isNode() ){
- elesToRemove.push( ele ); // nodes are removed last
-
- addConnectedEdges( ele );
- addChildren( ele );
- } else {
- elesToRemove.unshift( ele ); // edges are removed first
- }
- }
-
- // make the list of elements to remove
- // (may be removing more than specified due to connected edges etc)
-
- for( var i = 0, l = self.length; i < l; i++ ){
- var ele = self[i];
-
- add( ele );
- }
-
- function removeEdgeRef(node, edge){
- var connectedEdges = node._private.edges;
- for( var j = 0; j < connectedEdges.length; j++ ){
- var connectedEdge = connectedEdges[j];
-
- if( edge === connectedEdge ){
- connectedEdges.splice( j, 1 );
- break;
- }
- }
- }
-
- function removeChildRef(parent, ele){
- ele = ele[0];
- parent = parent[0];
- var children = parent._private.children;
-
- for( var j = 0; j < children.length; j++ ){
- if( children[j][0] === ele[0] ){
- children.splice(j, 1);
- break;
- }
- }
- }
-
- for( var i = 0; i < elesToRemove.length; i++ ){
- var ele = elesToRemove[i];
-
- // mark as removed
- ele._private.removed = true;
-
- // remove from core pool
- cy.removeFromPool( ele );
-
- // add to list of removed elements
- removed.push( ele );
-
- if( ele.isEdge() ){ // remove references to this edge in its connected nodes
- var src = ele.source()[0];
- var tgt = ele.target()[0];
-
- removeEdgeRef( src, ele );
- removeEdgeRef( tgt, ele );
-
- } else { // remove reference to parent
- var parent = ele.parent();
-
- if( parent.length !== 0 ){
- removeChildRef(parent, ele);
- }
- }
- }
-
- // check to see if we have a compound graph or not
- var elesStillInside = cy._private.elements;
- cy._private.hasCompoundNodes = false;
- for( var i = 0; i < elesStillInside.length; i++ ){
- var ele = elesStillInside[i];
-
- if( ele.isParent() ){
- cy._private.hasCompoundNodes = true;
- break;
- }
- }
-
- var removedElements = new Collection( this.cy(), removed );
- if( removedElements.size() > 0 ){
- // must manually notify since trigger won't do this automatically once removed
-
- if( notifyRenderer ){
- this.cy().notify({
- type: 'remove',
- collection: removedElements
- });
- }
-
- removedElements.trigger('remove');
- }
-
- // check for empty remaining parent nodes
- var checkedParentId = {};
- for( var i = 0; i < elesToRemove.length; i++ ){
- var ele = elesToRemove[i];
- var isNode = ele._private.group === 'nodes';
- var parentId = ele._private.data.parent;
-
- if( isNode && parentId !== undefined && !checkedParentId[ parentId ] ){
- checkedParentId[ parentId ] = true;
- var parent = cy.getElementById( parentId );
-
- if( parent && parent.length !== 0 && !parent._private.removed && parent.children().length === 0 ){
- parent.updateStyle();
- }
- }
- }
-
- return new Collection( cy, removed );
-};
-
-elesfn.move = function( struct ){
- var cy = this._private.cy;
-
- if( struct.source !== undefined || struct.target !== undefined ){
- var srcId = struct.source;
- var tgtId = struct.target;
- var srcExists = cy.getElementById( srcId ).length > 0;
- var tgtExists = cy.getElementById( tgtId ).length > 0;
-
- if( srcExists || tgtExists ){
- var jsons = this.jsons();
-
- this.remove();
-
- for( var i = 0; i < jsons.length; i++ ){
- var json = jsons[i];
-
- if( json.group === 'edges' ){
- if( srcExists ){ json.data.source = srcId; }
- if( tgtExists ){ json.data.target = tgtId; }
- }
- }
-
- return cy.add( jsons );
- }
-
- } else if( struct.parent !== undefined ){ // move node to new parent
- var parentId = struct.parent;
- var parentExists = parentId === null || cy.getElementById( parentId ).length > 0;
-
- if( parentExists ){
- var jsons = this.jsons();
- var descs = this.descendants();
- var descsEtc = descs.merge( descs.add(this).connectedEdges() );
-
- this.remove(); // NB: also removes descendants and their connected edges
-
- for( var i = 0; i < this.length; i++ ){
- var json = jsons[i];
-
- if( json.group === 'nodes' ){
- json.data.parent = parentId === null ? undefined : parentId;
- }
- }
- }
-
- return cy.add( jsons ).merge( descsEtc.restore() );
- }
-
- return this; // if nothing done
-};
-
-[
- _dereq_('./algorithms'),
- _dereq_('./animation'),
- _dereq_('./class'),
- _dereq_('./comparators'),
- _dereq_('./compounds'),
- _dereq_('./data'),
- _dereq_('./degree'),
- _dereq_('./dimensions'),
- _dereq_('./events'),
- _dereq_('./filter'),
- _dereq_('./group'),
- _dereq_('./index'),
- _dereq_('./iteration'),
- _dereq_('./layout'),
- _dereq_('./style'),
- _dereq_('./switch-functions'),
- _dereq_('./traversing')
-].forEach(function( props ){
- util.extend( elesfn, props );
-});
-
-module.exports = Collection;
-
-},{"../is":77,"../util":94,"./algorithms":9,"./animation":12,"./class":13,"./comparators":14,"./compounds":15,"./data":16,"./degree":17,"./dimensions":18,"./element":19,"./events":20,"./filter":21,"./group":22,"./index":23,"./iteration":24,"./layout":25,"./style":26,"./switch-functions":27,"./traversing":28}],24:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var zIndexSort = _dereq_('./zsort');
-
-var elesfn = ({
- each: function(fn){
- if( is.fn(fn) ){
- for(var i = 0; i < this.length; i++){
- var ele = this[i];
- var ret = fn.apply( ele, [ i, ele ] );
-
- if( ret === false ){ break; } // exit each early on return false
- }
- }
- return this;
- },
-
- forEach: function(fn, thisArg){
- if( is.fn(fn) ){
-
- for(var i = 0; i < this.length; i++){
- var ele = this[i];
- var ret = thisArg ? fn.apply( thisArg, [ ele, i, this ] ) : fn( ele, i, this );
-
- if( ret === false ){ break; } // exit each early on return false
- }
- }
-
- return this;
- },
-
- toArray: function(){
- var array = [];
-
- for(var i = 0; i < this.length; i++){
- array.push( this[i] );
- }
-
- return array;
- },
-
- slice: function(start, end){
- var array = [];
- var thisSize = this.length;
-
- if( end == null ){
- end = thisSize;
- }
-
- if( start == null ){
- start = 0;
- }
-
- if( start < 0 ){
- start = thisSize + start;
- }
-
- if( end < 0 ){
- end = thisSize + end;
- }
-
- for(var i = start; i >= 0 && i < end && i < thisSize; i++){
- array.push( this[i] );
- }
-
- return this.spawn(array);
- },
-
- size: function(){
- return this.length;
- },
-
- eq: function(i){
- return this[i] || this.spawn();
- },
-
- first: function(){
- return this[0] || this.spawn();
- },
-
- last: function(){
- return this[ this.length - 1 ] || this.spawn();
- },
-
- empty: function(){
- return this.length === 0;
- },
-
- nonempty: function(){
- return !this.empty();
- },
-
- sort: function( sortFn ){
- if( !is.fn( sortFn ) ){
- return this;
- }
-
- var sorted = this.toArray().sort( sortFn );
-
- return this.spawn(sorted);
- },
-
- sortByZIndex: function(){
- return this.sort( zIndexSort );
- },
-
- zDepth: function(){
- var ele = this[0];
- if( !ele ){ return undefined; }
-
- // var cy = ele.cy();
- var _p = ele._private;
- var group = _p.group;
-
- if( group === 'nodes' ){
- var depth = _p.data.parent ? ele.parents().size() : 0;
-
- if( !ele.isParent() ){
- return Number.MAX_VALUE; // childless nodes always on top
- }
-
- return depth;
- } else {
- var src = _p.source;
- var tgt = _p.target;
- var srcDepth = src.zDepth();
- var tgtDepth = tgt.zDepth();
-
- return Math.max( srcDepth, tgtDepth, 0 ); // depth of deepest parent
- }
- }
-});
-
-module.exports = elesfn;
-
-},{"../is":77,"./zsort":29}],25:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var util = _dereq_('../util');
-
-var elesfn = ({
-
- // using standard layout options, apply position function (w/ or w/o animation)
- layoutPositions: function( layout, options, fn ){
- var nodes = this.nodes();
- var cy = this.cy();
-
- layout.trigger({ type: 'layoutstart', layout: layout });
-
- layout.animations = [];
-
- if( options.animate ){
- for( var i = 0; i < nodes.length; i++ ){
- var node = nodes[i];
- var lastNode = i === nodes.length - 1;
-
- var newPos = fn.call( node, i, node );
- var pos = node.position();
-
- if( !is.number(pos.x) || !is.number(pos.y) ){
- node.silentPosition({ x: 0, y: 0 });
- }
-
- var ani = node.animation({
- position: newPos,
- duration: options.animationDuration,
- easing: options.animationEasing,
- step: !lastNode ? undefined : function(){
- if( options.fit ){
- cy.fit( options.eles, options.padding );
- }
- },
- complete: !lastNode ? undefined : function(){
- if( options.zoom != null ){
- cy.zoom( options.zoom );
- }
-
- if( options.pan ){
- cy.pan( options.pan );
- }
-
- if( options.fit ){
- cy.fit( options.eles, options.padding );
- }
-
- layout.one('layoutstop', options.stop);
- layout.trigger({ type: 'layoutstop', layout: layout });
- }
- });
-
- layout.animations.push( ani );
-
- ani.play();
- }
-
- layout.one('layoutready', options.ready);
- layout.trigger({ type: 'layoutready', layout: layout });
- } else {
- nodes.positions( fn );
-
- if( options.fit ){
- cy.fit( options.eles, options.padding );
- }
-
- if( options.zoom != null ){
- cy.zoom( options.zoom );
- }
-
- if( options.pan ){
- cy.pan( options.pan );
- }
-
- layout.one('layoutready', options.ready);
- layout.trigger({ type: 'layoutready', layout: layout });
-
- layout.one('layoutstop', options.stop);
- layout.trigger({ type: 'layoutstop', layout: layout });
- }
-
- return this; // chaining
- },
-
- layout: function( options ){
- var cy = this.cy();
-
- cy.layout( util.extend({}, options, {
- eles: this
- }) );
-
- return this;
- },
-
- makeLayout: function( options ){
- var cy = this.cy();
-
- return cy.makeLayout( util.extend({}, options, {
- eles: this
- }) );
- }
-
-});
-
-// aliases:
-elesfn.createLayout = elesfn.makeLayout;
-
-module.exports = elesfn;
-
-},{"../is":77,"../util":94}],26:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-
-var elesfn = ({
-
- // fully updates (recalculates) the style for the elements
- updateStyle: function( notifyRenderer ){
- var cy = this._private.cy;
-
- if( !cy.styleEnabled() ){ return this; }
-
- if( cy._private.batchingStyle ){
- var bEles = cy._private.batchStyleEles;
-
- bEles.merge( this );
-
- return this; // chaining and exit early when batching
- }
-
- var style = cy.style();
- notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
-
- style.apply( this );
-
- var updatedCompounds = this.updateCompoundBounds();
- var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this;
-
- if( notifyRenderer ){
- toNotify.rtrigger('style'); // let renderer know we changed style
- } else {
- toNotify.trigger('style'); // just fire the event
- }
- return this; // chaining
- },
-
- // just update the mappers in the elements' styles; cheaper than eles.updateStyle()
- updateMappers: function( notifyRenderer ){
- var cy = this._private.cy;
- var style = cy.style();
- notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
-
- if( !cy.styleEnabled() ){ return this; }
-
- style.updateMappers( this );
-
- var updatedCompounds = this.updateCompoundBounds();
- var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this;
-
- if( notifyRenderer ){
- toNotify.rtrigger('style'); // let renderer know we changed style
- } else {
- toNotify.trigger('style'); // just fire the event
- }
- return this; // chaining
- },
-
- // get the specified css property as a rendered value (i.e. on-screen value)
- // or get the whole rendered style if no property specified (NB doesn't allow setting)
- renderedCss: function( property ){
- var cy = this.cy();
- if( !cy.styleEnabled() ){ return this; }
-
- var ele = this[0];
-
- if( ele ){
- var renstyle = ele.cy().style().getRenderedStyle( ele );
-
- if( property === undefined ){
- return renstyle;
- } else {
- return renstyle[ property ];
- }
- }
- },
-
- // read the calculated css style of the element or override the style (via a bypass)
- css: function( name, value ){
- var cy = this.cy();
-
- if( !cy.styleEnabled() ){ return this; }
-
- var updateTransitions = false;
- var style = cy.style();
-
- if( is.plainObject(name) ){ // then extend the bypass
- var props = name;
- style.applyBypass( this, props, updateTransitions );
-
- var updatedCompounds = this.updateCompoundBounds();
- var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this;
- toNotify.rtrigger('style'); // let the renderer know we've updated style
-
- } else if( is.string(name) ){
-
- if( value === undefined ){ // then get the property from the style
- var ele = this[0];
-
- if( ele ){
- return style.getStylePropertyValue( ele, name );
- } else { // empty collection => can't get any value
- return;
- }
-
- } else { // then set the bypass with the property value
- style.applyBypass( this, name, value, updateTransitions );
-
- var updatedCompounds = this.updateCompoundBounds();
- var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this;
- toNotify.rtrigger('style'); // let the renderer know we've updated style
- }
-
- } else if( name === undefined ){
- var ele = this[0];
-
- if( ele ){
- return style.getRawStyle( ele );
- } else { // empty collection => can't get any value
- return;
- }
- }
-
- return this; // chaining
- },
-
- removeCss: function( names ){
- var cy = this.cy();
-
- if( !cy.styleEnabled() ){ return this; }
-
- var updateTransitions = false;
- var style = cy.style();
- var eles = this;
-
- if( names === undefined ){
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
-
- style.removeAllBypasses( ele, updateTransitions );
- }
- } else {
- names = names.split(/\s+/);
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
-
- style.removeBypasses( ele, names, updateTransitions );
- }
- }
-
- var updatedCompounds = this.updateCompoundBounds();
- var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this;
- toNotify.rtrigger('style'); // let the renderer know we've updated style
-
- return this; // chaining
- },
-
- show: function(){
- this.css('display', 'element');
- return this; // chaining
- },
-
- hide: function(){
- this.css('display', 'none');
- return this; // chaining
- },
-
- visible: function(){
- var cy = this.cy();
- if( !cy.styleEnabled() ){ return true; }
-
- var ele = this[0];
- var hasCompoundNodes = cy.hasCompoundNodes();
-
- if( ele ){
- var style = ele._private.style;
-
- if(
- style['visibility'].value !== 'visible'
- || style['display'].value !== 'element'
- ){
- return false;
- }
-
- if( ele._private.group === 'nodes' ){
- if( !hasCompoundNodes ){ return true; }
-
- var parents = ele._private.data.parent ? ele.parents() : null;
-
- if( parents ){
- for( var i = 0; i < parents.length; i++ ){
- var parent = parents[i];
- var pStyle = parent._private.style;
- var pVis = pStyle['visibility'].value;
- var pDis = pStyle['display'].value;
-
- if( pVis !== 'visible' || pDis !== 'element' ){
- return false;
- }
- }
- }
-
- return true;
- } else {
- var src = ele._private.source;
- var tgt = ele._private.target;
-
- return src.visible() && tgt.visible();
- }
-
- }
- },
-
- hidden: function(){
- var ele = this[0];
-
- if( ele ){
- return !ele.visible();
- }
- },
-
- effectiveOpacity: function(){
- var cy = this.cy();
- if( !cy.styleEnabled() ){ return 1; }
-
- var hasCompoundNodes = cy.hasCompoundNodes();
- var ele = this[0];
-
- if( ele ){
- var _p = ele._private;
- var parentOpacity = _p.style.opacity.value;
-
- if( !hasCompoundNodes ){ return parentOpacity; }
-
- var parents = !_p.data.parent ? null : ele.parents();
-
- if( parents ){
- for( var i = 0; i < parents.length; i++ ){
- var parent = parents[i];
- var opacity = parent._private.style.opacity.value;
-
- parentOpacity = opacity * parentOpacity;
- }
- }
-
- return parentOpacity;
- }
- },
-
- transparent: function(){
- var cy = this.cy();
- if( !cy.styleEnabled() ){ return false; }
-
- var ele = this[0];
- var hasCompoundNodes = ele.cy().hasCompoundNodes();
-
- if( ele ){
- if( !hasCompoundNodes ){
- return ele._private.style.opacity.value === 0;
- } else {
- return ele.effectiveOpacity() === 0;
- }
- }
- },
-
- isFullAutoParent: function(){
- var cy = this.cy();
- if( !cy.styleEnabled() ){ return false; }
-
- var ele = this[0];
-
- if( ele ){
- var autoW = ele._private.style['width'].value === 'auto';
- var autoH = ele._private.style['height'].value === 'auto';
-
- return ele.isParent() && autoW && autoH;
- }
- },
-
- backgrounding: function(){
- var cy = this.cy();
- if( !cy.styleEnabled() ){ return false; }
-
- var ele = this[0];
-
- return ele._private.backgrounding ? true : false;
- }
-
-});
-
-
-elesfn.bypass = elesfn.style = elesfn.css;
-elesfn.renderedStyle = elesfn.renderedCss;
-elesfn.removeBypass = elesfn.removeStyle = elesfn.removeCss;
-
-module.exports = elesfn;
-
-},{"../is":77}],27:[function(_dereq_,module,exports){
-'use strict';
-
-var elesfn = {};
-
-function defineSwitchFunction(params){
- return function(){
- var args = arguments;
- var changedEles = [];
-
- // e.g. cy.nodes().select( data, handler )
- if( args.length === 2 ){
- var data = args[0];
- var handler = args[1];
- this.bind( params.event, data, handler );
- }
-
- // e.g. cy.nodes().select( handler )
- else if( args.length === 1 ){
- var handler = args[0];
- this.bind( params.event, handler );
- }
-
- // e.g. cy.nodes().select()
- else if( args.length === 0 ){
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
- var able = !params.ableField || ele._private[params.ableField];
- var changed = ele._private[params.field] != params.value;
-
- if( params.overrideAble ){
- var overrideAble = params.overrideAble(ele);
-
- if( overrideAble !== undefined ){
- able = overrideAble;
-
- if( !overrideAble ){ return this; } // to save cycles assume not able for all on override
- }
- }
-
- if( able ){
- ele._private[params.field] = params.value;
-
- if( changed ){
- changedEles.push( ele );
- }
- }
- }
-
- var changedColl = this.spawn( changedEles );
- changedColl.updateStyle(); // change of state => possible change of style
- changedColl.trigger( params.event );
- }
-
- return this;
- };
-}
-
-function defineSwitchSet( params ){
- elesfn[ params.field ] = function(){
- var ele = this[0];
-
- if( ele ){
- if( params.overrideField ){
- var val = params.overrideField(ele);
-
- if( val !== undefined ){
- return val;
- }
- }
-
- return ele._private[ params.field ];
- }
- };
-
- elesfn[ params.on ] = defineSwitchFunction({
- event: params.on,
- field: params.field,
- ableField: params.ableField,
- overrideAble: params.overrideAble,
- value: true
- });
-
- elesfn[ params.off ] = defineSwitchFunction({
- event: params.off,
- field: params.field,
- ableField: params.ableField,
- overrideAble: params.overrideAble,
- value: false
- });
-}
-
-defineSwitchSet({
- field: 'locked',
- overrideField: function(ele){
- return ele.cy().autolock() ? true : undefined;
- },
- on: 'lock',
- off: 'unlock'
-});
-
-defineSwitchSet({
- field: 'grabbable',
- overrideField: function(ele){
- return ele.cy().autoungrabify() ? false : undefined;
- },
- on: 'grabify',
- off: 'ungrabify'
-});
-
-defineSwitchSet({
- field: 'selected',
- ableField: 'selectable',
- overrideAble: function(ele){
- return ele.cy().autounselectify() ? false : undefined;
- },
- on: 'select',
- off: 'unselect'
-});
-
-defineSwitchSet({
- field: 'selectable',
- overrideField: function(ele){
- return ele.cy().autounselectify() ? false : undefined;
- },
- on: 'selectify',
- off: 'unselectify'
-});
-
-elesfn.deselect = elesfn.unselect;
-
-elesfn.grabbed = function(){
- var ele = this[0];
- if( ele ){
- return ele._private.grabbed;
- }
-};
-
-defineSwitchSet({
- field: 'active',
- on: 'activate',
- off: 'unactivate'
-});
-
-elesfn.inactive = function(){
- var ele = this[0];
- if( ele ){
- return !ele._private.active;
- }
-};
-
-module.exports = elesfn;
-
-},{}],28:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-var is = _dereq_('../is');
-
-var elesfn = {};
-
-util.extend(elesfn, {
- // get the root nodes in the DAG
- roots: function( selector ){
- var eles = this;
- var roots = [];
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- if( !ele.isNode() ){
- continue;
- }
-
- var hasEdgesPointingIn = ele.connectedEdges(function(){
- return this.data('target') === ele.id() && this.data('source') !== ele.id();
- }).length > 0;
-
- if( !hasEdgesPointingIn ){
- roots.push( ele );
- }
- }
-
- return this.spawn( roots, { unique: true } ).filter( selector );
- },
-
- // get the leaf nodes in the DAG
- leaves: function( selector ){
- var eles = this;
- var leaves = [];
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- if( !ele.isNode() ){
- continue;
- }
-
- var hasEdgesPointingOut = ele.connectedEdges(function(){
- return this.data('source') === ele.id() && this.data('target') !== ele.id();
- }).length > 0;
-
- if( !hasEdgesPointingOut ){
- leaves.push( ele );
- }
- }
-
- return this.spawn( leaves, { unique: true } ).filter( selector );
- },
-
- // normally called children in graph theory
- // these nodes =edges=> outgoing nodes
- outgoers: function( selector ){
- var eles = this;
- var oEles = [];
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var eleId = ele.id();
-
- if( !ele.isNode() ){ continue; }
-
- var edges = ele._private.edges;
- for( var j = 0; j < edges.length; j++ ){
- var edge = edges[j];
- var srcId = edge._private.data.source;
- var tgtId = edge._private.data.target;
-
- if( srcId === eleId && tgtId !== eleId ){
- oEles.push( edge );
- oEles.push( edge.target()[0] );
- }
- }
- }
-
- return this.spawn( oEles, { unique: true } ).filter( selector );
- },
-
- // aka DAG descendants
- successors: function( selector ){
- var eles = this;
- var sEles = [];
- var sElesIds = {};
-
- for(;;){
- var outgoers = eles.outgoers();
-
- if( outgoers.length === 0 ){ break; } // done if no outgoers left
-
- var newOutgoers = false;
- for( var i = 0; i < outgoers.length; i++ ){
- var outgoer = outgoers[i];
- var outgoerId = outgoer.id();
-
- if( !sElesIds[ outgoerId ] ){
- sElesIds[ outgoerId ] = true;
- sEles.push( outgoer );
- newOutgoers = true;
- }
- }
-
- if( !newOutgoers ){ break; } // done if touched all outgoers already
-
- eles = outgoers;
- }
-
- return this.spawn( sEles, { unique: true } ).filter( selector );
- },
-
- // normally called parents in graph theory
- // these nodes <=edges= incoming nodes
- incomers: function( selector ){
- var eles = this;
- var oEles = [];
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var eleId = ele.id();
-
- if( !ele.isNode() ){ continue; }
-
- var edges = ele._private.edges;
- for( var j = 0; j < edges.length; j++ ){
- var edge = edges[j];
- var srcId = edge._private.data.source;
- var tgtId = edge._private.data.target;
-
- if( tgtId === eleId && srcId !== eleId ){
- oEles.push( edge );
- oEles.push( edge.source()[0] );
- }
- }
- }
-
- return this.spawn( oEles, { unique: true } ).filter( selector );
- },
-
- // aka DAG ancestors
- predecessors: function( selector ){
- var eles = this;
- var pEles = [];
- var pElesIds = {};
-
- for(;;){
- var incomers = eles.incomers();
-
- if( incomers.length === 0 ){ break; } // done if no incomers left
-
- var newIncomers = false;
- for( var i = 0; i < incomers.length; i++ ){
- var incomer = incomers[i];
- var incomerId = incomer.id();
-
- if( !pElesIds[ incomerId ] ){
- pElesIds[ incomerId ] = true;
- pEles.push( incomer );
- newIncomers = true;
- }
- }
-
- if( !newIncomers ){ break; } // done if touched all incomers already
-
- eles = incomers;
- }
-
- return this.spawn( pEles, { unique: true } ).filter( selector );
- }
-});
-
-
-// Neighbourhood functions
-//////////////////////////
-
-util.extend(elesfn, {
- neighborhood: function(selector){
- var elements = [];
- var nodes = this.nodes();
-
- for( var i = 0; i < nodes.length; i++ ){ // for all nodes
- var node = nodes[i];
- var connectedEdges = node.connectedEdges();
-
- // for each connected edge, add the edge and the other node
- for( var j = 0; j < connectedEdges.length; j++ ){
- var edge = connectedEdges[j];
- var src = edge._private.source;
- var tgt = edge._private.target;
- var otherNode = node === src ? tgt : src;
-
- // need check in case of loop
- if( otherNode.length > 0 ){
- elements.push( otherNode[0] ); // add node 1 hop away
- }
-
- // add connected edge
- elements.push( edge[0] );
- }
-
- }
-
- return ( this.spawn( elements, { unique: true } ) ).filter( selector );
- },
-
- closedNeighborhood: function(selector){
- return this.neighborhood().add( this ).filter( selector );
- },
-
- openNeighborhood: function(selector){
- return this.neighborhood( selector );
- }
-});
-
-// aliases
-elesfn.neighbourhood = elesfn.neighborhood;
-elesfn.closedNeighbourhood = elesfn.closedNeighborhood;
-elesfn.openNeighbourhood = elesfn.openNeighborhood;
-
-// Edge functions
-/////////////////
-
-util.extend(elesfn, {
- source: function( selector ){
- var ele = this[0];
- var src;
-
- if( ele ){
- src = ele._private.source;
- }
-
- return src && selector ? src.filter( selector ) : src;
- },
-
- target: function( selector ){
- var ele = this[0];
- var tgt;
-
- if( ele ){
- tgt = ele._private.target;
- }
-
- return tgt && selector ? tgt.filter( selector ) : tgt;
- },
-
- sources: defineSourceFunction({
- attr: 'source'
- }),
-
- targets: defineSourceFunction({
- attr: 'target'
- })
-});
-
-function defineSourceFunction( params ){
- return function( selector ){
- var sources = [];
-
- for( var i = 0; i < this.length; i++ ){
- var ele = this[i];
- var src = ele._private[ params.attr ];
-
- if( src ){
- sources.push( src );
- }
- }
-
- return this.spawn( sources, { unique: true } ).filter( selector );
- };
-}
-
-util.extend(elesfn, {
- edgesWith: defineEdgesWithFunction(),
-
- edgesTo: defineEdgesWithFunction({
- thisIs: 'source'
- })
-});
-
-function defineEdgesWithFunction( params ){
-
- return function edgesWithImpl( otherNodes ){
- var elements = [];
- var cy = this._private.cy;
- var p = params || {};
-
- // get elements if a selector is specified
- if( is.string(otherNodes) ){
- otherNodes = cy.$( otherNodes );
- }
-
- var thisIds = this._private.ids;
- var otherIds = otherNodes._private.ids;
-
- for( var h = 0; h < otherNodes.length; h++ ){
- var edges = otherNodes[h]._private.edges;
-
- for( var i = 0; i < edges.length; i++ ){
- var edge = edges[i];
- var edgeData = edge._private.data;
- var thisToOther = thisIds[ edgeData.source ] && otherIds[ edgeData.target ];
- var otherToThis = otherIds[ edgeData.source ] && thisIds[ edgeData.target ];
- var edgeConnectsThisAndOther = thisToOther || otherToThis;
-
- if( !edgeConnectsThisAndOther ){ continue; }
-
- if( p.thisIs ){
- if( p.thisIs === 'source' && !thisToOther ){ continue; }
-
- if( p.thisIs === 'target' && !otherToThis ){ continue; }
- }
-
- elements.push( edge );
- }
- }
-
- return this.spawn( elements, { unique: true } );
- };
-}
-
-util.extend(elesfn, {
- connectedEdges: function( selector ){
- var retEles = [];
-
- var eles = this;
- for( var i = 0; i < eles.length; i++ ){
- var node = eles[i];
- if( !node.isNode() ){ continue; }
-
- var edges = node._private.edges;
-
- for( var j = 0; j < edges.length; j++ ){
- var edge = edges[j];
- retEles.push( edge );
- }
- }
-
- return this.spawn( retEles, { unique: true } ).filter( selector );
- },
-
- connectedNodes: function( selector ){
- var retEles = [];
-
- var eles = this;
- for( var i = 0; i < eles.length; i++ ){
- var edge = eles[i];
- if( !edge.isEdge() ){ continue; }
-
- retEles.push( edge.source()[0] );
- retEles.push( edge.target()[0] );
- }
-
- return this.spawn( retEles, { unique: true } ).filter( selector );
- },
-
- parallelEdges: defineParallelEdgesFunction(),
-
- codirectedEdges: defineParallelEdgesFunction({
- codirected: true
- })
-});
-
-function defineParallelEdgesFunction(params){
- var defaults = {
- codirected: false
- };
- params = util.extend({}, defaults, params);
-
- return function( selector ){
- var elements = [];
- var edges = this.edges();
- var p = params;
-
- // look at all the edges in the collection
- for( var i = 0; i < edges.length; i++ ){
- var edge1 = edges[i];
- var src1 = edge1.source()[0];
- var srcid1 = src1.id();
- var tgt1 = edge1.target()[0];
- var tgtid1 = tgt1.id();
- var srcEdges1 = src1._private.edges;
-
- // look at edges connected to the src node of this edge
- for( var j = 0; j < srcEdges1.length; j++ ){
- var edge2 = srcEdges1[j];
- var edge2data = edge2._private.data;
- var tgtid2 = edge2data.target;
- var srcid2 = edge2data.source;
-
- var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
- var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
-
- if( (p.codirected && codirected) || (!p.codirected && (codirected || oppdirected)) ){
- elements.push( edge2 );
- }
- }
- }
-
- return this.spawn( elements, { unique: true } ).filter( selector );
- };
-
-}
-
-// Misc functions
-/////////////////
-
-util.extend(elesfn, {
- components: function(){
- var cy = this.cy();
- var visited = cy.collection();
- var unvisited = this.nodes();
- var components = [];
-
- var visitInComponent = function( node, component ){
- visited.merge( node );
- unvisited.unmerge( node );
- component.merge( node );
- };
-
- do {
- var component = cy.collection();
- components.push( component );
-
- var root = unvisited[0];
- visitInComponent( root, component );
-
- this.bfs({
- directed: false,
- roots: root,
- visit: function( i, depth, v, e, u ){
- visitInComponent( v, component );
- }
- });
-
- } while( unvisited.length > 0 );
-
- return components.map(function( component ){
- return component.closedNeighborhood(); // add the edges
- });
- }
-});
-
-module.exports = elesfn;
-
-},{"../is":77,"../util":94}],29:[function(_dereq_,module,exports){
-'use strict';
-
-var zIndexSort = function( a, b ){
- var cy = a.cy();
- var a_p = a._private;
- var b_p = b._private;
- var zDiff = a_p.style['z-index'].value - b_p.style['z-index'].value;
- var depthA = 0;
- var depthB = 0;
- var hasCompoundNodes = cy.hasCompoundNodes();
- var aIsNode = a_p.group === 'nodes';
- var aIsEdge = a_p.group === 'edges';
- var bIsNode = b_p.group === 'nodes';
- var bIsEdge = b_p.group === 'edges';
-
- // no need to calculate element depth if there is no compound node
- if( hasCompoundNodes ){
- depthA = a.zDepth();
- depthB = b.zDepth();
- }
-
- var depthDiff = depthA - depthB;
- var sameDepth = depthDiff === 0;
-
- if( sameDepth ){
-
- if( aIsNode && bIsEdge ){
- return 1; // 'a' is a node, it should be drawn later
-
- } else if( aIsEdge && bIsNode ){
- return -1; // 'a' is an edge, it should be drawn first
-
- } else { // both nodes or both edges
- if( zDiff === 0 ){ // same z-index => compare indices in the core (order added to graph w/ last on top)
- return a_p.index - b_p.index;
- } else {
- return zDiff;
- }
- }
-
- // elements on different level
- } else {
- return depthDiff; // deeper element should be drawn later
- }
-
-};
-
-module.exports = zIndexSort;
-
-},{}],30:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var util = _dereq_('../util');
-var Collection = _dereq_('../collection');
-var Element = _dereq_('../collection/element');
-var window = _dereq_('../window');
-var document = window ? window.document : null;
-var NullRenderer = _dereq_('../extensions/renderer/null');
-
-var corefn = {
- add: function(opts){
-
- var elements;
- var cy = this;
-
- // add the elements
- if( is.elementOrCollection(opts) ){
- var eles = opts;
-
- if( eles._private.cy === cy ){ // same instance => just restore
- elements = eles.restore();
-
- } else { // otherwise, copy from json
- var jsons = [];
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- jsons.push( ele.json() );
- }
-
- elements = new Collection( cy, jsons );
- }
- }
-
- // specify an array of options
- else if( is.array(opts) ){
- var jsons = opts;
-
- elements = new Collection(cy, jsons);
- }
-
- // specify via opts.nodes and opts.edges
- else if( is.plainObject(opts) && (is.array(opts.nodes) || is.array(opts.edges)) ){
- var elesByGroup = opts;
- var jsons = [];
-
- var grs = ['nodes', 'edges'];
- for( var i = 0, il = grs.length; i < il; i++ ){
- var group = grs[i];
- var elesArray = elesByGroup[group];
-
- if( is.array(elesArray) ){
-
- for( var j = 0, jl = elesArray.length; j < jl; j++ ){
- var json = util.extend( { group: group }, elesArray[j] );
-
- jsons.push( json );
- }
- }
- }
-
- elements = new Collection(cy, jsons);
- }
-
- // specify options for one element
- else {
- var json = opts;
- elements = (new Element( cy, json )).collection();
- }
-
- return elements;
- },
-
- remove: function(collection){
- if( is.elementOrCollection(collection) ){
- collection = collection;
- } else if( is.string(collection) ){
- var selector = collection;
- collection = this.$( selector );
- }
-
- return collection.remove();
- },
-
- load: function(elements, onload, ondone){
- var cy = this;
-
- cy.notifications(false);
-
- // remove old elements
- var oldEles = cy.elements();
- if( oldEles.length > 0 ){
- oldEles.remove();
- }
-
- if( elements != null ){
- if( is.plainObject(elements) || is.array(elements) ){
- cy.add( elements );
- }
- }
-
- cy.one('layoutready', function(e){
- cy.notifications(true);
- cy.trigger(e); // we missed this event by turning notifications off, so pass it on
-
- cy.notify({
- type: 'load',
- collection: cy.elements()
- });
-
- cy.one('load', onload);
- cy.trigger('load');
- }).one('layoutstop', function(){
- cy.one('done', ondone);
- cy.trigger('done');
- });
-
- var layoutOpts = util.extend({}, cy._private.options.layout);
- layoutOpts.eles = cy.$();
-
- cy.layout( layoutOpts );
-
- return this;
- }
-};
-
-module.exports = corefn;
-
-},{"../collection":23,"../collection/element":19,"../extensions/renderer/null":73,"../is":77,"../util":94,"../window":100}],31:[function(_dereq_,module,exports){
-'use strict';
-
-var define = _dereq_('../define');
-var util = _dereq_('../util');
-var is = _dereq_('../is');
-
-var corefn = ({
-
- // pull in animation functions
- animate: define.animate(),
- animation: define.animation(),
- animated: define.animated(),
- clearQueue: define.clearQueue(),
- delay: define.delay(),
- delayAnimation: define.delayAnimation(),
- stop: define.stop(),
-
- addToAnimationPool: function( eles ){
- var cy = this;
-
- if( !cy.styleEnabled() ){ return; } // save cycles when no style used
-
- cy._private.aniEles.merge( eles );
- },
-
- stopAnimationLoop: function(){
- this._private.animationsRunning = false;
- },
-
- startAnimationLoop: function(){
- var cy = this;
-
- cy._private.animationsRunning = true;
-
- if( !cy.styleEnabled() ){ return; } // save cycles when no style used
-
- // NB the animation loop will exec in headless environments if style enabled
- // and explicit cy.destroy() is necessary to stop the loop
-
- function globalAnimationStep(){
- if( !cy._private.animationsRunning ){ return; }
-
- util.requestAnimationFrame(function(now){
- handleElements(now);
- globalAnimationStep();
- });
- }
-
- globalAnimationStep(); // first call
-
- function handleElements( now ){
- var eles = cy._private.aniEles;
- var doneEles = [];
-
- function handleElement( ele, isCore ){
- var _p = ele._private;
- var current = _p.animation.current;
- var queue = _p.animation.queue;
- var ranAnis = false;
-
- // if nothing currently animating, get something from the queue
- if( current.length === 0 ){
- var next = queue.shift();
-
- if( next ){
- current.push( next );
- }
- }
-
- var callbacks = function( callbacks ){
- for( var j = callbacks.length - 1; j >= 0; j-- ){
- var cb = callbacks[j];
-
- cb();
- }
-
- callbacks.splice( 0, callbacks.length );
- };
-
- // step and remove if done
- for( var i = current.length - 1; i >= 0; i-- ){
- var ani = current[i];
- var ani_p = ani._private;
-
- if( ani_p.stopped ){
- current.splice( i, 1 );
-
- ani_p.hooked = false;
- ani_p.playing = false;
- ani_p.started = false;
-
- callbacks( ani_p.frames );
-
- continue;
- }
-
- if( !ani_p.playing && !ani_p.applying ){ continue; }
-
- // an apply() while playing shouldn't do anything
- if( ani_p.playing && ani_p.applying ){
- ani_p.applying = false;
- }
-
- if( !ani_p.started ){
- startAnimation( ele, ani, now );
- }
-
- step( ele, ani, now, isCore );
-
- if( ani_p.applying ){
- ani_p.applying = false;
- }
-
- callbacks( ani_p.frames );
-
- if( ani.completed() ){
- current.splice(i, 1);
-
- ani_p.hooked = false;
- ani_p.playing = false;
- ani_p.started = false;
-
- callbacks( ani_p.completes );
- }
-
- ranAnis = true;
- }
-
- if( !isCore && current.length === 0 && queue.length === 0 ){
- doneEles.push( ele );
- }
-
- return ranAnis;
- } // handleElement
-
- // handle all eles
- var ranEleAni = false;
- for( var e = 0; e < eles.length; e++ ){
- var ele = eles[e];
- var handledThisEle = handleElement( ele );
-
- ranEleAni = ranEleAni || handledThisEle;
- } // each element
-
- var ranCoreAni = handleElement( cy, true );
-
- // notify renderer
- if( ranEleAni || ranCoreAni ){
- var toNotify;
-
- if( eles.length > 0 ){
- var updatedEles = eles.updateCompoundBounds();
- toNotify = updatedEles.length > 0 ? eles.add( updatedEles ) : eles;
- }
-
- cy.notify({
- type: 'draw',
- collection: toNotify
- });
- }
-
- // remove elements from list of currently animating if its queues are empty
- eles.unmerge( doneEles );
-
- } // handleElements
-
- function startAnimation( self, ani, now ){
- var isCore = is.core( self );
- var isEles = !isCore;
- var ele = self;
- var style = cy._private.style;
- var ani_p = ani._private;
-
- if( isEles ){
- var pos = ele._private.position;
-
- ani_p.startPosition = ani_p.startPosition || {
- x: pos.x,
- y: pos.y
- };
-
- ani_p.startStyle = ani_p.startStyle || style.getValueStyle( ele );
- }
-
- if( isCore ){
- var pan = cy._private.pan;
-
- ani_p.startPan = ani_p.startPan || {
- x: pan.x,
- y: pan.y
- };
-
- ani_p.startZoom = ani_p.startZoom != null ? ani_p.startZoom : cy._private.zoom;
- }
-
- ani_p.started = true;
- ani_p.startTime = now - ani_p.progress * ani_p.duration;
- }
-
- function step( self, ani, now, isCore ){
- var style = cy._private.style;
- var isEles = !isCore;
- var _p = self._private;
- var ani_p = ani._private;
- var pEasing = ani_p.easing;
- var startTime = ani_p.startTime;
-
- if( !ani_p.easingImpl ){
-
- if( pEasing == null ){ // use default
- ani_p.easingImpl = easings['linear'];
-
- } else { // then define w/ name
- var easingVals;
-
- if( is.string( pEasing ) ){
- var easingProp = style.parse('transition-timing-function', pEasing);
-
- easingVals = easingProp.value;
-
- } else { // then assume preparsed array
- easingVals = pEasing;
- }
-
- var name, args;
-
- if( is.string( easingVals ) ){
- name = easingVals;
- args = [];
- } else {
- name = easingVals[1];
- args = easingVals.slice(2).map(function(n){ return +n; });
- }
-
- if( args.length > 0 ){ // create with args
- if( name === 'spring' ){
- args.push( ani_p.duration ); // need duration to generate spring
- }
-
- ani_p.easingImpl = easings[ name ].apply( null, args );
- } else { // static impl by name
- ani_p.easingImpl = easings[ name ];
- }
- }
-
- }
-
- var easing = ani_p.easingImpl;
- var percent;
-
- if( ani_p.duration === 0 ){
- percent = 1;
- } else {
- percent = (now - startTime) / ani_p.duration;
- }
-
- if( ani_p.applying ){
- percent = ani_p.progress;
- }
-
- if( percent < 0 ){
- percent = 0;
- } else if( percent > 1 ){
- percent = 1;
- }
-
- if( ani_p.delay == null ){ // then update
-
- var startPos = ani_p.startPosition;
- var endPos = ani_p.position;
- var pos = _p.position;
- if( endPos && isEles ){
- if( valid( startPos.x, endPos.x ) ){
- pos.x = ease( startPos.x, endPos.x, percent, easing );
- }
-
- if( valid( startPos.y, endPos.y ) ){
- pos.y = ease( startPos.y, endPos.y, percent, easing );
- }
- }
-
- var startPan = ani_p.startPan;
- var endPan = ani_p.pan;
- var pan = _p.pan;
- var animatingPan = endPan != null && isCore;
- if( animatingPan ){
- if( valid( startPan.x, endPan.x ) ){
- pan.x = ease( startPan.x, endPan.x, percent, easing );
- }
-
- if( valid( startPan.y, endPan.y ) ){
- pan.y = ease( startPan.y, endPan.y, percent, easing );
- }
-
- self.trigger('pan');
- }
-
- var startZoom = ani_p.startZoom;
- var endZoom = ani_p.zoom;
- var animatingZoom = endZoom != null && isCore;
- if( animatingZoom ){
- if( valid( startZoom, endZoom ) ){
- _p.zoom = ease( startZoom, endZoom, percent, easing );
- }
-
- self.trigger('zoom');
- }
-
- if( animatingPan || animatingZoom ){
- self.trigger('viewport');
- }
-
- var props = ani_p.style;
- if( props && isEles ){
-
- for( var i = 0; i < props.length; i++ ){
- var prop = props[i];
- var name = prop.name;
- var end = prop;
-
- var start = ani_p.startStyle[ name ];
- var easedVal = ease( start, end, percent, easing );
-
- style.overrideBypass( self, name, easedVal );
- } // for props
-
- } // if
-
- }
-
- if( is.fn(ani_p.step) ){
- ani_p.step.apply( self, [ now ] );
- }
-
- ani_p.progress = percent;
-
- return percent;
- }
-
- function valid(start, end){
- if( start == null || end == null ){
- return false;
- }
-
- if( is.number(start) && is.number(end) ){
- return true;
- } else if( (start) && (end) ){
- return true;
- }
-
- return false;
- }
-
- // assumes p0 = 0, p3 = 1
- function evalCubicBezier( p1, p2, t ){
- var one_t = 1 - t;
- var tsq = t*t;
-
- return ( 3 * one_t * one_t * t * p1 ) + ( 3 * one_t * tsq * p2 ) + tsq * t;
- }
-
- function cubicBezier( p1, p2 ){
- return function( start, end, percent ){
- return start + (end - start) * evalCubicBezier( p1, p2, percent );
- };
- }
-
- /* Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
- /* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass
- then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
- var generateSpringRK4 = (function () {
- function springAccelerationForState (state) {
- return (-state.tension * state.x) - (state.friction * state.v);
- }
-
- function springEvaluateStateWithDerivative (initialState, dt, derivative) {
- var state = {
- x: initialState.x + derivative.dx * dt,
- v: initialState.v + derivative.dv * dt,
- tension: initialState.tension,
- friction: initialState.friction
- };
-
- return { dx: state.v, dv: springAccelerationForState(state) };
- }
-
- function springIntegrateState (state, dt) {
- var a = {
- dx: state.v,
- dv: springAccelerationForState(state)
- },
- b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
- c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
- d = springEvaluateStateWithDerivative(state, dt, c),
- dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
- dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
-
- state.x = state.x + dxdt * dt;
- state.v = state.v + dvdt * dt;
-
- return state;
- }
-
- return function springRK4Factory (tension, friction, duration) {
-
- var initState = {
- x: -1,
- v: 0,
- tension: null,
- friction: null
- },
- path = [0],
- time_lapsed = 0,
- tolerance = 1 / 10000,
- DT = 16 / 1000,
- have_duration, dt, last_state;
-
- tension = parseFloat(tension) || 500;
- friction = parseFloat(friction) || 20;
- duration = duration || null;
-
- initState.tension = tension;
- initState.friction = friction;
-
- have_duration = duration !== null;
-
- /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
- if (have_duration) {
- /* Run the simulation without a duration. */
- time_lapsed = springRK4Factory(tension, friction);
- /* Compute the adjusted time delta. */
- dt = time_lapsed / duration * DT;
- } else {
- dt = DT;
- }
-
- while (true) {
- /* Next/step function .*/
- last_state = springIntegrateState(last_state || initState, dt);
- /* Store the position. */
- path.push(1 + last_state.x);
- time_lapsed += 16;
- /* If the change threshold is reached, break. */
- if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
- break;
- }
- }
-
- /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
- computed path and returns a snapshot of the position according to a given percentComplete. */
- return !have_duration ? time_lapsed : function(percentComplete) { return path[ (percentComplete * (path.length - 1)) | 0 ]; };
- };
- }());
-
- var easings = {
- 'linear': function( start, end, percent ){
- return start + (end - start) * percent;
- },
-
- // default easings
- 'ease': cubicBezier( 0.25, 0.1, 0.25, 1 ),
- 'ease-in': cubicBezier( 0.42, 0, 1, 1 ),
- 'ease-out': cubicBezier( 0, 0, 0.58, 1 ),
- 'ease-in-out': cubicBezier( 0.42, 0, 0.58, 1 ),
-
- // sine
- 'ease-in-sine': cubicBezier( 0.47, 0, 0.745, 0.715 ),
- 'ease-out-sine': cubicBezier( 0.39, 0.575, 0.565, 1 ),
- 'ease-in-out-sine': cubicBezier( 0.445, 0.05, 0.55, 0.95 ),
-
- // quad
- 'ease-in-quad': cubicBezier( 0.55, 0.085, 0.68, 0.53 ),
- 'ease-out-quad': cubicBezier( 0.25, 0.46, 0.45, 0.94 ),
- 'ease-in-out-quad': cubicBezier( 0.455, 0.03, 0.515, 0.955 ),
-
- // cubic
- 'ease-in-cubic': cubicBezier( 0.55, 0.055, 0.675, 0.19 ),
- 'ease-out-cubic': cubicBezier( 0.215, 0.61, 0.355, 1 ),
- 'ease-in-out-cubic': cubicBezier( 0.645, 0.045, 0.355, 1 ),
-
- // quart
- 'ease-in-quart': cubicBezier( 0.895, 0.03, 0.685, 0.22 ),
- 'ease-out-quart': cubicBezier( 0.165, 0.84, 0.44, 1 ),
- 'ease-in-out-quart': cubicBezier( 0.77, 0, 0.175, 1 ),
-
- // quint
- 'ease-in-quint': cubicBezier( 0.755, 0.05, 0.855, 0.06 ),
- 'ease-out-quint': cubicBezier( 0.23, 1, 0.32, 1 ),
- 'ease-in-out-quint': cubicBezier( 0.86, 0, 0.07, 1 ),
-
- // expo
- 'ease-in-expo': cubicBezier( 0.95, 0.05, 0.795, 0.035 ),
- 'ease-out-expo': cubicBezier( 0.19, 1, 0.22, 1 ),
- 'ease-in-out-expo': cubicBezier( 1, 0, 0, 1 ),
-
- // circ
- 'ease-in-circ': cubicBezier( 0.6, 0.04, 0.98, 0.335 ),
- 'ease-out-circ': cubicBezier( 0.075, 0.82, 0.165, 1 ),
- 'ease-in-out-circ': cubicBezier( 0.785, 0.135, 0.15, 0.86 ),
-
-
- // user param easings...
-
- 'spring': function( tension, friction, duration ){
- var spring = generateSpringRK4( tension, friction, duration );
-
- return function( start, end, percent ){
- return start + (end - start) * spring( percent );
- };
- },
-
- 'cubic-bezier': function( x1, y1, x2, y2 ){
- return cubicBezier( x1, y1, x2, y2 );
- }
- };
-
- function ease( startProp, endProp, percent, easingFn ){
- if( percent < 0 ){
- percent = 0;
- } else if( percent > 1 ){
- percent = 1;
- }
-
- var start, end;
-
- if( startProp.pfValue != null || startProp.value != null ){
- start = startProp.pfValue != null ? startProp.pfValue : startProp.value;
- } else {
- start = startProp;
- }
-
- if( endProp.pfValue != null || endProp.value != null ){
- end = endProp.pfValue != null ? endProp.pfValue : endProp.value;
- } else {
- end = endProp;
- }
-
- if( is.number(start) && is.number(end) ){
- return easingFn( start, end, percent );
-
- } else if( is.array(start) && is.array(end) ){
- var easedArr = [];
-
- for( var i = 0; i < end.length; i++ ){
- var si = start[i];
- var ei = end[i];
-
- if( si != null && ei != null ){
- var val = easingFn(si, ei, percent);
-
- if( startProp.roundValue ){ val = Math.round( val ); }
-
- easedArr.push( val );
- } else {
- easedArr.push( ei );
- }
- }
-
- return easedArr;
- }
-
- return undefined;
- }
-
- }
-
-});
-
-module.exports = corefn;
-
-},{"../define":41,"../is":77,"../util":94}],32:[function(_dereq_,module,exports){
-'use strict';
-
-var define = _dereq_('../define');
-
-var corefn = ({
- on: define.on(), // .on( events [, selector] [, data], handler)
- one: define.on({ unbindSelfOnTrigger: true }),
- once: define.on({ unbindAllBindersOnTrigger: true }),
- off: define.off(), // .off( events [, selector] [, handler] )
- trigger: define.trigger() // .trigger( events [, extraParams] )
-});
-
-define.eventAliasesOn( corefn );
-
-module.exports = corefn;
-
-},{"../define":41}],33:[function(_dereq_,module,exports){
-'use strict';
-
-var corefn = ({
-
- png: function( options ){
- var renderer = this._private.renderer;
- options = options || {};
-
- return renderer.png( options );
- },
-
- jpg: function( options ){
- var renderer = this._private.renderer;
- options = options || {};
-
- options.bg = options.bg || '#fff';
-
- return renderer.jpg( options );
- }
-
-});
-
-corefn.jpeg = corefn.jpg;
-
-module.exports = corefn;
-
-},{}],34:[function(_dereq_,module,exports){
-'use strict';
-
-var window = _dereq_('../window');
-var util = _dereq_('../util');
-var Collection = _dereq_('../collection');
-var is = _dereq_('../is');
-var Promise = _dereq_('../promise');
-var define = _dereq_('../define');
-
-var Core = function( opts ){
- if( !(this instanceof Core) ){
- return new Core(opts);
- }
- var cy = this;
-
- opts = util.extend({}, opts);
-
- var container = opts.container;
-
- // allow for passing a wrapped jquery object
- // e.g. cytoscape({ container: $('#cy') })
- if( container && !is.htmlElement( container ) && is.htmlElement( container[0] ) ){
- container = container[0];
- }
-
- var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
- reg = reg || {};
-
- if( reg && reg.cy ){
- reg.cy.destroy();
-
- reg = {}; // old instance => replace reg completely
- }
-
- var readies = reg.readies = reg.readies || [];
-
- if( container ){ container._cyreg = reg; } // make sure container assoc'd reg points to this cy
- reg.cy = cy;
-
- var head = window !== undefined && container !== undefined && !opts.headless;
- var options = opts;
- options.layout = util.extend( { name: head ? 'grid' : 'null' }, options.layout );
- options.renderer = util.extend( { name: head ? 'canvas' : 'null' }, options.renderer );
-
- var defVal = function( def, val, altVal ){
- if( val !== undefined ){
- return val;
- } else if( altVal !== undefined ){
- return altVal;
- } else {
- return def;
- }
- };
-
- var _p = this._private = {
- container: container, // html dom ele container
- ready: false, // whether ready has been triggered
- initrender: false, // has initrender has been triggered
- options: options, // cached options
- elements: [], // array of elements
- id2index: {}, // element id => index in elements array
- listeners: [], // list of listeners
- onRenders: [], // rendering listeners
- aniEles: Collection(this), // elements being animated
- scratch: {}, // scratch object for core
- layout: null,
- renderer: null,
- notificationsEnabled: true, // whether notifications are sent to the renderer
- minZoom: 1e-50,
- maxZoom: 1e50,
- zoomingEnabled: defVal(true, options.zoomingEnabled),
- userZoomingEnabled: defVal(true, options.userZoomingEnabled),
- panningEnabled: defVal(true, options.panningEnabled),
- userPanningEnabled: defVal(true, options.userPanningEnabled),
- boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
- autolock: defVal(false, options.autolock, options.autolockNodes),
- autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
- autounselectify: defVal(false, options.autounselectify),
- styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
- zoom: is.number(options.zoom) ? options.zoom : 1,
- pan: {
- x: is.plainObject(options.pan) && is.number(options.pan.x) ? options.pan.x : 0,
- y: is.plainObject(options.pan) && is.number(options.pan.y) ? options.pan.y : 0
- },
- animation: { // object for currently-running animations
- current: [],
- queue: []
- },
- hasCompoundNodes: false,
- deferredExecQueue: []
- };
-
- // set selection type
- var selType = options.selectionType;
- if( selType === undefined || (selType !== 'additive' && selType !== 'single') ){
- // then set default
-
- _p.selectionType = 'single';
- } else {
- _p.selectionType = selType;
- }
-
- // init zoom bounds
- if( is.number(options.minZoom) && is.number(options.maxZoom) && options.minZoom < options.maxZoom ){
- _p.minZoom = options.minZoom;
- _p.maxZoom = options.maxZoom;
- } else if( is.number(options.minZoom) && options.maxZoom === undefined ){
- _p.minZoom = options.minZoom;
- } else if( is.number(options.maxZoom) && options.minZoom === undefined ){
- _p.maxZoom = options.maxZoom;
- }
-
- var loadExtData = function( next ){
- var anyIsPromise = false;
-
- for( var i = 0; i < extData.length; i++ ){
- var datum = extData[i];
-
- if( is.promise(datum) ){
- anyIsPromise = true;
- break;
- }
- }
-
- if( anyIsPromise ){
- return Promise.all( extData ).then( next ); // load all data asynchronously, then exec rest of init
- } else {
- next( extData ); // exec synchronously for convenience
- }
- };
-
- // create the renderer
- cy.initRenderer( util.extend({
- hideEdgesOnViewport: options.hideEdgesOnViewport,
- hideLabelsOnViewport: options.hideLabelsOnViewport,
- textureOnViewport: options.textureOnViewport,
- wheelSensitivity: is.number(options.wheelSensitivity) && options.wheelSensitivity > 0 ? options.wheelSensitivity : 1,
- motionBlur: options.motionBlur === undefined ? true : options.motionBlur, // on by default
- motionBlurOpacity: options.motionBlurOpacity === undefined ? 0.05 : options.motionBlurOpacity,
- pixelRatio: is.number(options.pixelRatio) && options.pixelRatio > 0 ? options.pixelRatio : undefined,
- desktopTapThreshold: options.desktopTapThreshold === undefined ? 4 : options.desktopTapThreshold,
- touchTapThreshold: options.touchTapThreshold === undefined ? 8 : options.touchTapThreshold
- }, options.renderer) );
-
- var extData = [ options.style, options.elements ];
- loadExtData(function( thens ){
- var initStyle = thens[0];
- var initEles = thens[1];
-
- // init style
- if( _p.styleEnabled ){
- cy.setStyle( initStyle );
- }
-
- // trigger the passed function for the `initrender` event
- if( options.initrender ){
- cy.on('initrender', options.initrender);
- cy.on('initrender', function(){
- _p.initrender = true;
- });
- }
-
- // initial load
- cy.load(initEles, function(){ // onready
- cy.startAnimationLoop();
- _p.ready = true;
-
- // if a ready callback is specified as an option, the bind it
- if( is.fn( options.ready ) ){
- cy.on('ready', options.ready);
- }
-
- // bind all the ready handlers registered before creating this instance
- for( var i = 0; i < readies.length; i++ ){
- var fn = readies[i];
- cy.on('ready', fn);
- }
- if( reg ){ reg.readies = []; } // clear b/c we've bound them all and don't want to keep it around in case a new core uses the same div etc
-
- cy.trigger('ready');
- }, options.done);
-
- });
-};
-
-var corefn = Core.prototype; // short alias
-
-util.extend(corefn, {
- instanceString: function(){
- return 'core';
- },
-
- isReady: function(){
- return this._private.ready;
- },
-
- ready: function( fn ){
- if( this.isReady() ){
- this.trigger('ready', [], fn); // just calls fn as though triggered via ready event
- } else {
- this.on('ready', fn);
- }
-
- return this;
- },
-
- initrender: function(){
- return this._private.initrender;
- },
-
- destroy: function(){
- var cy = this;
-
- cy.stopAnimationLoop();
-
- cy.notify({ type: 'destroy' }); // destroy the renderer
-
- var domEle = cy.container();
- if( domEle ){
- domEle._cyreg = null;
-
- while( domEle.childNodes.length > 0 ){
- domEle.removeChild( domEle.childNodes[0] );
- }
- }
-
- return cy;
- },
-
- getElementById: function( id ){
- var index = this._private.id2index[ id ];
- if( index !== undefined ){
- return this._private.elements[ index ];
- }
-
- // worst case, return an empty collection
- return Collection( this );
- },
-
- selectionType: function(){
- return this._private.selectionType;
- },
-
- hasCompoundNodes: function(){
- return this._private.hasCompoundNodes;
- },
-
- styleEnabled: function(){
- return this._private.styleEnabled;
- },
-
- addToPool: function( eles ){
- var elements = this._private.elements;
- var id2index = this._private.id2index;
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
-
- var id = ele._private.data.id;
- var index = id2index[ id ];
- var alreadyInPool = index !== undefined;
-
- if( !alreadyInPool ){
- index = elements.length;
- elements.push( ele );
- id2index[ id ] = index;
- ele._private.index = index;
- }
- }
-
- return this; // chaining
- },
-
- removeFromPool: function( eles ){
- var elements = this._private.elements;
- var id2index = this._private.id2index;
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
-
- var id = ele._private.data.id;
- var index = id2index[ id ];
- var inPool = index !== undefined;
-
- if( inPool ){
- this._private.id2index[ id ] = undefined;
- elements.splice(index, 1);
-
- // adjust the index of all elements past this index
- for( var j = index; j < elements.length; j++ ){
- var jid = elements[j]._private.data.id;
- id2index[ jid ]--;
- elements[j]._private.index--;
- }
- }
- }
- },
-
- container: function(){
- return this._private.container;
- },
-
- options: function(){
- return util.copy( this._private.options );
- },
-
- json: function( obj ){
- var cy = this;
- var _p = cy._private;
-
- if( is.plainObject(obj) ){ // set
-
- cy.startBatch();
-
- if( obj.elements ){
- var idInJson = {};
-
- var updateEles = function( jsons, gr ){
- for( var i = 0; i < jsons.length; i++ ){
- var json = jsons[i];
- var id = json.data.id;
- var ele = cy.getElementById( id );
-
- idInJson[ id ] = true;
-
- if( ele.length !== 0 ){ // existing element should be updated
- ele.json( json );
- } else { // otherwise should be added
- if( gr ){
- cy.add( util.extend({ group: gr }, json) );
- } else {
- cy.add( json );
- }
- }
- }
- };
-
- if( is.array(obj.elements) ){ // elements: []
- updateEles( obj.elements );
-
- } else { // elements: { nodes: [], edges: [] }
- var grs = ['nodes', 'edges'];
- for( var i = 0; i < grs.length; i++ ){
- var gr = grs[i];
- var elements = obj.elements[ gr ];
-
- if( is.array(elements) ){
- updateEles( elements, gr );
- }
- }
- }
-
- // elements not specified in json should be removed
- cy.elements().stdFilter(function( ele ){
- return !idInJson[ ele.id() ];
- }).remove();
- }
-
- if( obj.style ){
- cy.style( obj.style );
- }
-
- if( obj.zoom != null && obj.zoom !== _p.zoom ){
- cy.zoom( obj.zoom );
- }
-
- if( obj.pan ){
- if( obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y ){
- cy.pan( obj.pan );
- }
- }
-
- var fields = [
- 'minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled',
- 'panningEnabled', 'userPanningEnabled',
- 'boxSelectionEnabled',
- 'autolock', 'autoungrabify', 'autounselectify'
- ];
-
- for( var i = 0; i < fields.length; i++ ){
- var f = fields[i];
-
- if( obj[f] != null ){
- cy[f]( obj[f] );
- }
- }
-
- cy.endBatch();
-
- return this; // chaining
- } else if( obj === undefined ){ // get
- var json = {};
-
- json.elements = {};
- cy.elements().each(function(i, ele){
- var group = ele.group();
-
- if( !json.elements[group] ){
- json.elements[group] = [];
- }
-
- json.elements[group].push( ele.json() );
- });
-
- if( this._private.styleEnabled ){
- json.style = cy.style().json();
- }
-
- json.zoomingEnabled = cy._private.zoomingEnabled;
- json.userZoomingEnabled = cy._private.userZoomingEnabled;
- json.zoom = cy._private.zoom;
- json.minZoom = cy._private.minZoom;
- json.maxZoom = cy._private.maxZoom;
- json.panningEnabled = cy._private.panningEnabled;
- json.userPanningEnabled = cy._private.userPanningEnabled;
- json.pan = util.copy( cy._private.pan );
- json.boxSelectionEnabled = cy._private.boxSelectionEnabled;
- json.renderer = util.copy( cy._private.options.renderer );
- json.hideEdgesOnViewport = cy._private.options.hideEdgesOnViewport;
- json.hideLabelsOnViewport = cy._private.options.hideLabelsOnViewport;
- json.textureOnViewport = cy._private.options.textureOnViewport;
- json.wheelSensitivity = cy._private.options.wheelSensitivity;
- json.motionBlur = cy._private.options.motionBlur;
-
- return json;
- }
- },
-
- scratch: define.data({
- field: 'scratch',
- bindingEvent: 'scratch',
- allowBinding: true,
- allowSetting: true,
- settingEvent: 'scratch',
- settingTriggersEvent: true,
- triggerFnName: 'trigger',
- allowGetting: true
- }),
-
- removeScratch: define.removeData({
- field: 'scratch',
- event: 'scratch',
- triggerFnName: 'trigger',
- triggerEvent: true
- })
-
-});
-
-[
- _dereq_('./add-remove'),
- _dereq_('./animation'),
- _dereq_('./events'),
- _dereq_('./export'),
- _dereq_('./layout'),
- _dereq_('./notification'),
- _dereq_('./renderer'),
- _dereq_('./search'),
- _dereq_('./style'),
- _dereq_('./viewport')
-].forEach(function( props ){
- util.extend( corefn, props );
-});
-
-module.exports = Core;
-
-},{"../collection":23,"../define":41,"../is":77,"../promise":80,"../util":94,"../window":100,"./add-remove":30,"./animation":31,"./events":32,"./export":33,"./layout":35,"./notification":36,"./renderer":37,"./search":38,"./style":39,"./viewport":40}],35:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-var is = _dereq_('../is');
-
-var corefn = ({
-
- layout: function( params ){
- var layout = this._private.prevLayout = ( params == null ? this._private.prevLayout : this.makeLayout( params ) );
-
- layout.run();
-
- return this; // chaining
- },
-
- makeLayout: function( options ){
- var cy = this;
-
- if( options == null ){
- util.error('Layout options must be specified to make a layout');
- return;
- }
-
- if( options.name == null ){
- util.error('A `name` must be specified to make a layout');
- return;
- }
-
- var name = options.name;
- var Layout = cy.extension('layout', name);
-
- if( Layout == null ){
- util.error('Can not apply layout: No such layout `' + name + '` found; did you include its JS file?');
- return;
- }
-
- var eles;
- if( is.string( options.eles ) ){
- eles = cy.$( options.eles );
- } else {
- eles = options.eles != null ? options.eles : cy.$();
- }
-
- var layout = new Layout( util.extend({}, options, {
- cy: cy,
- eles: eles
- }) );
-
- return layout;
- }
-
-});
-
-corefn.createLayout = corefn.makeLayout;
-
-module.exports = corefn;
-
-},{"../is":77,"../util":94}],36:[function(_dereq_,module,exports){
-'use strict';
-
-var corefn = ({
- notify: function( params ){
- var _p = this._private;
-
- if( _p.batchingNotify ){
- var bEles = _p.batchNotifyEles;
- var bTypes = _p.batchNotifyTypes;
-
- if( params.collection ){
- bEles.merge( params.collection );
- }
-
- if( !bTypes.ids[ params.type ] ){
- bTypes.push( params.type );
- }
-
- return; // notifications are disabled during batching
- }
-
- if( !_p.notificationsEnabled ){ return; } // exit on disabled
-
- var renderer = this.renderer();
-
- renderer.notify(params);
- },
-
- notifications: function( bool ){
- var p = this._private;
-
- if( bool === undefined ){
- return p.notificationsEnabled;
- } else {
- p.notificationsEnabled = bool ? true : false;
- }
- },
-
- noNotifications: function( callback ){
- this.notifications(false);
- callback();
- this.notifications(true);
- },
-
- startBatch: function(){
- var _p = this._private;
-
- if( _p.batchCount == null ){
- _p.batchCount = 0;
- }
-
- if( _p.batchCount === 0 ){
- _p.batchingStyle = _p.batchingNotify = true;
- _p.batchStyleEles = this.collection();
- _p.batchNotifyEles = this.collection();
- _p.batchNotifyTypes = [];
-
- _p.batchNotifyTypes.ids = {};
- }
-
- _p.batchCount++;
-
- return this;
- },
-
- endBatch: function(){
- var _p = this._private;
-
- _p.batchCount--;
-
- if( _p.batchCount === 0 ){
- // update style for dirty eles
- _p.batchingStyle = false;
- _p.batchStyleEles.updateStyle();
-
- // notify the renderer of queued eles and event types
- _p.batchingNotify = false;
- this.notify({
- type: _p.batchNotifyTypes,
- collection: _p.batchNotifyEles
- });
- }
-
- return this;
- },
-
- batch: function( callback ){
- this.startBatch();
- callback();
- this.endBatch();
-
- return this;
- },
-
- // for backwards compatibility
- batchData: function( map ){
- var cy = this;
-
- return this.batch(function(){
- for( var id in map ){
- var data = map[id];
- var ele = cy.getElementById( id );
-
- ele.data( data );
- }
- });
- }
-});
-
-module.exports = corefn;
-
-},{}],37:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-
-var corefn = ({
-
- renderTo: function( context, zoom, pan, pxRatio ){
- var r = this._private.renderer;
-
- r.renderTo( context, zoom, pan, pxRatio );
- return this;
- },
-
- renderer: function(){
- return this._private.renderer;
- },
-
- forceRender: function(){
- this.notify({
- type: 'draw'
- });
-
- return this;
- },
-
- resize: function(){
- this.notify({
- type: 'resize'
- });
-
- this.trigger('resize');
-
- return this;
- },
-
- initRenderer: function( options ){
- var cy = this;
-
- var RendererProto = cy.extension('renderer', options.name);
- if( RendererProto == null ){
- util.error('Can not initialise: No such renderer `%s` found; did you include its JS file?', options.name);
- return;
- }
-
- var rOpts = util.extend({}, options, {
- cy: cy
- });
- var renderer = cy._private.renderer = new RendererProto( rOpts );
-
- renderer.init( rOpts );
-
- },
-
- triggerOnRender: function(){
- var cbs = this._private.onRenders;
-
- for( var i = 0; i < cbs.length; i++ ){
- var cb = cbs[i];
-
- cb();
- }
-
- return this;
- },
-
- onRender: function( cb ){
- this._private.onRenders.push( cb );
-
- return this;
- },
-
- offRender: function( fn ){
- var cbs = this._private.onRenders;
-
- if( fn == null ){ // unbind all
- this._private.onRenders = [];
- return this;
- }
-
- for( var i = 0; i < cbs.length; i++ ){ // unbind specified
- var cb = cbs[i];
-
- if( fn === cb ){
- cbs.splice( i, 1 );
- break;
- }
- }
-
- return this;
- }
-
-});
-
-corefn.invalidateDimensions = corefn.resize;
-
-module.exports = corefn;
-
-},{"../util":94}],38:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var Collection = _dereq_('../collection');
-
-var corefn = ({
-
- // get a collection
- // - empty collection on no args
- // - collection of elements in the graph on selector arg
- // - guarantee a returned collection when elements or collection specified
- collection: function( eles, opts ){
-
- if( is.string( eles ) ){
- return this.$( eles );
-
- } else if( is.elementOrCollection( eles ) ){
- return eles.collection();
-
- } else if( is.array( eles ) ){
- return Collection( this, eles, opts );
- }
-
- return Collection( this );
- },
-
- nodes: function( selector ){
- var nodes = this.$(function(){
- return this.isNode();
- });
-
- if( selector ){
- return nodes.filter( selector );
- }
-
- return nodes;
- },
-
- edges: function( selector ){
- var edges = this.$(function(){
- return this.isEdge();
- });
-
- if( selector ){
- return edges.filter( selector );
- }
-
- return edges;
- },
-
- // search the graph like jQuery
- $: function( selector ){
- var eles = new Collection( this, this._private.elements );
-
- if( selector ){
- return eles.filter( selector );
- }
-
- return eles;
- }
-
-});
-
-// aliases
-corefn.elements = corefn.filter = corefn.$;
-
-module.exports = corefn;
-
-},{"../collection":23,"../is":77}],39:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var Style = _dereq_('../style');
-
-var corefn = ({
-
- style: function( newStyle ){
- if( newStyle ){
- var s = this.setStyle( newStyle );
-
- s.update();
- }
-
- return this._private.style;
- },
-
- setStyle: function( style ){
- var _p = this._private;
-
- if( is.stylesheet(style) ){
- _p.style = style.generateStyle(this);
-
- } else if( is.array(style) ) {
- _p.style = Style.fromJson(this, style);
-
- } else if( is.string(style) ){
- _p.style = Style.fromString(this, style);
-
- } else {
- _p.style = Style( this );
- }
-
- return _p.style;
- }
-});
-
-module.exports = corefn;
-
-},{"../is":77,"../style":86}],40:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-
-var corefn = ({
-
- autolock: function(bool){
- if( bool !== undefined ){
- this._private.autolock = bool ? true : false;
- } else {
- return this._private.autolock;
- }
-
- return this; // chaining
- },
-
- autoungrabify: function(bool){
- if( bool !== undefined ){
- this._private.autoungrabify = bool ? true : false;
- } else {
- return this._private.autoungrabify;
- }
-
- return this; // chaining
- },
-
- autounselectify: function(bool){
- if( bool !== undefined ){
- this._private.autounselectify = bool ? true : false;
- } else {
- return this._private.autounselectify;
- }
-
- return this; // chaining
- },
-
- panningEnabled: function( bool ){
- if( bool !== undefined ){
- this._private.panningEnabled = bool ? true : false;
- } else {
- return this._private.panningEnabled;
- }
-
- return this; // chaining
- },
-
- userPanningEnabled: function( bool ){
- if( bool !== undefined ){
- this._private.userPanningEnabled = bool ? true : false;
- } else {
- return this._private.userPanningEnabled;
- }
-
- return this; // chaining
- },
-
- zoomingEnabled: function( bool ){
- if( bool !== undefined ){
- this._private.zoomingEnabled = bool ? true : false;
- } else {
- return this._private.zoomingEnabled;
- }
-
- return this; // chaining
- },
-
- userZoomingEnabled: function( bool ){
- if( bool !== undefined ){
- this._private.userZoomingEnabled = bool ? true : false;
- } else {
- return this._private.userZoomingEnabled;
- }
-
- return this; // chaining
- },
-
- boxSelectionEnabled: function( bool ){
- if( bool !== undefined ){
- this._private.boxSelectionEnabled = bool ? true : false;
- } else {
- return this._private.boxSelectionEnabled;
- }
-
- return this; // chaining
- },
-
- pan: function(){
- var args = arguments;
- var pan = this._private.pan;
- var dim, val, dims, x, y;
-
- switch( args.length ){
- case 0: // .pan()
- return pan;
-
- case 1:
-
- if( is.string( args[0] ) ){ // .pan('x')
- dim = args[0];
- return pan[ dim ];
-
- } else if( is.plainObject( args[0] ) ) { // .pan({ x: 0, y: 100 })
- if( !this._private.panningEnabled ){
- return this;
- }
-
- dims = args[0];
- x = dims.x;
- y = dims.y;
-
- if( is.number(x) ){
- pan.x = x;
- }
-
- if( is.number(y) ){
- pan.y = y;
- }
-
- this.trigger('pan viewport');
- }
- break;
-
- case 2: // .pan('x', 100)
- if( !this._private.panningEnabled ){
- return this;
- }
-
- dim = args[0];
- val = args[1];
-
- if( (dim === 'x' || dim === 'y') && is.number(val) ){
- pan[dim] = val;
- }
-
- this.trigger('pan viewport');
- break;
-
- default:
- break; // invalid
- }
-
- this.notify({ // notify the renderer that the viewport changed
- type: 'viewport'
- });
-
- return this; // chaining
- },
-
- panBy: function(params){
- var args = arguments;
- var pan = this._private.pan;
- var dim, val, dims, x, y;
-
- if( !this._private.panningEnabled ){
- return this;
- }
-
- switch( args.length ){
- case 1:
-
- if( is.plainObject( args[0] ) ) { // .panBy({ x: 0, y: 100 })
- dims = args[0];
- x = dims.x;
- y = dims.y;
-
- if( is.number(x) ){
- pan.x += x;
- }
-
- if( is.number(y) ){
- pan.y += y;
- }
-
- this.trigger('pan viewport');
- }
- break;
-
- case 2: // .panBy('x', 100)
- dim = args[0];
- val = args[1];
-
- if( (dim === 'x' || dim === 'y') && is.number(val) ){
- pan[dim] += val;
- }
-
- this.trigger('pan viewport');
- break;
-
- default:
- break; // invalid
- }
-
- this.notify({ // notify the renderer that the viewport changed
- type: 'viewport'
- });
-
- return this; // chaining
- },
-
- fit: function( elements, padding ){
- var viewportState = this.getFitViewport( elements, padding );
-
- if( viewportState ){
- var _p = this._private;
- _p.zoom = viewportState.zoom;
- _p.pan = viewportState.pan;
-
- this.trigger('pan zoom viewport');
-
- this.notify({ // notify the renderer that the viewport changed
- type: 'viewport'
- });
- }
-
- return this; // chaining
- },
-
- getFitViewport: function( elements, padding ){
- if( is.number(elements) && padding === undefined ){ // elements is optional
- padding = elements;
- elements = undefined;
- }
-
- if( !this._private.panningEnabled || !this._private.zoomingEnabled ){
- return;
- }
-
- var bb;
-
- if( is.string(elements) ){
- var sel = elements;
- elements = this.$( sel );
-
- } else if( is.boundingBox(elements) ){ // assume bb
- var bbe = elements;
- bb = {
- x1: bbe.x1,
- y1: bbe.y1,
- x2: bbe.x2,
- y2: bbe.y2
- };
-
- bb.w = bb.x2 - bb.x1;
- bb.h = bb.y2 - bb.y1;
-
- } else if( !is.elementOrCollection(elements) ){
- elements = this.elements();
- }
-
- bb = bb || elements.boundingBox();
-
- var w = this.width();
- var h = this.height();
- var zoom;
- padding = is.number(padding) ? padding : 0;
-
- if( !isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0 ){
- zoom = Math.min( (w - 2*padding)/bb.w, (h - 2*padding)/bb.h );
-
- // crop zoom
- zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
- zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
-
- var pan = { // now pan to middle
- x: (w - zoom*( bb.x1 + bb.x2 ))/2,
- y: (h - zoom*( bb.y1 + bb.y2 ))/2
- };
-
- return {
- zoom: zoom,
- pan: pan
- };
- }
-
- return;
- },
-
- minZoom: function( zoom ){
- if( zoom === undefined ){
- return this._private.minZoom;
- } else if( is.number(zoom) ){
- this._private.minZoom = zoom;
- }
-
- return this;
- },
-
- maxZoom: function( zoom ){
- if( zoom === undefined ){
- return this._private.maxZoom;
- } else if( is.number(zoom) ){
- this._private.maxZoom = zoom;
- }
-
- return this;
- },
-
- zoom: function( params ){
- var pos; // in rendered px
- var zoom;
-
- if( params === undefined ){ // then get the zoom
- return this._private.zoom;
-
- } else if( is.number(params) ){ // then set the zoom
- zoom = params;
-
- } else if( is.plainObject(params) ){ // then zoom about a point
- zoom = params.level;
-
- if( params.position ){
- var p = params.position;
- var pan = this._private.pan;
- var z = this._private.zoom;
-
- pos = { // convert to rendered px
- x: p.x * z + pan.x,
- y: p.y * z + pan.y
- };
- } else if( params.renderedPosition ){
- pos = params.renderedPosition;
- }
-
- if( pos && !this._private.panningEnabled ){
- return this; // panning disabled
- }
- }
-
- if( !this._private.zoomingEnabled ){
- return this; // zooming disabled
- }
-
- if( !is.number(zoom) || ( pos && (!is.number(pos.x) || !is.number(pos.y)) ) ){
- return this; // can't zoom with invalid params
- }
-
- // crop zoom
- zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
- zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
-
- if( pos ){ // set zoom about position
- var pan1 = this._private.pan;
- var zoom1 = this._private.zoom;
- var zoom2 = zoom;
-
- var pan2 = {
- x: -zoom2/zoom1 * (pos.x - pan1.x) + pos.x,
- y: -zoom2/zoom1 * (pos.y - pan1.y) + pos.y
- };
-
- this._private.zoom = zoom;
- this._private.pan = pan2;
-
- var posChanged = pan1.x !== pan2.x || pan1.y !== pan2.y;
- this.trigger(' zoom ' + (posChanged ? ' pan ' : '') + ' viewport ' );
-
- } else { // just set the zoom
- this._private.zoom = zoom;
- this.trigger('zoom viewport');
- }
-
- this.notify({ // notify the renderer that the viewport changed
- type: 'viewport'
- });
-
- return this; // chaining
- },
-
- viewport: function( opts ){
- var _p = this._private;
- var zoomDefd = true;
- var panDefd = true;
- var events = []; // to trigger
- var zoomFailed = false;
- var panFailed = false;
-
- if( !opts ){ return this; }
- if( !is.number(opts.zoom) ){ zoomDefd = false; }
- if( !is.plainObject(opts.pan) ){ panDefd = false; }
- if( !zoomDefd && !panDefd ){ return this; }
-
- if( zoomDefd ){
- var z = opts.zoom;
-
- if( z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled ){
- zoomFailed = true;
-
- } else {
- _p.zoom = z;
-
- events.push('zoom');
- }
- }
-
- if( panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled ){
- var p = opts.pan;
-
- if( is.number(p.x) ){
- _p.pan.x = p.x;
- panFailed = false;
- }
-
- if( is.number(p.y) ){
- _p.pan.y = p.y;
- panFailed = false;
- }
-
- if( !panFailed ){
- events.push('pan');
- }
- }
-
- if( events.length > 0 ){
- events.push('viewport');
- this.trigger( events.join(' ') );
-
- this.notify({
- type: 'viewport'
- });
- }
-
- return this; // chaining
- },
-
- center: function( elements ){
- var pan = this.getCenterPan( elements );
-
- if( pan ){
- this._private.pan = pan;
-
- this.trigger('pan viewport');
-
- this.notify({ // notify the renderer that the viewport changed
- type: 'viewport'
- });
- }
-
- return this; // chaining
- },
-
- getCenterPan: function( elements, zoom ){
- if( !this._private.panningEnabled ){
- return;
- }
-
- if( is.string(elements) ){
- var selector = elements;
- elements = this.elements( selector );
- } else if( !is.elementOrCollection(elements) ){
- elements = this.elements();
- }
-
- var bb = elements.boundingBox();
- var w = this.width();
- var h = this.height();
- zoom = zoom === undefined ? this._private.zoom : zoom;
-
- var pan = { // middle
- x: (w - zoom*( bb.x1 + bb.x2 ))/2,
- y: (h - zoom*( bb.y1 + bb.y2 ))/2
- };
-
- return pan;
- },
-
- reset: function(){
- if( !this._private.panningEnabled || !this._private.zoomingEnabled ){
- return this;
- }
-
- this.viewport({
- pan: { x: 0, y: 0 },
- zoom: 1
- });
-
- return this; // chaining
- },
-
- width: function(){
- var container = this._private.container;
-
- if( container ){
- return container.clientWidth;
- }
-
- return 1; // fallback if no container (not 0 b/c can be used for dividing etc)
- },
-
- height: function(){
- var container = this._private.container;
-
- if( container ){
- return container.clientHeight;
- }
-
- return 1; // fallback if no container (not 0 b/c can be used for dividing etc)
- },
-
- extent: function(){
- var pan = this._private.pan;
- var zoom = this._private.zoom;
- var rb = this.renderedExtent();
-
- var b = {
- x1: ( rb.x1 - pan.x )/zoom,
- x2: ( rb.x2 - pan.x )/zoom,
- y1: ( rb.y1 - pan.y )/zoom,
- y2: ( rb.y2 - pan.y )/zoom
- };
-
- b.w = b.x2 - b.x1;
- b.h = b.y2 - b.y1;
-
- return b;
- },
-
- renderedExtent: function(){
- var width = this.width();
- var height = this.height();
-
- return {
- x1: 0,
- y1: 0,
- x2: width,
- y2: height,
- w: width,
- h: height
- };
- }
-});
-
-// aliases
-corefn.centre = corefn.center;
-
-// backwards compatibility
-corefn.autolockNodes = corefn.autolock;
-corefn.autoungrabifyNodes = corefn.autoungrabify;
-
-module.exports = corefn;
-
-},{"../is":77}],41:[function(_dereq_,module,exports){
-'use strict';
-
-// use this module to cherry pick functions into your prototype
-// (useful for functions shared between the core and collections, for example)
-
-// e.g.
-// var foo = define.foo({ /* params... */ })
-
-var util = _dereq_('./util');
-var is = _dereq_('./is');
-var Selector = _dereq_('./selector');
-var Promise = _dereq_('./promise');
-var Event = _dereq_('./event');
-var Animation = _dereq_('./animation');
-
-var define = {
-
- // access data field
- data: function( params ){
- var defaults = {
- field: 'data',
- bindingEvent: 'data',
- allowBinding: false,
- allowSetting: false,
- allowGetting: false,
- settingEvent: 'data',
- settingTriggersEvent: false,
- triggerFnName: 'trigger',
- immutableKeys: {}, // key => true if immutable
- updateStyle: false,
- onSet: function( self ){},
- canSet: function( self ){ return true; }
- };
- params = util.extend({}, defaults, params);
-
- return function dataImpl( name, value ){
- var p = params;
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var single = selfIsArrayLike ? self[0] : self;
-
- // .data('foo', ...)
- if( is.string(name) ){ // set or get property
-
- // .data('foo')
- if( p.allowGetting && value === undefined ){ // get
-
- var ret;
- if( single ){
- ret = single._private[ p.field ][ name ];
- }
- return ret;
-
- // .data('foo', 'bar')
- } else if( p.allowSetting && value !== undefined ) { // set
- var valid = !p.immutableKeys[name];
- if( valid ){
- for( var i = 0, l = all.length; i < l; i++ ){
- if( p.canSet( all[i] ) ){
- all[i]._private[ p.field ][ name ] = value;
- }
- }
-
- // update mappers if asked
- if( p.updateStyle ){ self.updateStyle(); }
-
- // call onSet callback
- p.onSet( self );
-
- if( p.settingTriggersEvent ){
- self[ p.triggerFnName ]( p.settingEvent );
- }
- }
- }
-
- // .data({ 'foo': 'bar' })
- } else if( p.allowSetting && is.plainObject(name) ){ // extend
- var obj = name;
- var k, v;
-
- for( k in obj ){
- v = obj[ k ];
-
- var valid = !p.immutableKeys[k];
- if( valid ){
- for( var i = 0, l = all.length; i < l; i++ ){
- if( p.canSet( all[i] ) ){
- all[i]._private[ p.field ][ k ] = v;
- }
- }
- }
- }
-
- // update mappers if asked
- if( p.updateStyle ){ self.updateStyle(); }
-
- // call onSet callback
- p.onSet( self );
-
- if( p.settingTriggersEvent ){
- self[ p.triggerFnName ]( p.settingEvent );
- }
-
- // .data(function(){ ... })
- } else if( p.allowBinding && is.fn(name) ){ // bind to event
- var fn = name;
- self.bind( p.bindingEvent, fn );
-
- // .data()
- } else if( p.allowGetting && name === undefined ){ // get whole object
- var ret;
- if( single ){
- ret = single._private[ p.field ];
- }
- return ret;
- }
-
- return self; // maintain chainability
- }; // function
- }, // data
-
- // remove data field
- removeData: function( params ){
- var defaults = {
- field: 'data',
- event: 'data',
- triggerFnName: 'trigger',
- triggerEvent: false,
- immutableKeys: {} // key => true if immutable
- };
- params = util.extend({}, defaults, params);
-
- return function removeDataImpl( names ){
- var p = params;
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
-
- // .removeData('foo bar')
- if( is.string(names) ){ // then get the list of keys, and delete them
- var keys = names.split(/\s+/);
- var l = keys.length;
-
- for( var i = 0; i < l; i++ ){ // delete each non-empty key
- var key = keys[i];
- if( is.emptyString(key) ){ continue; }
-
- var valid = !p.immutableKeys[ key ]; // not valid if immutable
- if( valid ){
- for( var i_a = 0, l_a = all.length; i_a < l_a; i_a++ ){
- all[ i_a ]._private[ p.field ][ key ] = undefined;
- }
- }
- }
-
- if( p.triggerEvent ){
- self[ p.triggerFnName ]( p.event );
- }
-
- // .removeData()
- } else if( names === undefined ){ // then delete all keys
-
- for( var i_a = 0, l_a = all.length; i_a < l_a; i_a++ ){
- var _privateFields = all[ i_a ]._private[ p.field ];
-
- for( var key in _privateFields ){
- var validKeyToDelete = !p.immutableKeys[ key ];
-
- if( validKeyToDelete ){
- _privateFields[ key ] = undefined;
- }
- }
- }
-
- if( p.triggerEvent ){
- self[ p.triggerFnName ]( p.event );
- }
- }
-
- return self; // maintain chaining
- }; // function
- }, // removeData
-
- // event function reusable stuff
- event: {
- regex: /(\w+)(\.\w+)?/, // regex for matching event strings (e.g. "click.namespace")
- optionalTypeRegex: /(\w+)?(\.\w+)?/,
- falseCallback: function(){ return false; }
- },
-
- // event binding
- on: function( params ){
- var defaults = {
- unbindSelfOnTrigger: false,
- unbindAllBindersOnTrigger: false
- };
- params = util.extend({}, defaults, params);
-
- return function onImpl(events, selector, data, callback){
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var eventsIsString = is.string(events);
- var p = params;
-
- if( is.plainObject(selector) ){ // selector is actually data
- callback = data;
- data = selector;
- selector = undefined;
- } else if( is.fn(selector) || selector === false ){ // selector is actually callback
- callback = selector;
- data = undefined;
- selector = undefined;
- }
-
- if( is.fn(data) || data === false ){ // data is actually callback
- callback = data;
- data = undefined;
- }
-
- // if there isn't a callback, we can't really do anything
- // (can't speak for mapped events arg version)
- if( !(is.fn(callback) || callback === false) && eventsIsString ){
- return self; // maintain chaining
- }
-
- if( eventsIsString ){ // then convert to map
- var map = {};
- map[ events ] = callback;
- events = map;
- }
-
- for( var evts in events ){
- callback = events[evts];
- if( callback === false ){
- callback = define.event.falseCallback;
- }
-
- if( !is.fn(callback) ){ continue; }
-
- evts = evts.split(/\s+/);
- for( var i = 0; i < evts.length; i++ ){
- var evt = evts[i];
- if( is.emptyString(evt) ){ continue; }
-
- var match = evt.match( define.event.regex ); // type[.namespace]
-
- if( match ){
- var type = match[1];
- var namespace = match[2] ? match[2] : undefined;
-
- var listener = {
- callback: callback, // callback to run
- data: data, // extra data in eventObj.data
- delegated: selector ? true : false, // whether the evt is delegated
- selector: selector, // the selector to match for delegated events
- selObj: new Selector(selector), // cached selector object to save rebuilding
- type: type, // the event type (e.g. 'click')
- namespace: namespace, // the event namespace (e.g. ".foo")
- unbindSelfOnTrigger: p.unbindSelfOnTrigger,
- unbindAllBindersOnTrigger: p.unbindAllBindersOnTrigger,
- binders: all // who bound together
- };
-
- for( var j = 0; j < all.length; j++ ){
- var _p = all[j]._private;
-
- _p.listeners = _p.listeners || [];
- _p.listeners.push( listener );
- }
- }
- } // for events array
- } // for events map
-
- return self; // maintain chaining
- }; // function
- }, // on
-
- eventAliasesOn: function( proto ){
- var p = proto;
-
- p.addListener = p.listen = p.bind = p.on;
- p.removeListener = p.unlisten = p.unbind = p.off;
- p.emit = p.trigger;
-
- // this is just a wrapper alias of .on()
- p.pon = p.promiseOn = function( events, selector ){
- var self = this;
- var args = Array.prototype.slice.call( arguments, 0 );
-
- return new Promise(function( resolve, reject ){
- var callback = function( e ){
- self.off.apply( self, offArgs );
-
- resolve( e );
- };
-
- var onArgs = args.concat([ callback ]);
- var offArgs = onArgs.concat([]);
-
- self.on.apply( self, onArgs );
- });
- };
- },
-
- off: function offImpl( params ){
- var defaults = {
- };
- params = util.extend({}, defaults, params);
-
- return function(events, selector, callback){
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var eventsIsString = is.string(events);
-
- if( arguments.length === 0 ){ // then unbind all
-
- for( var i = 0; i < all.length; i++ ){
- all[i]._private.listeners = [];
- }
-
- return self; // maintain chaining
- }
-
- if( is.fn(selector) || selector === false ){ // selector is actually callback
- callback = selector;
- selector = undefined;
- }
-
- if( eventsIsString ){ // then convert to map
- var map = {};
- map[ events ] = callback;
- events = map;
- }
-
- for( var evts in events ){
- callback = events[evts];
-
- if( callback === false ){
- callback = define.event.falseCallback;
- }
-
- evts = evts.split(/\s+/);
- for( var h = 0; h < evts.length; h++ ){
- var evt = evts[h];
- if( is.emptyString(evt) ){ continue; }
-
- var match = evt.match( define.event.optionalTypeRegex ); // [type][.namespace]
- if( match ){
- var type = match[1] ? match[1] : undefined;
- var namespace = match[2] ? match[2] : undefined;
-
- for( var i = 0; i < all.length; i++ ){ //
- var listeners = all[i]._private.listeners = all[i]._private.listeners || [];
-
- for( var j = 0; j < listeners.length; j++ ){
- var listener = listeners[j];
- var nsMatches = !namespace || namespace === listener.namespace;
- var typeMatches = !type || listener.type === type;
- var cbMatches = !callback || callback === listener.callback;
- var listenerMatches = nsMatches && typeMatches && cbMatches;
-
- // delete listener if it matches
- if( listenerMatches ){
- listeners.splice(j, 1);
- j--;
- }
- } // for listeners
- } // for all
- } // if match
- } // for events array
-
- } // for events map
-
- return self; // maintain chaining
- }; // function
- }, // off
-
- trigger: function( params ){
- var defaults = {};
- params = util.extend({}, defaults, params);
-
- return function triggerImpl(events, extraParams, fnToTrigger){
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var eventsIsString = is.string(events);
- var eventsIsObject = is.plainObject(events);
- var eventsIsEvent = is.event(events);
- var cy = this._private.cy || ( is.core(this) ? this : null );
- var hasCompounds = cy ? cy.hasCompoundNodes() : false;
-
- if( eventsIsString ){ // then make a plain event object for each event name
- var evts = events.split(/\s+/);
- events = [];
-
- for( var i = 0; i < evts.length; i++ ){
- var evt = evts[i];
- if( is.emptyString(evt) ){ continue; }
-
- var match = evt.match( define.event.regex ); // type[.namespace]
- var type = match[1];
- var namespace = match[2] ? match[2] : undefined;
-
- events.push( {
- type: type,
- namespace: namespace
- } );
- }
- } else if( eventsIsObject ){ // put in length 1 array
- var eventArgObj = events;
-
- events = [ eventArgObj ];
- }
-
- if( extraParams ){
- if( !is.array(extraParams) ){ // make sure extra params are in an array if specified
- extraParams = [ extraParams ];
- }
- } else { // otherwise, we've got nothing
- extraParams = [];
- }
-
- for( var i = 0; i < events.length; i++ ){ // trigger each event in order
- var evtObj = events[i];
-
- for( var j = 0; j < all.length; j++ ){ // for each
- var triggerer = all[j];
- var listeners = triggerer._private.listeners = triggerer._private.listeners || [];
- var triggererIsElement = is.element(triggerer);
- var bubbleUp = triggererIsElement || params.layout;
-
- // create the event for this element from the event object
- var evt;
-
- if( eventsIsEvent ){ // then just get the object
- evt = evtObj;
-
- evt.cyTarget = evt.cyTarget || triggerer;
- evt.cy = evt.cy || cy;
-
- } else { // then we have to make one
- evt = new Event( evtObj, {
- cyTarget: triggerer,
- cy: cy,
- namespace: evtObj.namespace
- } );
- }
-
- // if a layout was specified, then put it in the typed event
- if( evtObj.layout ){
- evt.layout = evtObj.layout;
- }
-
- // if triggered by layout, put in event
- if( params.layout ){
- evt.layout = triggerer;
- }
-
- // create a rendered position based on the passed position
- if( evt.cyPosition ){
- var pos = evt.cyPosition;
- var zoom = cy.zoom();
- var pan = cy.pan();
-
- evt.cyRenderedPosition = {
- x: pos.x * zoom + pan.x,
- y: pos.y * zoom + pan.y
- };
- }
-
- if( fnToTrigger ){ // then override the listeners list with just the one we specified
- listeners = [{
- namespace: evt.namespace,
- type: evt.type,
- callback: fnToTrigger
- }];
- }
-
- for( var k = 0; k < listeners.length; k++ ){ // check each listener
- var lis = listeners[k];
- var nsMatches = !lis.namespace || lis.namespace === evt.namespace;
- var typeMatches = lis.type === evt.type;
- var targetMatches = lis.delegated ? ( triggerer !== evt.cyTarget && is.element(evt.cyTarget) && lis.selObj.matches(evt.cyTarget) ) : (true); // we're not going to validate the hierarchy; that's too expensive
- var listenerMatches = nsMatches && typeMatches && targetMatches;
-
- if( listenerMatches ){ // then trigger it
- var args = [ evt ];
- args = args.concat( extraParams ); // add extra params to args list
-
- if( lis.data ){ // add on data plugged into binding
- evt.data = lis.data;
- } else { // or clear it in case the event obj is reused
- evt.data = undefined;
- }
-
- if( lis.unbindSelfOnTrigger || lis.unbindAllBindersOnTrigger ){ // then remove listener
- listeners.splice(k, 1);
- k--;
- }
-
- if( lis.unbindAllBindersOnTrigger ){ // then delete the listener for all binders
- var binders = lis.binders;
- for( var l = 0; l < binders.length; l++ ){
- var binder = binders[l];
- if( !binder || binder === triggerer ){ continue; } // already handled triggerer or we can't handle it
-
- var binderListeners = binder._private.listeners;
- for( var m = 0; m < binderListeners.length; m++ ){
- var binderListener = binderListeners[m];
-
- if( binderListener === lis ){ // delete listener from list
- binderListeners.splice(m, 1);
- m--;
- }
- }
- }
- }
-
- // run the callback
- var context = lis.delegated ? evt.cyTarget : triggerer;
- var ret = lis.callback.apply( context, args );
-
- if( ret === false || evt.isPropagationStopped() ){
- // then don't bubble
- bubbleUp = false;
-
- if( ret === false ){
- // returning false is a shorthand for stopping propagation and preventing the def. action
- evt.stopPropagation();
- evt.preventDefault();
- }
- }
- } // if listener matches
- } // for each listener
-
- // bubble up event for elements
- if( bubbleUp ){
- var parent = hasCompounds ? triggerer._private.parent : null;
- var hasParent = parent != null && parent.length !== 0;
-
- if( hasParent ){ // then bubble up to parent
- parent = parent[0];
- parent.trigger(evt);
- } else { // otherwise, bubble up to the core
- cy.trigger(evt);
- }
- }
-
- } // for each of all
- } // for each event
-
- return self; // maintain chaining
- }; // function
- }, // trigger
-
- animated: function( fnParams ){
- var defaults = {};
- fnParams = util.extend({}, defaults, fnParams);
-
- return function animatedImpl(){
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var cy = this._private.cy || this;
-
- if( !cy.styleEnabled() ){ return false; }
-
- var ele = all[0];
-
- if( ele ){
- return ele._private.animation.current.length > 0;
- }
- };
- }, // animated
-
- clearQueue: function( fnParams ){
- var defaults = {};
- fnParams = util.extend({}, defaults, fnParams);
-
- return function clearQueueImpl(){
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var cy = this._private.cy || this;
-
- if( !cy.styleEnabled() ){ return this; }
-
- for( var i = 0; i < all.length; i++ ){
- var ele = all[i];
- ele._private.animation.queue = [];
- }
-
- return this;
- };
- }, // clearQueue
-
- delay: function( fnParams ){
- var defaults = {};
- fnParams = util.extend({}, defaults, fnParams);
-
- return function delayImpl( time, complete ){
- var cy = this._private.cy || this;
-
- if( !cy.styleEnabled() ){ return this; }
-
- return this.animate({
- delay: time,
- duration: time,
- complete: complete
- });
- };
- }, // delay
-
- delayAnimation: function( fnParams ){
- var defaults = {};
- fnParams = util.extend({}, defaults, fnParams);
-
- return function delayAnimationImpl( time, complete ){
- var cy = this._private.cy || this;
-
- if( !cy.styleEnabled() ){ return this; }
-
- return this.animation({
- delay: time,
- duration: time,
- complete: complete
- });
- };
- }, // delay
-
- animation: function( fnParams ){
- var defaults = {};
- fnParams = util.extend({}, defaults, fnParams);
-
- return function animationImpl( properties, params ){
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var cy = this._private.cy || this;
- var isCore = !selfIsArrayLike;
- var isEles = !isCore;
-
- if( !cy.styleEnabled() ){ return this; }
-
- var style = cy.style();
-
- properties = util.extend( {}, properties, params );
-
- if( properties.duration === undefined ){
- properties.duration = 400;
- }
-
- switch( properties.duration ){
- case 'slow':
- properties.duration = 600;
- break;
- case 'fast':
- properties.duration = 200;
- break;
- }
-
- var propertiesEmpty = true;
- if( properties ){ for( var i in properties ){ // jshint ignore:line
- propertiesEmpty = false;
- break;
- } }
-
- if( propertiesEmpty ){
- return new Animation( all[0], properties ); // nothing to animate
- }
-
- if( isEles ){
- properties.style = style.getPropsList( properties.style || properties.css );
-
- properties.css = undefined;
- }
-
- if( properties.renderedPosition && isEles ){
- var rpos = properties.renderedPosition;
- var pan = cy.pan();
- var zoom = cy.zoom();
-
- properties.position = {
- x: ( rpos.x - pan.x ) /zoom,
- y: ( rpos.y - pan.y ) /zoom
- };
- }
-
- // override pan w/ panBy if set
- if( properties.panBy && isCore ){
- var panBy = properties.panBy;
- var cyPan = cy.pan();
-
- properties.pan = {
- x: cyPan.x + panBy.x,
- y: cyPan.y + panBy.y
- };
- }
-
- // override pan w/ center if set
- var center = properties.center || properties.centre;
- if( center && isCore ){
- var centerPan = cy.getCenterPan( center.eles, properties.zoom );
-
- if( centerPan ){
- properties.pan = centerPan;
- }
- }
-
- // override pan & zoom w/ fit if set
- if( properties.fit && isCore ){
- var fit = properties.fit;
- var fitVp = cy.getFitViewport( fit.eles || fit.boundingBox, fit.padding );
-
- if( fitVp ){
- properties.pan = fitVp.pan;
- properties.zoom = fitVp.zoom;
- }
- }
-
- return new Animation( all[0], properties );
- };
- }, // animate
-
- animate: function( fnParams ){
- var defaults = {};
- fnParams = util.extend({}, defaults, fnParams);
-
- return function animateImpl( properties, params ){
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var cy = this._private.cy || this;
-
- if( !cy.styleEnabled() ){ return this; }
-
- if( params ){
- properties = util.extend( {}, properties, params );
- }
-
- // manually hook and run the animation
- for( var i = 0; i < all.length; i++ ){
- var ele = all[i];
- var queue = ele.animated() && (properties.queue === undefined || properties.queue);
-
- var ani = ele.animation( properties, (queue ? { queue: true } : undefined) );
-
- ani.play();
- }
-
- return this; // chaining
- };
- }, // animate
-
- stop: function( fnParams ){
- var defaults = {};
- fnParams = util.extend({}, defaults, fnParams);
-
- return function stopImpl( clearQueue, jumpToEnd ){
- var self = this;
- var selfIsArrayLike = self.length !== undefined;
- var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
- var cy = this._private.cy || this;
-
- if( !cy.styleEnabled() ){ return this; }
-
- for( var i = 0; i < all.length; i++ ){
- var ele = all[i];
- var _p = ele._private;
- var anis = _p.animation.current;
-
- for( var j = 0; j < anis.length; j++ ){
- var ani = anis[j];
- var ani_p = ani._private;
-
- if( jumpToEnd ){
- // next iteration of the animation loop, the animation
- // will go straight to the end and be removed
- ani_p.duration = 0;
- }
- }
-
- // clear the queue of future animations
- if( clearQueue ){
- _p.animation.queue = [];
- }
-
- if( !jumpToEnd ){
- _p.animation.current = [];
- }
- }
-
- // we have to notify (the animation loop doesn't do it for us on `stop`)
- cy.notify({
- collection: this,
- type: 'draw'
- });
-
- return this;
- };
- } // stop
-
-}; // define
-
-module.exports = define;
-
-},{"./animation":1,"./event":42,"./is":77,"./promise":80,"./selector":81,"./util":94}],42:[function(_dereq_,module,exports){
-'use strict';
-
-// ref
-// https://github.com/jquery/jquery/blob/master/src/event.js
-
-var Event = function( src, props ) {
- // Allow instantiation without the 'new' keyword
- if ( !(this instanceof Event) ) {
- return new Event( src, props );
- }
-
- // Event object
- if ( src && src.type ) {
- this.originalEvent = src;
- this.type = src.type;
-
- // Events bubbling up the document may have been marked as prevented
- // by a handler lower down the tree; reflect the correct value.
- this.isDefaultPrevented = ( src.defaultPrevented ) ? returnTrue : returnFalse;
-
- // Event type
- } else {
- this.type = src;
- }
-
- // Put explicitly provided properties onto the event object
- if ( props ) {
- // util.extend( this, props );
-
- // more efficient to manually copy fields we use
- this.type = props.type !== undefined ? props.type : this.type;
- this.cy = props.cy;
- this.cyTarget = props.cyTarget;
- this.cyPosition = props.cyPosition;
- this.cyRenderedPosition = props.cyRenderedPosition;
- this.namespace = props.namespace;
- this.layout = props.layout;
- this.data = props.data;
- this.message = props.message;
- }
-
- // Create a timestamp if incoming event doesn't have one
- this.timeStamp = src && src.timeStamp || Date.now();
-};
-
-function returnFalse() {
- return false;
-}
-
-function returnTrue() {
- return true;
-}
-
-// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
-Event.prototype = {
- instanceString: function(){
- return 'event';
- },
-
- preventDefault: function() {
- this.isDefaultPrevented = returnTrue;
-
- var e = this.originalEvent;
- if ( !e ) {
- return;
- }
-
- // if preventDefault exists run it on the original event
- if ( e.preventDefault ) {
- e.preventDefault();
- }
- },
-
- stopPropagation: function() {
- this.isPropagationStopped = returnTrue;
-
- var e = this.originalEvent;
- if ( !e ) {
- return;
- }
-
- // if stopPropagation exists run it on the original event
- if ( e.stopPropagation ) {
- e.stopPropagation();
- }
- },
-
- stopImmediatePropagation: function() {
- this.isImmediatePropagationStopped = returnTrue;
- this.stopPropagation();
- },
-
- isDefaultPrevented: returnFalse,
- isPropagationStopped: returnFalse,
- isImmediatePropagationStopped: returnFalse
-};
-
-module.exports = Event;
-
-},{}],43:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('./util');
-var define = _dereq_('./define');
-var Collection = _dereq_('./collection');
-var Core = _dereq_('./core');
-var incExts = _dereq_('./extensions');
-var is = _dereq_('./is');
-
-// registered extensions to cytoscape, indexed by name
-var extensions = {};
-
-// registered modules for extensions, indexed by name
-var modules = {};
-
-function setExtension( type, name, registrant ){
-
- var ext = registrant;
-
- if( type === 'core' ){
- Core.prototype[ name ] = registrant;
-
- } else if( type === 'collection' ){
- Collection.prototype[ name ] = registrant;
-
- } else if( type === 'layout' ){
- // fill in missing layout functions in the prototype
-
- var Layout = function( options ){
- this.options = options;
-
- registrant.call( this, options );
-
- // make sure layout has _private for use w/ std apis like .on()
- if( !is.plainObject(this._private) ){
- this._private = {};
- }
-
- this._private.cy = options.cy;
- this._private.listeners = [];
- };
-
- var layoutProto = Layout.prototype = Object.create( registrant.prototype );
-
- var optLayoutFns = [];
-
- for( var i = 0; i < optLayoutFns.length; i++ ){
- var fnName = optLayoutFns[i];
-
- layoutProto[fnName] = layoutProto[fnName] || function(){ return this; };
- }
-
- // either .start() or .run() is defined, so autogen the other
- if( layoutProto.start && !layoutProto.run ){
- layoutProto.run = function(){ this.start(); return this; };
- } else if( !layoutProto.start && layoutProto.run ){
- layoutProto.start = function(){ this.run(); return this; };
- }
-
- if( !layoutProto.stop ){
- layoutProto.stop = function(){
- var opts = this.options;
-
- if( opts && opts.animate ){
- var anis = this.animations;
- for( var i = 0; i < anis.length; i++ ){
- anis[i].stop();
- }
- }
-
- this.trigger('layoutstop');
-
- return this;
- };
- }
-
- if( !layoutProto.destroy ){
- layoutProto.destroy = function(){
- return this;
- };
- }
-
- layoutProto.on = define.on({ layout: true });
- layoutProto.one = define.on({ layout: true, unbindSelfOnTrigger: true });
- layoutProto.once = define.on({ layout: true, unbindAllBindersOnTrigger: true });
- layoutProto.off = define.off({ layout: true });
- layoutProto.trigger = define.trigger({ layout: true });
-
- define.eventAliasesOn( layoutProto );
-
- ext = Layout; // replace with our wrapped layout
-
- } else if( type === 'renderer' && name !== 'null' && name !== 'base' ){
- // user registered renderers inherit from base
-
- var bProto = getExtension( 'renderer', 'base' ).prototype;
- var rProto = registrant.prototype;
-
- for( var pName in bProto ){
- var pVal = bProto[ pName ];
- var existsInR = rProto[ pName ] != null;
-
- if( existsInR ){
- util.error('Can not register renderer `' + name + '` since it overrides `' + pName + '` in its prototype');
- return;
- }
-
- rProto[ pName ] = pVal; // take impl from base
- }
-
- bProto.clientFunctions.forEach(function( name ){
- rProto[ name ] = rProto[ name ] || function(){
- util.error('Renderer does not implement `renderer.' + name + '()` on its prototype');
- };
- });
-
- }
-
- return util.setMap({
- map: extensions,
- keys: [ type, name ],
- value: ext
- });
-}
-
-function getExtension(type, name){
- return util.getMap({
- map: extensions,
- keys: [ type, name ]
- });
-}
-
-function setModule(type, name, moduleType, moduleName, registrant){
- return util.setMap({
- map: modules,
- keys: [ type, name, moduleType, moduleName ],
- value: registrant
- });
-}
-
-function getModule(type, name, moduleType, moduleName){
- return util.getMap({
- map: modules,
- keys: [ type, name, moduleType, moduleName ]
- });
-}
-
-var extension = function(){
- // e.g. extension('renderer', 'svg')
- if( arguments.length === 2 ){
- return getExtension.apply(null, arguments);
- }
-
- // e.g. extension('renderer', 'svg', { ... })
- else if( arguments.length === 3 ){
- return setExtension.apply(null, arguments);
- }
-
- // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
- else if( arguments.length === 4 ){
- return getModule.apply(null, arguments);
- }
-
- // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
- else if( arguments.length === 5 ){
- return setModule.apply(null, arguments);
- }
-
- else {
- util.error('Invalid extension access syntax');
- }
-
-};
-
-// allows a core instance to access extensions internally
-Core.prototype.extension = extension;
-
-// included extensions
-incExts.forEach(function( group ){
- group.extensions.forEach(function( ext ){
- setExtension( group.type, ext.name, ext.impl );
- });
-});
-
-module.exports = extension;
-
-},{"./collection":23,"./core":34,"./define":41,"./extensions":44,"./is":77,"./util":94}],44:[function(_dereq_,module,exports){
-'use strict';
-
-module.exports = [
- {
- type: 'layout',
- extensions: _dereq_('./layout')
- },
-
- {
- type: 'renderer',
- extensions: _dereq_('./renderer')
- }
-];
-
-},{"./layout":50,"./renderer":72}],45:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-var math = _dereq_('../../math');
-var is = _dereq_('../../is');
-
-var defaults = {
- fit: true, // whether to fit the viewport to the graph
- directed: false, // whether the tree is directed downwards (or edges can point in any direction if false)
- padding: 30, // padding on fit
- circle: false, // put depths in concentric circles if true, put depths top down if false
- spacingFactor: 1.75, // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
- boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
- avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space
- roots: undefined, // the roots of the trees
- maximalAdjustments: 0, // how many times to try to position the nodes in a maximal way (i.e. no backtracking)
- animate: false, // whether to transition the node positions
- animationDuration: 500, // duration of animation in ms if enabled
- animationEasing: undefined, // easing of animation if enabled
- ready: undefined, // callback on layoutready
- stop: undefined // callback on layoutstop
-};
-
-function BreadthFirstLayout( options ){
- this.options = util.extend({}, defaults, options);
-}
-
-BreadthFirstLayout.prototype.run = function(){
- var params = this.options;
- var options = params;
-
- var cy = params.cy;
- var eles = options.eles;
- var nodes = eles.nodes().not(':parent');
- var graph = eles;
-
- var bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
- x1: 0, y1: 0, w: cy.width(), h: cy.height()
- } );
-
- var roots;
- if( is.elementOrCollection(options.roots) ){
- roots = options.roots;
- } else if( is.array(options.roots) ){
- var rootsArray = [];
-
- for( var i = 0; i < options.roots.length; i++ ){
- var id = options.roots[i];
- var ele = cy.getElementById( id );
- rootsArray.push( ele );
- }
-
- roots = cy.collection( rootsArray );
- } else if( is.string(options.roots) ){
- roots = cy.$( options.roots );
-
- } else {
- if( options.directed ){
- roots = nodes.roots();
- } else {
- var components = [];
- var unhandledNodes = nodes;
-
- while( unhandledNodes.length > 0 ){
- var currComp = cy.collection();
-
- eles.bfs({
- roots: unhandledNodes[0],
- visit: function(i, depth, node, edge, pNode){
- currComp = currComp.add( node );
- },
- directed: false
- });
-
- unhandledNodes = unhandledNodes.not( currComp );
- components.push( currComp );
- }
-
- roots = cy.collection();
- for( var i = 0; i < components.length; i++ ){
- var comp = components[i];
- var maxDegree = comp.maxDegree( false );
- var compRoots = comp.filter(function(){
- return this.degree(false) === maxDegree;
- });
-
- roots = roots.add( compRoots );
- }
-
- }
- }
-
-
- var depths = [];
- var foundByBfs = {};
- var id2depth = {};
- var prevNode = {};
- var prevEdge = {};
- var successors = {};
-
- // find the depths of the nodes
- graph.bfs({
- roots: roots,
- directed: options.directed,
- visit: function(i, depth, node, edge, pNode){
- var ele = this[0];
- var id = ele.id();
-
- if( !depths[depth] ){
- depths[depth] = [];
- }
-
- depths[depth].push( ele );
- foundByBfs[ id ] = true;
- id2depth[ id ] = depth;
- prevNode[ id ] = pNode;
- prevEdge[ id ] = edge;
-
- if( pNode ){
- var prevId = pNode.id();
- var succ = successors[ prevId ] = successors[ prevId ] || [];
-
- succ.push( node );
- }
- }
- });
-
- // check for nodes not found by bfs
- var orphanNodes = [];
- for( var i = 0; i < nodes.length; i++ ){
- var ele = nodes[i];
-
- if( foundByBfs[ ele.id() ] ){
- continue;
- } else {
- orphanNodes.push( ele );
- }
- }
-
- // assign orphan nodes a depth from their neighborhood
- var maxChecks = orphanNodes.length * 3;
- var checks = 0;
- while( orphanNodes.length !== 0 && checks < maxChecks ){
- var node = orphanNodes.shift();
- var neighbors = node.neighborhood().nodes();
- var assignedDepth = false;
-
- for( var i = 0; i < neighbors.length; i++ ){
- var depth = id2depth[ neighbors[i].id() ];
-
- if( depth !== undefined ){
- depths[depth].push( node );
- assignedDepth = true;
- break;
- }
- }
-
- if( !assignedDepth ){
- orphanNodes.push( node );
- }
-
- checks++;
- }
-
- // assign orphan nodes that are still left to the depth of their subgraph
- while( orphanNodes.length !== 0 ){
- var node = orphanNodes.shift();
- //var subgraph = graph.bfs( node ).path;
- var assignedDepth = false;
-
- // for( var i = 0; i < subgraph.length; i++ ){
- // var depth = id2depth[ subgraph[i].id() ];
-
- // if( depth !== undefined ){
- // depths[depth].push( node );
- // assignedDepth = true;
- // break;
- // }
- // }
-
- if( !assignedDepth ){ // worst case if the graph really isn't tree friendly, then just dump it in 0
- if( depths.length === 0 ){
- depths.push([]);
- }
-
- depths[0].push( node );
- }
- }
-
- // assign the nodes a depth and index
- var assignDepthsToEles = function(){
- for( var i = 0; i < depths.length; i++ ){
- var eles = depths[i];
-
- for( var j = 0; j < eles.length; j++ ){
- var ele = eles[j];
-
- ele._private.scratch.breadthfirst = {
- depth: i,
- index: j
- };
- }
- }
- };
- assignDepthsToEles();
-
-
- var intersectsDepth = function( node ){ // returns true if has edges pointing in from a higher depth
- var edges = node.connectedEdges(function(){
- return this.data('target') === node.id();
- });
- var thisInfo = node._private.scratch.breadthfirst;
- var highestDepthOfOther = 0;
- var highestOther;
- for( var i = 0; i < edges.length; i++ ){
- var edge = edges[i];
- var otherNode = edge.source()[0];
- var otherInfo = otherNode._private.scratch.breadthfirst;
-
- if( thisInfo.depth <= otherInfo.depth && highestDepthOfOther < otherInfo.depth ){
- highestDepthOfOther = otherInfo.depth;
- highestOther = otherNode;
- }
- }
-
- return highestOther;
- };
-
- // make maximal if so set by adjusting depths
- for( var adj = 0; adj < options.maximalAdjustments; adj++ ){
-
- var nDepths = depths.length;
- var elesToMove = [];
- for( var i = 0; i < nDepths; i++ ){
- var depth = depths[i];
-
- var nDepth = depth.length;
- for( var j = 0; j < nDepth; j++ ){
- var ele = depth[j];
- var info = ele._private.scratch.breadthfirst;
- var intEle = intersectsDepth(ele);
-
- if( intEle ){
- info.intEle = intEle;
- elesToMove.push( ele );
- }
- }
- }
-
- for( var i = 0; i < elesToMove.length; i++ ){
- var ele = elesToMove[i];
- var info = ele._private.scratch.breadthfirst;
- var intEle = info.intEle;
- var intInfo = intEle._private.scratch.breadthfirst;
-
- depths[ info.depth ].splice( info.index, 1 ); // remove from old depth & index
-
- // add to end of new depth
- var newDepth = intInfo.depth + 1;
- while( newDepth > depths.length - 1 ){
- depths.push([]);
- }
- depths[ newDepth ].push( ele );
-
- info.depth = newDepth;
- info.index = depths[newDepth].length - 1;
- }
-
- assignDepthsToEles();
- }
-
- // find min distance we need to leave between nodes
- var minDistance = 0;
- if( options.avoidOverlap ){
- for( var i = 0; i < nodes.length; i++ ){
- var n = nodes[i];
- var nbb = n.boundingBox();
- var w = nbb.w;
- var h = nbb.h;
-
- minDistance = Math.max(minDistance, w, h);
- }
- minDistance *= options.spacingFactor; // just to have some nice spacing
- }
-
- // get the weighted percent for an element based on its connectivity to other levels
- var cachedWeightedPercent = {};
- var getWeightedPercent = function( ele ){
- if( cachedWeightedPercent[ ele.id() ] ){
- return cachedWeightedPercent[ ele.id() ];
- }
-
- var eleDepth = ele._private.scratch.breadthfirst.depth;
- var neighbors = ele.neighborhood().nodes().not(':parent');
- var percent = 0;
- var samples = 0;
-
- for( var i = 0; i < neighbors.length; i++ ){
- var neighbor = neighbors[i];
- var bf = neighbor._private.scratch.breadthfirst;
- var index = bf.index;
- var depth = bf.depth;
- var nDepth = depths[depth].length;
-
- if( eleDepth > depth || eleDepth === 0 ){ // only get influenced by elements above
- percent += index / nDepth;
- samples++;
- }
- }
-
- samples = Math.max(1, samples);
- percent = percent / samples;
-
- if( samples === 0 ){ // so lone nodes have a "don't care" state in sorting
- percent = undefined;
- }
-
- cachedWeightedPercent[ ele.id() ] = percent;
- return percent;
- };
-
-
- // rearrange the indices in each depth level based on connectivity
-
- var sortFn = function(a, b){
- var apct = getWeightedPercent( a );
- var bpct = getWeightedPercent( b );
-
- return apct - bpct;
- };
-
- for( var times = 0; times < 3; times++ ){ // do it a few times b/c the depths are dynamic and we want a more stable result
-
- for( var i = 0; i < depths.length; i++ ){
- depths[i] = depths[i].sort( sortFn );
- }
- assignDepthsToEles(); // and update
-
- }
-
- var biggestDepthSize = 0;
- for( var i = 0; i < depths.length; i++ ){
- biggestDepthSize = Math.max( depths[i].length, biggestDepthSize );
- }
-
- var center = {
- x: bb.x1 + bb.w/2,
- y: bb.x1 + bb.h/2
- };
-
- var getPosition = function( ele, isBottomDepth ){
- var info = ele._private.scratch.breadthfirst;
- var depth = info.depth;
- var index = info.index;
- var depthSize = depths[depth].length;
-
- var distanceX = Math.max( bb.w / (depthSize + 1), minDistance );
- var distanceY = Math.max( bb.h / (depths.length + 1), minDistance );
- var radiusStepSize = Math.min( bb.w / 2 / depths.length, bb.h / 2 / depths.length );
- radiusStepSize = Math.max( radiusStepSize, minDistance );
-
- if( !options.circle ){
-
- var epos = {
- x: center.x + (index + 1 - (depthSize + 1)/2) * distanceX,
- y: (depth + 1) * distanceY
- };
-
- if( isBottomDepth ){
- return epos;
- }
-
- // var succs = successors[ ele.id() ];
- // if( succs ){
- // epos.x = 0;
- //
- // for( var i = 0 ; i < succs.length; i++ ){
- // var spos = pos[ succs[i].id() ];
- //
- // epos.x += spos.x;
- // }
- //
- // epos.x /= succs.length;
- // } else {
- // //debugger;
- // }
-
- return epos;
-
- } else {
- if( options.circle ){
- var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize/2 : 0);
- var theta = 2 * Math.PI / depths[depth].length * index;
-
- if( depth === 0 && depths[0].length === 1 ){
- radius = 1;
- }
-
- return {
- x: center.x + radius * Math.cos(theta),
- y: center.y + radius * Math.sin(theta)
- };
-
- } else {
- return {
- x: center.x + (index + 1 - (depthSize + 1)/2) * distanceX,
- y: (depth + 1) * distanceY
- };
- }
- }
-
- };
-
- // get positions in reverse depth order
- var pos = {};
- for( var i = depths.length - 1; i >=0; i-- ){
- var depth = depths[i];
-
- for( var j = 0; j < depth.length; j++ ){
- var node = depth[j];
-
- pos[ node.id() ] = getPosition( node, i === depths.length - 1 );
- }
- }
-
- nodes.layoutPositions(this, options, function(){
- return pos[ this.id() ];
- });
-
- return this; // chaining
-};
-
-module.exports = BreadthFirstLayout;
-
-},{"../../is":77,"../../math":79,"../../util":94}],46:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-var math = _dereq_('../../math');
-var is = _dereq_('../../is');
-
-var defaults = {
- fit: true, // whether to fit the viewport to the graph
- padding: 30, // the padding on fit
- boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
- avoidOverlap: true, // prevents node overlap, may overflow boundingBox and radius if not enough space
- radius: undefined, // the radius of the circle
- startAngle: 3/2 * Math.PI, // where nodes start in radians
- sweep: undefined, // how many radians should be between the first and last node (defaults to full circle)
- clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
- sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
- animate: false, // whether to transition the node positions
- animationDuration: 500, // duration of animation in ms if enabled
- animationEasing: undefined, // easing of animation if enabled
- ready: undefined, // callback on layoutready
- stop: undefined // callback on layoutstop
-};
-
-function CircleLayout( options ){
- this.options = util.extend({}, defaults, options);
-}
-
-CircleLayout.prototype.run = function(){
- var params = this.options;
- var options = params;
-
- var cy = params.cy;
- var eles = options.eles;
-
- var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
-
- var nodes = eles.nodes().not(':parent');
-
- if( options.sort ){
- nodes = nodes.sort( options.sort );
- }
-
- var bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
- x1: 0, y1: 0, w: cy.width(), h: cy.height()
- } );
-
- var center = {
- x: bb.x1 + bb.w/2,
- y: bb.y1 + bb.h/2
- };
-
- var sweep = options.sweep === undefined ? 2*Math.PI - 2*Math.PI/nodes.length : options.sweep;
-
- var dTheta = sweep / ( Math.max(1, nodes.length - 1) );
- var r;
-
- var minDistance = 0;
- for( var i = 0; i < nodes.length; i++ ){
- var n = nodes[i];
- var nbb = n.boundingBox();
- var w = nbb.w;
- var h = nbb.h;
-
- minDistance = Math.max(minDistance, w, h);
- }
-
- if( is.number(options.radius) ){
- r = options.radius;
- } else if( nodes.length <= 1 ){
- r = 0;
- } else {
- r = Math.min( bb.h, bb.w )/2 - minDistance;
- }
-
- // calculate the radius
- if( nodes.length > 1 && options.avoidOverlap ){ // but only if more than one node (can't overlap)
- minDistance *= 1.75; // just to have some nice spacing
-
- var dcos = Math.cos(dTheta) - Math.cos(0);
- var dsin = Math.sin(dTheta) - Math.sin(0);
- var rMin = Math.sqrt( minDistance * minDistance / ( dcos*dcos + dsin*dsin ) ); // s.t. no nodes overlapping
- r = Math.max( rMin, r );
- }
-
- var getPos = function( i, ele ){
- var theta = options.startAngle + i * dTheta * ( clockwise ? 1 : -1 );
-
- var rx = r * Math.cos( theta );
- var ry = r * Math.sin( theta );
- var pos = {
- x: center.x + rx,
- y: center.y + ry
- };
-
- return pos;
- };
-
- nodes.layoutPositions( this, options, getPos );
-
- return this; // chaining
-};
-
-module.exports = CircleLayout;
-
-},{"../../is":77,"../../math":79,"../../util":94}],47:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-var math = _dereq_('../../math');
-
-var defaults = {
- fit: true, // whether to fit the viewport to the graph
- padding: 30, // the padding on fit
- startAngle: 3/2 * Math.PI, // where nodes start in radians
- sweep: undefined, // how many radians should be between the first and last node (defaults to full circle)
- clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
- equidistant: false, // whether levels have an equal radial distance betwen them, may cause bounding box overflow
- minNodeSpacing: 10, // min spacing between outside of nodes (used for radius adjustment)
- boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
- avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space
- height: undefined, // height of layout area (overrides container height)
- width: undefined, // width of layout area (overrides container width)
- concentric: function(node){ // returns numeric value for each node, placing higher nodes in levels towards the centre
- return node.degree();
- },
- levelWidth: function(nodes){ // the variation of concentric values in each level
- return nodes.maxDegree() / 4;
- },
- animate: false, // whether to transition the node positions
- animationDuration: 500, // duration of animation in ms if enabled
- animationEasing: undefined, // easing of animation if enabled
- ready: undefined, // callback on layoutready
- stop: undefined // callback on layoutstop
-};
-
-function ConcentricLayout( options ){
- this.options = util.extend({}, defaults, options);
-}
-
-ConcentricLayout.prototype.run = function(){
- var params = this.options;
- var options = params;
-
- var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
-
- var cy = params.cy;
-
- var eles = options.eles;
- var nodes = eles.nodes().not(':parent');
-
- var bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
- x1: 0, y1: 0, w: cy.width(), h: cy.height()
- } );
-
- var center = {
- x: bb.x1 + bb.w/2,
- y: bb.y1 + bb.h/2
- };
-
- var nodeValues = []; // { node, value }
- var theta = options.startAngle;
- var maxNodeSize = 0;
-
- for( var i = 0; i < nodes.length; i++ ){
- var node = nodes[i];
- var value;
-
- // calculate the node value
- value = options.concentric.apply(node, [ node ]);
- nodeValues.push({
- value: value,
- node: node
- });
-
- // for style mapping
- node._private.scratch.concentric = value;
- }
-
- // in case we used the `concentric` in style
- nodes.updateStyle();
-
- // calculate max size now based on potentially updated mappers
- for( var i = 0; i < nodes.length; i++ ){
- var node = nodes[i];
- var nbb = node.boundingBox();
-
- maxNodeSize = Math.max( maxNodeSize, nbb.w, nbb.h );
- }
-
- // sort node values in descreasing order
- nodeValues.sort(function(a, b){
- return b.value - a.value;
- });
-
- var levelWidth = options.levelWidth( nodes );
-
- // put the values into levels
- var levels = [ [] ];
- var currentLevel = levels[0];
- for( var i = 0; i < nodeValues.length; i++ ){
- var val = nodeValues[i];
-
- if( currentLevel.length > 0 ){
- var diff = Math.abs( currentLevel[0].value - val.value );
-
- if( diff >= levelWidth ){
- currentLevel = [];
- levels.push( currentLevel );
- }
- }
-
- currentLevel.push( val );
- }
-
- // create positions from levels
-
- var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
-
- if( !options.avoidOverlap ){ // then strictly constrain to bb
- var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
- var maxR = ( Math.min(bb.w, bb.h) / 2 - minDist );
- var rStep = maxR / ( levels.length + firstLvlHasMulti ? 1 : 0 );
-
- minDist = Math.min( minDist, rStep );
- }
-
- // find the metrics for each level
- var r = 0;
- for( var i = 0; i < levels.length; i++ ){
- var level = levels[i];
- var sweep = options.sweep === undefined ? 2*Math.PI - 2*Math.PI/level.length : options.sweep;
- var dTheta = level.dTheta = sweep / ( Math.max(1, level.length - 1) );
-
- // calculate the radius
- if( level.length > 1 && options.avoidOverlap ){ // but only if more than one node (can't overlap)
- var dcos = Math.cos(dTheta) - Math.cos(0);
- var dsin = Math.sin(dTheta) - Math.sin(0);
- var rMin = Math.sqrt( minDist * minDist / ( dcos*dcos + dsin*dsin ) ); // s.t. no nodes overlapping
-
- r = Math.max( rMin, r );
- }
-
- level.r = r;
-
- r += minDist;
- }
-
- if( options.equidistant ){
- var rDeltaMax = 0;
- var r = 0;
-
- for( var i = 0; i < levels.length; i++ ){
- var level = levels[i];
- var rDelta = level.r - r;
-
- rDeltaMax = Math.max( rDeltaMax, rDelta );
- }
-
- r = 0;
- for( var i = 0; i < levels.length; i++ ){
- var level = levels[i];
-
- if( i === 0 ){
- r = level.r;
- }
-
- level.r = r;
-
- r += rDeltaMax;
- }
- }
-
- // calculate the node positions
- var pos = {}; // id => position
- for( var i = 0; i < levels.length; i++ ){
- var level = levels[i];
- var dTheta = level.dTheta;
- var r = level.r;
-
- for( var j = 0; j < level.length; j++ ){
- var val = level[j];
- var theta = options.startAngle + (clockwise ? 1 : -1) * dTheta * j;
-
- var p = {
- x: center.x + r * Math.cos(theta),
- y: center.y + r * Math.sin(theta)
- };
-
- pos[ val.node.id() ] = p;
- }
- }
-
- // position the nodes
- nodes.layoutPositions(this, options, function(){
- var id = this.id();
-
- return pos[id];
- });
-
- return this; // chaining
-};
-
-module.exports = ConcentricLayout;
-
-},{"../../math":79,"../../util":94}],48:[function(_dereq_,module,exports){
-'use strict';
-
-/*
-The CoSE layout was written by Gerardo Huck.
-https://www.linkedin.com/in/gerardohuck/
-
-Based on the following article:
-http://dl.acm.org/citation.cfm?id=1498047
-
-Modifications tracked on Github.
-*/
-
-var util = _dereq_('../../util');
-var math = _dereq_('../../math');
-var Thread = _dereq_('../../thread');
-var is = _dereq_('../../is');
-
-var DEBUG;
-
-/**
- * @brief : default layout options
- */
-var defaults = {
- // Called on `layoutready`
- ready : function() {},
-
- // Called on `layoutstop`
- stop : function() {},
-
- // Whether to animate while running the layout
- animate : true,
-
- // The layout animates only after this many milliseconds
- // (prevents flashing on fast runs)
- animationThreshold : 250,
-
- // Number of iterations between consecutive screen positions update
- // (0 -> only updated on the end)
- refresh : 20,
-
- // Whether to fit the network view after when done
- fit : true,
-
- // Padding on fit
- padding : 30,
-
- // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
- boundingBox : undefined,
-
- // Extra spacing between components in non-compound graphs
- componentSpacing : 100,
-
- // Node repulsion (non overlapping) multiplier
- nodeRepulsion : function( node ){ return 400000; },
-
- // Node repulsion (overlapping) multiplier
- nodeOverlap : 10,
-
- // Ideal edge (non nested) length
- idealEdgeLength : function( edge ){ return 10; },
-
- // Divisor to compute edge forces
- edgeElasticity : function( edge ){ return 100; },
-
- // Nesting factor (multiplier) to compute ideal edge length for nested edges
- nestingFactor : 5,
-
- // Gravity force (constant)
- gravity : 80,
-
- // Maximum number of iterations to perform
- numIter : 1000,
-
- // Initial temperature (maximum node displacement)
- initialTemp : 200,
-
- // Cooling factor (how the temperature is reduced between consecutive iterations
- coolingFactor : 0.95,
-
- // Lower temperature threshold (below this point the layout will end)
- minTemp : 1.0,
-
- // Whether to use threading to speed up the layout
- useMultitasking : true
-};
-
-
-/**
- * @brief : constructor
- * @arg options : object containing layout options
- */
-function CoseLayout(options) {
- this.options = util.extend({}, defaults, options);
-
- this.options.layout = this;
-}
-
-
-/**
- * @brief : runs the layout
- */
-CoseLayout.prototype.run = function() {
- var options = this.options;
- var cy = options.cy;
- var layout = this;
- var thread = this.thread;
-
- if( !thread || thread.stopped() ){
- thread = this.thread = Thread({ disabled: !options.useMultitasking });
- }
-
- layout.stopped = false;
-
- layout.trigger({ type: 'layoutstart', layout: layout });
-
- // Set DEBUG - Global variable
- if (true === options.debug) {
- DEBUG = true;
- } else {
- DEBUG = false;
- }
-
- // Initialize layout info
- var layoutInfo = createLayoutInfo(cy, layout, options);
-
- // Show LayoutInfo contents if debugging
- if (DEBUG) {
- printLayoutInfo(layoutInfo);
- }
-
- // If required, randomize node positions
- // if (true === options.randomize) {
- randomizePositions(layoutInfo, cy);
- // }
-
- var startTime = Date.now();
- var refreshRequested = false;
- var refresh = function( rOpts ){
- rOpts = rOpts || {};
-
- if( refreshRequested ){
- return;
- }
-
- if( !rOpts.force && Date.now() - startTime < options.animationThreshold ){
- return;
- }
-
- refreshRequested = true;
-
- util.requestAnimationFrame(function(){
- refreshPositions(layoutInfo, cy, options);
-
- // Fit the graph if necessary
- if (true === options.fit) {
- cy.fit( options.padding );
- }
-
- refreshRequested = false;
- });
- };
-
- thread.on('message', function( e ){
- var layoutNodes = e.message;
-
- layoutInfo.layoutNodes = layoutNodes;
- refresh();
- });
-
- thread.pass({
- layoutInfo: layoutInfo,
- options: {
- animate: options.animate,
- refresh: options.refresh,
- componentSpacing: options.componentSpacing,
- nodeOverlap: options.nodeOverlap,
- nestingFactor: options.nestingFactor,
- gravity: options.gravity,
- numIter: options.numIter,
- initialTemp: options.initialTemp,
- coolingFactor: options.coolingFactor,
- minTemp: options.minTemp
- }
- }).run(function( pass ){
- var layoutInfo = pass.layoutInfo;
- var options = pass.options;
- var stopped = false;
-
- /**
- * @brief : Performs one iteration of the physical simulation
- * @arg layoutInfo : LayoutInfo object already initialized
- * @arg cy : Cytoscape object
- * @arg options : Layout options
- */
- var step = function(layoutInfo, options, step) {
- // var s = "\n\n###############################";
- // s += "\nSTEP: " + step;
- // s += "\n###############################\n";
- // logDebug(s);
-
- // Calculate node repulsions
- calculateNodeForces(layoutInfo, options);
- // Calculate edge forces
- calculateEdgeForces(layoutInfo, options);
- // Calculate gravity forces
- calculateGravityForces(layoutInfo, options);
- // Propagate forces from parent to child
- propagateForces(layoutInfo, options);
- // Update positions based on calculated forces
- updatePositions(layoutInfo, options);
- };
-
- /**
- * @brief : Computes the node repulsion forces
- */
- var calculateNodeForces = function(layoutInfo, options) {
- // Go through each of the graphs in graphSet
- // Nodes only repel each other if they belong to the same graph
- // var s = 'calculateNodeForces';
- // logDebug(s);
- for (var i = 0; i < layoutInfo.graphSet.length; i ++) {
- var graph = layoutInfo.graphSet[i];
- var numNodes = graph.length;
-
- // s = "Set: " + graph.toString();
- // logDebug(s);
-
- // Now get all the pairs of nodes
- // Only get each pair once, (A, B) = (B, A)
- for (var j = 0; j < numNodes; j++) {
- var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
-
- for (var k = j + 1; k < numNodes; k++) {
- var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
-
- nodeRepulsion(node1, node2, layoutInfo, options);
- }
- }
- }
- };
-
- /**
- * @brief : Compute the node repulsion forces between a pair of nodes
- */
- var nodeRepulsion = function(node1, node2, layoutInfo, options) {
- // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
-
- var cmptId1 = node1.cmptId;
- var cmptId2 = node2.cmptId;
-
- if( cmptId1 !== cmptId2 && !layoutInfo.isCompound ){ return; }
-
- // Get direction of line connecting both node centers
- var directionX = node2.positionX - node1.positionX;
- var directionY = node2.positionY - node1.positionY;
- // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
-
- // If both centers are the same, apply a random force
- if (0 === directionX && 0 === directionY) {
- // s += "\nNodes have the same position.";
- return; // TODO could be improved with random force
- }
-
- var overlap = nodesOverlap(node1, node2, directionX, directionY);
-
- if (overlap > 0) {
- // s += "\nNodes DO overlap.";
- // s += "\nOverlap: " + overlap;
- // If nodes overlap, repulsion force is proportional
- // to the overlap
- var force = options.nodeOverlap * overlap;
-
- // Compute the module and components of the force vector
- var distance = Math.sqrt(directionX * directionX + directionY * directionY);
- // s += "\nDistance: " + distance;
- var forceX = force * directionX / distance;
- var forceY = force * directionY / distance;
-
- } else {
- // s += "\nNodes do NOT overlap.";
- // If there's no overlap, force is inversely proportional
- // to squared distance
-
- // Get clipping points for both nodes
- var point1 = findClippingPoint(node1, directionX, directionY);
- var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY);
-
- // Use clipping points to compute distance
- var distanceX = point2.x - point1.x;
- var distanceY = point2.y - point1.y;
- var distanceSqr = distanceX * distanceX + distanceY * distanceY;
- var distance = Math.sqrt(distanceSqr);
- // s += "\nDistance: " + distance;
-
- // Compute the module and components of the force vector
- var force = ( node1.nodeRepulsion + node2.nodeRepulsion ) / distanceSqr;
- var forceX = force * distanceX / distance;
- var forceY = force * distanceY / distance;
- }
-
- // Apply force
- if( !node1.isLocked ){
- node1.offsetX -= forceX;
- node1.offsetY -= forceY;
- }
-
- if( !node2.isLocked ){
- node2.offsetX += forceX;
- node2.offsetY += forceY;
- }
-
- // s += "\nForceX: " + forceX + " ForceY: " + forceY;
- // logDebug(s);
-
- return;
- };
-
- /**
- * @brief : Determines whether two nodes overlap or not
- * @return : Amount of overlapping (0 => no overlap)
- */
- var nodesOverlap = function(node1, node2, dX, dY) {
-
- if (dX > 0) {
- var overlapX = node1.maxX - node2.minX;
- } else {
- var overlapX = node2.maxX - node1.minX;
- }
-
- if (dY > 0) {
- var overlapY = node1.maxY - node2.minY;
- } else {
- var overlapY = node2.maxY - node1.minY;
- }
-
- if (overlapX >= 0 && overlapY >= 0) {
- return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
- } else {
- return 0;
- }
- };
-
- /**
- * @brief : Finds the point in which an edge (direction dX, dY) intersects
- * the rectangular bounding box of it's source/target node
- */
- var findClippingPoint = function(node, dX, dY) {
-
- // Shorcuts
- var X = node.positionX;
- var Y = node.positionY;
- var H = node.height || 1;
- var W = node.width || 1;
- var dirSlope = dY / dX;
- var nodeSlope = H / W;
-
- // var s = 'Computing clipping point of node ' + node.id +
- // " . Height: " + H + ", Width: " + W +
- // "\nDirection " + dX + ", " + dY;
- //
- // Compute intersection
- var res = {};
- do {
- // Case: Vertical direction (up)
- if (0 === dX && 0 < dY) {
- res.x = X;
- // s += "\nUp direction";
- res.y = Y + H / 2;
- break;
- }
-
- // Case: Vertical direction (down)
- if (0 === dX && 0 > dY) {
- res.x = X;
- res.y = Y + H / 2;
- // s += "\nDown direction";
- break;
- }
-
- // Case: Intersects the right border
- if (0 < dX &&
- -1 * nodeSlope <= dirSlope &&
- dirSlope <= nodeSlope) {
- res.x = X + W / 2;
- res.y = Y + (W * dY / 2 / dX);
- // s += "\nRightborder";
- break;
- }
-
- // Case: Intersects the left border
- if (0 > dX &&
- -1 * nodeSlope <= dirSlope &&
- dirSlope <= nodeSlope) {
- res.x = X - W / 2;
- res.y = Y - (W * dY / 2 / dX);
- // s += "\nLeftborder";
- break;
- }
-
- // Case: Intersects the top border
- if (0 < dY &&
- ( dirSlope <= -1 * nodeSlope ||
- dirSlope >= nodeSlope )) {
- res.x = X + (H * dX / 2 / dY);
- res.y = Y + H / 2;
- // s += "\nTop border";
- break;
- }
-
- // Case: Intersects the bottom border
- if (0 > dY &&
- ( dirSlope <= -1 * nodeSlope ||
- dirSlope >= nodeSlope )) {
- res.x = X - (H * dX / 2 / dY);
- res.y = Y - H / 2;
- // s += "\nBottom border";
- break;
- }
-
- } while (false);
-
- // s += "\nClipping point found at " + res.x + ", " + res.y;
- // logDebug(s);
- return res;
- };
-
- /**
- * @brief : Calculates all edge forces
- */
- var calculateEdgeForces = function(layoutInfo, options) {
- // Iterate over all edges
- for (var i = 0; i < layoutInfo.edgeSize; i++) {
- // Get edge, source & target nodes
- var edge = layoutInfo.layoutEdges[i];
- var sourceIx = layoutInfo.idToIndex[edge.sourceId];
- var source = layoutInfo.layoutNodes[sourceIx];
- var targetIx = layoutInfo.idToIndex[edge.targetId];
- var target = layoutInfo.layoutNodes[targetIx];
-
- // Get direction of line connecting both node centers
- var directionX = target.positionX - source.positionX;
- var directionY = target.positionY - source.positionY;
-
- // If both centers are the same, do nothing.
- // A random force has already been applied as node repulsion
- if (0 === directionX && 0 === directionY) {
- return;
- }
-
- // Get clipping points for both nodes
- var point1 = findClippingPoint(source, directionX, directionY);
- var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
-
-
- var lx = point2.x - point1.x;
- var ly = point2.y - point1.y;
- var l = Math.sqrt(lx * lx + ly * ly);
-
- var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
-
- if (0 !== l) {
- var forceX = force * lx / l;
- var forceY = force * ly / l;
- } else {
- var forceX = 0;
- var forceY = 0;
- }
-
- // Add this force to target and source nodes
- if( !source.isLocked ){
- source.offsetX += forceX;
- source.offsetY += forceY;
- }
-
- if( !target.isLocked ){
- target.offsetX -= forceX;
- target.offsetY -= forceY;
- }
-
- // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
- // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
- // logDebug(s);
- }
- };
-
- /**
- * @brief : Computes gravity forces for all nodes
- */
- var calculateGravityForces = function(layoutInfo, options) {
- var distThreshold = 1;
-
- // var s = 'calculateGravityForces';
- // logDebug(s);
- for (var i = 0; i < layoutInfo.graphSet.length; i ++) {
- var graph = layoutInfo.graphSet[i];
- var numNodes = graph.length;
-
- // s = "Set: " + graph.toString();
- // logDebug(s);
-
- // Compute graph center
- if (0 === i) {
- var centerX = layoutInfo.clientHeight / 2;
- var centerY = layoutInfo.clientWidth / 2;
- } else {
- // Get Parent node for this graph, and use its position as center
- var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
- var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
- var centerX = parent.positionX;
- var centerY = parent.positionY;
- }
- // s = "Center found at: " + centerX + ", " + centerY;
- // logDebug(s);
-
- // Apply force to all nodes in graph
- for (var j = 0; j < numNodes; j++) {
- var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
- // s = "Node: " + node.id;
-
- if( node.isLocked ){ continue; }
-
- var dx = centerX - node.positionX;
- var dy = centerY - node.positionY;
- var d = Math.sqrt(dx * dx + dy * dy);
- if (d > distThreshold) {
- var fx = options.gravity * dx / d;
- var fy = options.gravity * dy / d;
- node.offsetX += fx;
- node.offsetY += fy;
- // s += ": Applied force: " + fx + ", " + fy;
- } else {
- // s += ": skypped since it's too close to center";
- }
- // logDebug(s);
- }
- }
- };
-
- /**
- * @brief : This function propagates the existing offsets from
- * parent nodes to its descendents.
- * @arg layoutInfo : layoutInfo Object
- * @arg cy : cytoscape Object
- * @arg options : Layout options
- */
- var propagateForces = function(layoutInfo, options) {
- // Inline implementation of a queue, used for traversing the graph in BFS order
- var queue = [];
- var start = 0; // Points to the start the queue
- var end = -1; // Points to the end of the queue
-
- // logDebug('propagateForces');
-
- // Start by visiting the nodes in the root graph
- queue.push.apply(queue, layoutInfo.graphSet[0]);
- end += layoutInfo.graphSet[0].length;
-
- // Traverse the graph, level by level,
- while (start <= end) {
- // Get the node to visit and remove it from queue
- var nodeId = queue[start++];
- var nodeIndex = layoutInfo.idToIndex[nodeId];
- var node = layoutInfo.layoutNodes[nodeIndex];
- var children = node.children;
-
- // We only need to process the node if it's compound
- if (0 < children.length && !node.isLocked) {
- var offX = node.offsetX;
- var offY = node.offsetY;
-
- // var s = "Propagating offset from parent node : " + node.id +
- // ". OffsetX: " + offX + ". OffsetY: " + offY;
- // s += "\n Children: " + children.toString();
- // logDebug(s);
-
- for (var i = 0; i < children.length; i++) {
- var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]];
- // Propagate offset
- childNode.offsetX += offX;
- childNode.offsetY += offY;
- // Add children to queue to be visited
- queue[++end] = children[i];
- }
-
- // Reset parent offsets
- node.offsetX = 0;
- node.offsetY = 0;
- }
-
- }
- };
-
- /**
- * @brief : Updates the layout model positions, based on
- * the accumulated forces
- */
- var updatePositions = function(layoutInfo, options) {
- // var s = 'Updating positions';
- // logDebug(s);
-
- // Reset boundaries for compound nodes
- for (var i = 0; i < layoutInfo.nodeSize; i++) {
- var n = layoutInfo.layoutNodes[i];
- if (0 < n.children.length) {
- // logDebug("Resetting boundaries of compound node: " + n.id);
- n.maxX = undefined;
- n.minX = undefined;
- n.maxY = undefined;
- n.minY = undefined;
- }
- }
-
- for (var i = 0; i < layoutInfo.nodeSize; i++) {
- var n = layoutInfo.layoutNodes[i];
- if (0 < n.children.length || n.isLocked) {
- // No need to set compound or locked node position
- // logDebug("Skipping position update of node: " + n.id);
- continue;
- }
- // s = "Node: " + n.id + " Previous position: (" +
- // n.positionX + ", " + n.positionY + ").";
-
- // Limit displacement in order to improve stability
- var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
- n.positionX += tempForce.x;
- n.positionY += tempForce.y;
- n.offsetX = 0;
- n.offsetY = 0;
- n.minX = n.positionX - n.width;
- n.maxX = n.positionX + n.width;
- n.minY = n.positionY - n.height;
- n.maxY = n.positionY + n.height;
- // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
- // logDebug(s);
-
- // Update ancestry boudaries
- updateAncestryBoundaries(n, layoutInfo);
- }
-
- // Update size, position of compund nodes
- for (var i = 0; i < layoutInfo.nodeSize; i++) {
- var n = layoutInfo.layoutNodes[i];
- if ( 0 < n.children.length && !n.isLocked ) {
- n.positionX = (n.maxX + n.minX) / 2;
- n.positionY = (n.maxY + n.minY) / 2;
- n.width = n.maxX - n.minX;
- n.height = n.maxY - n.minY;
- // s = "Updating position, size of compound node " + n.id;
- // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
- // s += "\nWidth: " + n.width + ", Height: " + n.height;
- // logDebug(s);
- }
- }
- };
-
- /**
- * @brief : Limits a force (forceX, forceY) to be not
- * greater (in modulo) than max.
- 8 Preserves force direction.
- */
- var limitForce = function(forceX, forceY, max) {
- // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
- var force = Math.sqrt(forceX * forceX + forceY * forceY);
-
- if (force > max) {
- var res = {
- x : max * forceX / force,
- y : max * forceY / force
- };
-
- } else {
- var res = {
- x : forceX,
- y : forceY
- };
- }
-
- // s += ".\nResult: (" + res.x + ", " + res.y + ")";
- // logDebug(s);
-
- return res;
- };
-
- /**
- * @brief : Function used for keeping track of compound node
- * sizes, since they should bound all their subnodes.
- */
- var updateAncestryBoundaries = function(node, layoutInfo) {
- // var s = "Propagating new position/size of node " + node.id;
- var parentId = node.parentId;
- if (null == parentId) {
- // If there's no parent, we are done
- // s += ". No parent node.";
- // logDebug(s);
- return;
- }
-
- // Get Parent Node
- var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
- var flag = false;
-
- // MaxX
- if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
- p.maxX = node.maxX + p.padRight;
- flag = true;
- // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
- }
-
- // MinX
- if (null == p.minX || node.minX - p.padLeft < p.minX) {
- p.minX = node.minX - p.padLeft;
- flag = true;
- // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
- }
-
- // MaxY
- if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
- p.maxY = node.maxY + p.padBottom;
- flag = true;
- // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
- }
-
- // MinY
- if (null == p.minY || node.minY - p.padTop < p.minY) {
- p.minY = node.minY - p.padTop;
- flag = true;
- // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
- }
-
- // If updated boundaries, propagate changes upward
- if (flag) {
- // logDebug(s);
- return updateAncestryBoundaries(p, layoutInfo);
- }
-
- // s += ". No changes in boundaries/position of parent node " + p.id;
- // logDebug(s);
- return;
- };
-
- var separateComponents = function(layutInfo, options){
- var nodes = layoutInfo.layoutNodes;
- var components = [];
-
- for( var i = 0; i < nodes.length; i++ ){
- var node = nodes[i];
- var cid = node.cmptId;
- var component = components[ cid ] = components[ cid ] || [];
-
- component.push( node );
- }
-
- var totalA = 0;
-
- for( var i = 0; i < components.length; i++ ){
- var c = components[i];
- c.x1 = Infinity;
- c.x2 = -Infinity;
- c.y1 = Infinity;
- c.y2 = -Infinity;
-
- for( var j = 0; j < c.length; j++ ){
- var n = c[j];
-
- c.x1 = Math.min( c.x1, n.positionX - n.width/2 );
- c.x2 = Math.max( c.x2, n.positionX + n.width/2 );
- c.y1 = Math.min( c.y1, n.positionY - n.height/2 );
- c.y2 = Math.max( c.y2, n.positionY + n.height/2 );
- }
-
- c.w = c.x2 - c.x1;
- c.h = c.y2 - c.y1;
-
- totalA += c.w * c.h;
- }
-
- components.sort(function( c1, c2 ){
- return c2.w*c2.h - c1.w*c1.h;
- });
-
- var x = 0;
- var y = 0;
- var usedW = 0;
- var rowH = 0;
- var maxRowW = Math.sqrt( totalA ) * layoutInfo.clientWidth / layoutInfo.clientHeight;
-
- for( var i = 0; i < components.length; i++ ){
- var c = components[i];
-
- for( var j = 0; j < c.length; j++ ){
- var n = c[j];
-
- if( !n.isLocked ){
- n.positionX += x;
- n.positionY += y;
- }
- }
-
- x += c.w + options.componentSpacing;
- usedW += c.w + options.componentSpacing;
- rowH = Math.max( rowH, c.h );
-
- if( usedW > maxRowW ){
- y += rowH + options.componentSpacing;
- x = 0;
- usedW = 0;
- rowH = 0;
- }
- }
- };
-
- var mainLoop = function(i){
- if( stopped ){
- // logDebug("Layout manually stopped. Stopping computation in step " + i);
- return false;
- }
-
- // Do one step in the phisical simulation
- step(layoutInfo, options, i);
-
- // Update temperature
- layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor;
- // logDebug("New temperature: " + layoutInfo.temperature);
-
- if (layoutInfo.temperature < options.minTemp) {
- // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
- return false;
- }
-
- return true;
- };
-
- var i = 0;
- var loopRet;
-
- do {
- var f = 0;
-
- while( f < options.refresh && i < options.numIter ){
- var loopRet = mainLoop(i);
- if( !loopRet ){ break; }
-
- f++;
- i++;
- }
-
- if( options.animate ){
- broadcast( layoutInfo.layoutNodes ); // jshint ignore:line
- }
-
- } while ( loopRet && i + 1 < options.numIter );
-
- separateComponents( layoutInfo, options );
-
- return layoutInfo;
- }).then(function( layoutInfoUpdated ){
- layoutInfo.layoutNodes = layoutInfoUpdated.layoutNodes; // get the positions
-
- thread.stop();
- done();
- });
-
- var done = function(){
- refresh({ force: true });
-
- // Layout has finished
- layout.one('layoutstop', options.stop);
- layout.trigger({ type: 'layoutstop', layout: layout });
- };
-
- return this; // chaining
-};
-
-
-/**
- * @brief : called on continuous layouts to stop them before they finish
- */
-CoseLayout.prototype.stop = function(){
- this.stopped = true;
-
- if( this.thread ){
- this.thread.stop();
- }
-
- this.trigger('layoutstop');
-
- return this; // chaining
-};
-
-CoseLayout.prototype.destroy = function(){
- if( this.thread ){
- this.thread.stop();
- }
-
- return this; // chaining
-};
-
-
-/**
- * @brief : Creates an object which is contains all the data
- * used in the layout process
- * @arg cy : cytoscape.js object
- * @return : layoutInfo object initialized
- */
-var createLayoutInfo = function(cy, layout, options) {
- // Shortcut
- var edges = options.eles.edges();
- var nodes = options.eles.nodes();
-
- var layoutInfo = {
- isCompound : cy.hasCompoundNodes(),
- layoutNodes : [],
- idToIndex : {},
- nodeSize : nodes.size(),
- graphSet : [],
- indexToGraph : [],
- layoutEdges : [],
- edgeSize : edges.size(),
- temperature : options.initialTemp,
- clientWidth : cy.width(),
- clientHeight : cy.width(),
- boundingBox : math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
- x1: 0, y1: 0, w: cy.width(), h: cy.height()
- } )
- };
-
- var components = options.eles.components();
- var id2cmptId = {};
-
- for( var i = 0; i < components.length; i++ ){
- var component = components[i];
-
- for( var j = 0; j < component.length; j++ ){
- var node = component[j];
-
- id2cmptId[ node.id() ] = i;
- }
- }
-
- // Iterate over all nodes, creating layout nodes
- for (var i = 0; i < layoutInfo.nodeSize; i++) {
- var n = nodes[i];
- var nbb = n.boundingBox();
-
- var tempNode = {};
- tempNode.isLocked = n.locked();
- tempNode.id = n.data('id');
- tempNode.parentId = n.data('parent');
- tempNode.cmptId = id2cmptId[ n.id() ];
- tempNode.children = [];
- tempNode.positionX = n.position('x');
- tempNode.positionY = n.position('y');
- tempNode.offsetX = 0;
- tempNode.offsetY = 0;
- tempNode.height = nbb.w;
- tempNode.width = nbb.h;
- tempNode.maxX = tempNode.positionX + tempNode.width / 2;
- tempNode.minX = tempNode.positionX - tempNode.width / 2;
- tempNode.maxY = tempNode.positionY + tempNode.height / 2;
- tempNode.minY = tempNode.positionY - tempNode.height / 2;
- tempNode.padLeft = parseFloat( n.style('padding-left') );
- tempNode.padRight = parseFloat( n.style('padding-right') );
- tempNode.padTop = parseFloat( n.style('padding-top') );
- tempNode.padBottom = parseFloat( n.style('padding-bottom') );
-
- // forces
- tempNode.nodeRepulsion = is.fn( options.nodeRepulsion ) ? options.nodeRepulsion.call( n, n ) : options.nodeRepulsion;
-
- // Add new node
- layoutInfo.layoutNodes.push(tempNode);
- // Add entry to id-index map
- layoutInfo.idToIndex[tempNode.id] = i;
- }
-
- // Inline implementation of a queue, used for traversing the graph in BFS order
- var queue = [];
- var start = 0; // Points to the start the queue
- var end = -1; // Points to the end of the queue
-
- var tempGraph = [];
-
- // Second pass to add child information and
- // initialize queue for hierarchical traversal
- for (var i = 0; i < layoutInfo.nodeSize; i++) {
- var n = layoutInfo.layoutNodes[i];
- var p_id = n.parentId;
- // Check if node n has a parent node
- if (null != p_id) {
- // Add node Id to parent's list of children
- layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
- } else {
- // If a node doesn't have a parent, then it's in the root graph
- queue[++end] = n.id;
- tempGraph.push(n.id);
- }
- }
-
- // Add root graph to graphSet
- layoutInfo.graphSet.push(tempGraph);
-
- // Traverse the graph, level by level,
- while (start <= end) {
- // Get the node to visit and remove it from queue
- var node_id = queue[start++];
- var node_ix = layoutInfo.idToIndex[node_id];
- var node = layoutInfo.layoutNodes[node_ix];
- var children = node.children;
- if (children.length > 0) {
- // Add children nodes as a new graph to graph set
- layoutInfo.graphSet.push(children);
- // Add children to que queue to be visited
- for (var i = 0; i < children.length; i++) {
- queue[++end] = children[i];
- }
- }
- }
-
- // Create indexToGraph map
- for (var i = 0; i < layoutInfo.graphSet.length; i++) {
- var graph = layoutInfo.graphSet[i];
- for (var j = 0; j < graph.length; j++) {
- var index = layoutInfo.idToIndex[graph[j]];
- layoutInfo.indexToGraph[index] = i;
- }
- }
-
- // Iterate over all edges, creating Layout Edges
- for (var i = 0; i < layoutInfo.edgeSize; i++) {
- var e = edges[i];
- var tempEdge = {};
- tempEdge.id = e.data('id');
- tempEdge.sourceId = e.data('source');
- tempEdge.targetId = e.data('target');
-
- // Compute ideal length
- var idealLength = is.fn( options.idealEdgeLength ) ? options.idealEdgeLength.call( e, e ) : options.idealEdgeLength;
- var elasticity = is.fn( options.edgeElasticity ) ? options.edgeElasticity.call( e, e ) : options.edgeElasticity;
-
- // Check if it's an inter graph edge
- var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
- var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
- var sourceGraph = layoutInfo.indexToGraph[sourceIx];
- var targetGraph = layoutInfo.indexToGraph[targetIx];
-
- if (sourceGraph != targetGraph) {
- // Find lowest common graph ancestor
- var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo);
-
- // Compute sum of node depths, relative to lca graph
- var lcaGraph = layoutInfo.graphSet[lca];
- var depth = 0;
-
- // Source depth
- var tempNode = layoutInfo.layoutNodes[sourceIx];
- while ( -1 === lcaGraph.indexOf(tempNode.id) ) {
- tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
- depth++;
- }
-
- // Target depth
- tempNode = layoutInfo.layoutNodes[targetIx];
- while ( -1 === lcaGraph.indexOf(tempNode.id) ) {
- tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
- depth++;
- }
-
- // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
- // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
- // ". Depth: " + depth);
-
- // Update idealLength
- idealLength *= depth * options.nestingFactor;
- }
-
- tempEdge.idealLength = idealLength;
- tempEdge.elasticity = elasticity;
-
- layoutInfo.layoutEdges.push(tempEdge);
- }
-
- // Finally, return layoutInfo object
- return layoutInfo;
-};
-
-
-/**
- * @brief : This function finds the index of the lowest common
- * graph ancestor between 2 nodes in the subtree
- * (from the graph hierarchy induced tree) whose
- * root is graphIx
- *
- * @arg node1: node1's ID
- * @arg node2: node2's ID
- * @arg layoutInfo: layoutInfo object
- *
- */
-var findLCA = function(node1, node2, layoutInfo) {
- // Find their common ancester, starting from the root graph
- var res = findLCA_aux(node1, node2, 0, layoutInfo);
- if (2 > res.count) {
- // If aux function couldn't find the common ancester,
- // then it is the root graph
- return 0;
- } else {
- return res.graph;
- }
-};
-
-
-/**
- * @brief : Auxiliary function used for LCA computation
- *
- * @arg node1 : node1's ID
- * @arg node2 : node2's ID
- * @arg graphIx : subgraph index
- * @arg layoutInfo : layoutInfo object
- *
- * @return : object of the form {count: X, graph: Y}, where:
- * X is the number of ancesters (max: 2) found in
- * graphIx (and it's subgraphs),
- * Y is the graph index of the lowest graph containing
- * all X nodes
- */
-var findLCA_aux = function(node1, node2, graphIx, layoutInfo) {
- var graph = layoutInfo.graphSet[graphIx];
- // If both nodes belongs to graphIx
- if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
- return {count:2, graph:graphIx};
- }
-
- // Make recursive calls for all subgraphs
- var c = 0;
- for (var i = 0; i < graph.length; i++) {
- var nodeId = graph[i];
- var nodeIx = layoutInfo.idToIndex[nodeId];
- var children = layoutInfo.layoutNodes[nodeIx].children;
-
- // If the node has no child, skip it
- if (0 === children.length) {
- continue;
- }
-
- var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
- var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
- if (0 === result.count) {
- // Neither node1 nor node2 are present in this subgraph
- continue;
- } else if (1 === result.count) {
- // One of (node1, node2) is present in this subgraph
- c++;
- if (2 === c) {
- // We've already found both nodes, no need to keep searching
- break;
- }
- } else {
- // Both nodes are present in this subgraph
- return result;
- }
- }
-
- return {count:c, graph:graphIx};
-};
-
-
-/**
- * @brief: printsLayoutInfo into js console
- * Only used for debbuging
- */
-var printLayoutInfo = function(layoutInfo) {
- /* jshint ignore:start */
-
- if (!DEBUG) {
- return;
- }
- console.debug("layoutNodes:");
- for (var i = 0; i < layoutInfo.nodeSize; i++) {
- var n = layoutInfo.layoutNodes[i];
- var s =
- "\nindex: " + i +
- "\nId: " + n.id +
- "\nChildren: " + n.children.toString() +
- "\nparentId: " + n.parentId +
- "\npositionX: " + n.positionX +
- "\npositionY: " + n.positionY +
- "\nOffsetX: " + n.offsetX +
- "\nOffsetY: " + n.offsetY +
- "\npadLeft: " + n.padLeft +
- "\npadRight: " + n.padRight +
- "\npadTop: " + n.padTop +
- "\npadBottom: " + n.padBottom;
-
- console.debug(s);
- }
-
- console.debug('idToIndex');
- for (var i in layoutInfo.idToIndex) {
- console.debug("Id: " + i + "\nIndex: " + layoutInfo.idToIndex[i]);
- }
-
- console.debug('Graph Set');
- var set = layoutInfo.graphSet;
- for (var i = 0; i < set.length; i ++) {
- console.debug("Set : " + i + ": " + set[i].toString());
- }
-
- var s = 'IndexToGraph';
- for (var i = 0; i < layoutInfo.indexToGraph.length; i ++) {
- s += "\nIndex : " + i + " Graph: "+ layoutInfo.indexToGraph[i];
- }
- console.debug(s);
-
- s = 'Layout Edges';
- for (var i = 0; i < layoutInfo.layoutEdges.length; i++) {
- var e = layoutInfo.layoutEdges[i];
- s += "\nEdge Index: " + i + " ID: " + e.id +
- " SouceID: " + e.sourceId + " TargetId: " + e.targetId +
- " Ideal Length: " + e.idealLength;
- }
- console.debug(s);
-
- s = "nodeSize: " + layoutInfo.nodeSize;
- s += "\nedgeSize: " + layoutInfo.edgeSize;
- s += "\ntemperature: " + layoutInfo.temperature;
- console.debug(s);
-
- return;
- /* jshint ignore:end */
-};
-
-
-/**
- * @brief : Randomizes the position of all nodes
- */
-var randomizePositions = function(layoutInfo, cy) {
- var width = layoutInfo.clientWidth;
- var height = layoutInfo.clientHeight;
-
- for (var i = 0; i < layoutInfo.nodeSize; i++) {
- var n = layoutInfo.layoutNodes[i];
-
- // No need to randomize compound nodes or locked nodes
- if ( 0 === n.children.length && !n.isLocked ) {
- n.positionX = Math.random() * width;
- n.positionY = Math.random() * height;
- }
- }
-};
-
-
-/**
- * @brief : Updates the positions of nodes in the network
- * @arg layoutInfo : LayoutInfo object
- * @arg cy : Cytoscape object
- * @arg options : Layout options
- */
-var refreshPositions = function(layoutInfo, cy, options) {
- // var s = 'Refreshing positions';
- // logDebug(s);
-
- var layout = options.layout;
- var nodes = options.eles.nodes();
- var bb = layoutInfo.boundingBox;
- var coseBB = { x1: Infinity, x2: -Infinity, y1: Infinity, y2: -Infinity };
-
- if( options.boundingBox ){
- nodes.forEach(function( node ){
- var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
-
- coseBB.x1 = Math.min( coseBB.x1, lnode.positionX );
- coseBB.x2 = Math.max( coseBB.x2, lnode.positionX );
-
- coseBB.y1 = Math.min( coseBB.y1, lnode.positionY );
- coseBB.y2 = Math.max( coseBB.y2, lnode.positionY );
- });
-
- coseBB.w = coseBB.x2 - coseBB.x1;
- coseBB.h = coseBB.y2 - coseBB.y1;
- }
-
- nodes.positions(function(i, ele) {
- var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
- // s = "Node: " + lnode.id + ". Refreshed position: (" +
- // lnode.positionX + ", " + lnode.positionY + ").";
- // logDebug(s);
-
- if( options.boundingBox ){ // then add extra bounding box constraint
- var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
- var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
-
- return {
- x: bb.x1 + pctX * bb.w,
- y: bb.y1 + pctY * bb.h
- };
- } else {
- return {
- x: lnode.positionX,
- y: lnode.positionY
- };
- }
- });
-
- // Trigger layoutReady only on first call
- if (true !== layoutInfo.ready) {
- // s = 'Triggering layoutready';
- // logDebug(s);
- layoutInfo.ready = true;
- layout.one('layoutready', options.ready);
- layout.trigger({ type: 'layoutready', layout: this });
- }
-};
-
-/**
- * @brief : Logs a debug message in JS console, if DEBUG is ON
- */
-// var logDebug = function(text) {
-// if (DEBUG) {
-// console.debug(text);
-// }
-// };
-
-module.exports = CoseLayout;
-
-},{"../../is":77,"../../math":79,"../../thread":92,"../../util":94}],49:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-var math = _dereq_('../../math');
-
-var defaults = {
- fit: true, // whether to fit the viewport to the graph
- padding: 30, // padding used on fit
- boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
- avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space
- avoidOverlapPadding: 10, // extra spacing around nodes when avoidOverlap: true
- condense: false, // uses all available space on false, uses minimal space on true
- rows: undefined, // force num of rows in the grid
- cols: undefined, // force num of columns in the grid
- position: function( node ){}, // returns { row, col } for element
- sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
- animate: false, // whether to transition the node positions
- animationDuration: 500, // duration of animation in ms if enabled
- animationEasing: undefined, // easing of animation if enabled
- ready: undefined, // callback on layoutready
- stop: undefined // callback on layoutstop
-};
-
-function GridLayout( options ){
- this.options = util.extend({}, defaults, options);
-}
-
-GridLayout.prototype.run = function(){
- var params = this.options;
- var options = params;
-
- var cy = params.cy;
- var eles = options.eles;
- var nodes = eles.nodes().not(':parent');
-
- if( options.sort ){
- nodes = nodes.sort( options.sort );
- }
-
- var bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
- x1: 0, y1: 0, w: cy.width(), h: cy.height()
- } );
-
- if( bb.h === 0 || bb.w === 0){
- nodes.layoutPositions(this, options, function(){
- return { x: bb.x1, y: bb.y1 };
- });
-
- } else {
-
- // width/height * splits^2 = cells where splits is number of times to split width
- var cells = nodes.size();
- var splits = Math.sqrt( cells * bb.h/bb.w );
- var rows = Math.round( splits );
- var cols = Math.round( bb.w/bb.h * splits );
-
- var small = function(val){
- if( val == null ){
- return Math.min(rows, cols);
- } else {
- var min = Math.min(rows, cols);
- if( min == rows ){
- rows = val;
- } else {
- cols = val;
- }
- }
- };
-
- var large = function(val){
- if( val == null ){
- return Math.max(rows, cols);
- } else {
- var max = Math.max(rows, cols);
- if( max == rows ){
- rows = val;
- } else {
- cols = val;
- }
- }
- };
-
- var oRows = options.rows;
- var oCols = options.cols != null ? options.cols : options.columns;
-
- // if rows or columns were set in options, use those values
- if( oRows != null && oCols != null ){
- rows = oRows;
- cols = oCols;
- } else if( oRows != null && oCols == null ){
- rows = oRows;
- cols = Math.ceil( cells / rows );
- } else if( oRows == null && oCols != null ){
- cols = oCols;
- rows = Math.ceil( cells / cols );
- }
-
- // otherwise use the automatic values and adjust accordingly
-
- // if rounding was up, see if we can reduce rows or columns
- else if( cols * rows > cells ){
- var sm = small();
- var lg = large();
-
- // reducing the small side takes away the most cells, so try it first
- if( (sm - 1) * lg >= cells ){
- small(sm - 1);
- } else if( (lg - 1) * sm >= cells ){
- large(lg - 1);
- }
- } else {
-
- // if rounding was too low, add rows or columns
- while( cols * rows < cells ){
- var sm = small();
- var lg = large();
-
- // try to add to larger side first (adds less in multiplication)
- if( (lg + 1) * sm >= cells ){
- large(lg + 1);
- } else {
- small(sm + 1);
- }
- }
- }
-
- var cellWidth = bb.w / cols;
- var cellHeight = bb.h / rows;
-
- if( options.condense ){
- cellWidth = 0;
- cellHeight = 0;
- }
-
- if( options.avoidOverlap ){
- for( var i = 0; i < nodes.length; i++ ){
- var node = nodes[i];
- var pos = node._private.position;
-
- if( pos.x == null || pos.y == null ){ // for bb
- pos.x = 0;
- pos.y = 0;
- }
-
- var nbb = node.boundingBox();
- var p = options.avoidOverlapPadding;
-
- var w = nbb.w + p;
- var h = nbb.h + p;
-
- cellWidth = Math.max( cellWidth, w );
- cellHeight = Math.max( cellHeight, h );
- }
- }
-
- var cellUsed = {}; // e.g. 'c-0-2' => true
-
- var used = function(row, col){
- return cellUsed['c-' + row + '-' + col] ? true : false;
- };
-
- var use = function(row, col){
- cellUsed['c-' + row + '-' + col] = true;
- };
-
- // to keep track of current cell position
- var row = 0;
- var col = 0;
- var moveToNextCell = function(){
- col++;
- if( col >= cols ){
- col = 0;
- row++;
- }
- };
-
- // get a cache of all the manual positions
- var id2manPos = {};
- for( var i = 0; i < nodes.length; i++ ){
- var node = nodes[i];
- var rcPos = options.position( node );
-
- if( rcPos && (rcPos.row !== undefined || rcPos.col !== undefined) ){ // must have at least row or col def'd
- var pos = {
- row: rcPos.row,
- col: rcPos.col
- };
-
- if( pos.col === undefined ){ // find unused col
- pos.col = 0;
-
- while( used(pos.row, pos.col) ){
- pos.col++;
- }
- } else if( pos.row === undefined ){ // find unused row
- pos.row = 0;
-
- while( used(pos.row, pos.col) ){
- pos.row++;
- }
- }
-
- id2manPos[ node.id() ] = pos;
- use( pos.row, pos.col );
- }
- }
-
- var getPos = function(i, element){
- var x, y;
-
- if( element.locked() || element.isFullAutoParent() ){
- return false;
- }
-
- // see if we have a manual position set
- var rcPos = id2manPos[ element.id() ];
- if( rcPos ){
- x = rcPos.col * cellWidth + cellWidth/2 + bb.x1;
- y = rcPos.row * cellHeight + cellHeight/2 + bb.y1;
-
- } else { // otherwise set automatically
-
- while( used(row, col) ){
- moveToNextCell();
- }
-
- x = col * cellWidth + cellWidth/2 + bb.x1;
- y = row * cellHeight + cellHeight/2 + bb.y1;
- use( row, col );
-
- moveToNextCell();
- }
-
- return { x: x, y: y };
-
- };
-
- nodes.layoutPositions( this, options, getPos );
- }
-
- return this; // chaining
-
-};
-
-module.exports = GridLayout;
-
-},{"../../math":79,"../../util":94}],50:[function(_dereq_,module,exports){
-'use strict';
-
-module.exports = [
- { name: 'breadthfirst', impl: _dereq_('./breadthfirst') },
- { name: 'circle', impl: _dereq_('./circle') },
- { name: 'concentric',impl: _dereq_('./concentric') },
- { name: 'cose', impl: _dereq_('./cose') },
- { name: 'grid', impl: _dereq_('./grid') },
- { name: 'null', impl: _dereq_('./null') },
- { name: 'preset', impl: _dereq_('./preset') },
- { name: 'random', impl: _dereq_('./random') }
-];
-
-},{"./breadthfirst":45,"./circle":46,"./concentric":47,"./cose":48,"./grid":49,"./null":51,"./preset":52,"./random":53}],51:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-
-// default layout options
-var defaults = {
- ready: function(){}, // on layoutready
- stop: function(){} // on layoutstop
-};
-
-// constructor
-// options : object containing layout options
-function NullLayout( options ){
- this.options = util.extend({}, defaults, options);
-}
-
-// runs the layout
-NullLayout.prototype.run = function(){
- var options = this.options;
- var eles = options.eles; // elements to consider in the layout
- var layout = this;
-
- // cy is automatically populated for us in the constructor
- var cy = options.cy; // jshint ignore:line
-
- layout.trigger('layoutstart');
-
- // puts all nodes at (0, 0)
- eles.nodes().positions(function(){
- return {
- x: 0,
- y: 0
- };
- });
-
- // trigger layoutready when each node has had its position set at least once
- layout.one('layoutready', options.ready);
- layout.trigger('layoutready');
-
- // trigger layoutstop when the layout stops (e.g. finishes)
- layout.one('layoutstop', options.stop);
- layout.trigger('layoutstop');
-
- return this; // chaining
-};
-
-// called on continuous layouts to stop them before they finish
-NullLayout.prototype.stop = function(){
- return this; // chaining
-};
-
-module.exports = NullLayout;
-
-},{"../../util":94}],52:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-var is = _dereq_('../../is');
-
-var defaults = {
- positions: undefined, // map of (node id) => (position obj); or function(node){ return somPos; }
- zoom: undefined, // the zoom level to set (prob want fit = false if set)
- pan: undefined, // the pan level to set (prob want fit = false if set)
- fit: true, // whether to fit to viewport
- padding: 30, // padding on fit
- animate: false, // whether to transition the node positions
- animationDuration: 500, // duration of animation in ms if enabled
- animationEasing: undefined, // easing of animation if enabled
- ready: undefined, // callback on layoutready
- stop: undefined // callback on layoutstop
-};
-
-function PresetLayout( options ){
- this.options = util.extend({}, defaults, options);
-}
-
-PresetLayout.prototype.run = function(){
- var options = this.options;
- var eles = options.eles;
-
- var nodes = eles.nodes();
- var posIsFn = is.fn( options.positions );
-
- function getPosition(node){
- if( options.positions == null ){
- return null;
- }
-
- if( posIsFn ){
- return options.positions.apply( node, [ node ] );
- }
-
- var pos = options.positions[node._private.data.id];
-
- if( pos == null ){
- return null;
- }
-
- return pos;
- }
-
- nodes.layoutPositions(this, options, function(i, node){
- var position = getPosition(node);
-
- if( node.locked() || position == null ){
- return false;
- }
-
- return position;
- });
-
- return this; // chaining
-};
-
-module.exports = PresetLayout;
-
-},{"../../is":77,"../../util":94}],53:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../../util');
-var math = _dereq_('../../math');
-
-var defaults = {
- fit: true, // whether to fit to viewport
- padding: 30, // fit padding
- boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
- animate: false, // whether to transition the node positions
- animationDuration: 500, // duration of animation in ms if enabled
- animationEasing: undefined, // easing of animation if enabled
- ready: undefined, // callback on layoutready
- stop: undefined // callback on layoutstop
-};
-
-function RandomLayout( options ){
- this.options = util.extend({}, defaults, options);
-}
-
-RandomLayout.prototype.run = function(){
- var options = this.options;
- var cy = options.cy;
- var eles = options.eles;
- var nodes = eles.nodes().not(':parent');
-
- var bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
- x1: 0, y1: 0, w: cy.width(), h: cy.height()
- } );
-
- var getPos = function( i, node ){
- return {
- x: bb.x1 + Math.round( Math.random() * bb.w ),
- y: bb.y1 + Math.round( Math.random() * bb.h )
- };
- };
-
- nodes.layoutPositions( this, options, getPos );
-
- return this; // chaining
-};
-
-module.exports = RandomLayout;
-
-},{"../../math":79,"../../util":94}],54:[function(_dereq_,module,exports){
-'use strict';
-
-var math = _dereq_('../../../math');
-var is = _dereq_('../../../is');
-var util = _dereq_('../../../util');
-
-var BRp = {};
-
-BRp.arrowShapeHeight = 0.3;
-
-BRp.registerArrowShapes = function(){
- var arrowShapes = this.arrowShapes = {};
- var renderer = this;
-
- // Contract for arrow shapes:
- // 0, 0 is arrow tip
- // (0, 1) is direction towards node
- // (1, 0) is right
- //
- // functional api:
- // collide: check x, y in shape
- // roughCollide: called before collide, no false negatives
- // draw: draw
- // spacing: dist(arrowTip, nodeBoundary)
- // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
-
- var bbCollide = function( x, y, size, angle, translation, padding ){
- var x1 = translation.x - size/2 - padding;
- var x2 = translation.x + size/2 + padding;
- var y1 = translation.y - size/2 - padding;
- var y2 = translation.y + size/2 + padding;
-
- var inside = (x1 <= x && x <= x2) && (y1 <= y && y <= y2);
-
- return inside;
- };
-
- var transform = function( x, y, size, angle, translation ){
- var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
- var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
-
- var xScaled = xRotated * size;
- var yScaled = yRotated * size;
-
- var xTranslated = xScaled + translation.x;
- var yTranslated = yScaled + translation.y;
-
- return {
- x: xTranslated,
- y: yTranslated
- };
- };
-
- var transformPoints = function( pts, size, angle, translation ){
- var retPts = [];
-
- for( var i = 0; i < pts.length; i += 2 ){
- var x = pts[i];
- var y = pts[i + 1];
-
- retPts.push( transform(x, y, size, angle, translation) );
- }
-
- return retPts;
- };
-
- var pointsToArr = function( pts ){
- var ret = [];
-
- for( var i = 0; i < pts.length; i++ ){
- var p = pts[i];
-
- ret.push( p.x, p.y );
- }
-
- return ret;
- };
-
- var defineArrowShape = function( name, defn ){
- if( is.string(defn) ){
- defn = arrowShapes[ defn ];
- }
-
- arrowShapes[ name ] = util.extend( {
- name: name,
-
- points: [
- -0.15, -0.3,
- 0.15, -0.3,
- 0.15, 0.3,
- -0.15, 0.3
- ],
-
- collide: function( x, y, size, angle, translation, padding ){
- var points = pointsToArr( transformPoints( this.points, size + 2*padding, angle, translation ) );
- var inside = math.pointInsidePolygonPoints( x, y, points );
-
- return inside;
- },
-
- roughCollide: bbCollide,
-
- draw: function( context, size, angle, translation ){
- var points = transformPoints( this.points, size, angle, translation );
-
- renderer.arrowShapeImpl('polygon')( context, points );
- },
-
- spacing: function( edge ){
- return 0;
- },
-
- gap: function( edge ){
- return edge._private.style['width'].pfValue * 2;
- }
- }, defn );
- };
-
- defineArrowShape( 'none', {
- collide: util.falsify,
-
- roughCollide: util.falsify,
-
- draw: util.noop,
-
- spacing: util.zeroify,
-
- gap: util.zeroify
- } );
-
- defineArrowShape( 'triangle', {
- points: [
- -0.15, -0.3,
- 0, 0,
- 0.15, -0.3
- ]
- } );
-
- defineArrowShape( 'arrow', 'triangle' );
-
- defineArrowShape( 'triangle-backcurve', {
- points: arrowShapes['triangle'].points,
-
- controlPoint: [ 0, -0.15 ],
-
- roughCollide: bbCollide,
-
- draw: function( context, size, angle, translation ){
- var ptsTrans = transformPoints( this.points, size, angle, translation );
- var ctrlPt = this.controlPoint;
- var ctrlPtTrans = transform( ctrlPt[0], ctrlPt[1], size, angle, translation );
-
- renderer.arrowShapeImpl( this.name )( context, ptsTrans, ctrlPtTrans );
- },
-
- gap: function( edge ){
- return edge._private.style['width'].pfValue;
- }
- } );
-
-
- defineArrowShape( 'triangle-tee', {
- points: [
- -0.15, -0.3,
- 0, 0,
- 0.15, -0.3,
- -0.15, -0.3
- ],
-
- pointsTee: [
- -0.15, -0.4,
- -0.15, -0.5,
- 0.15, -0.5,
- 0.15, -0.4
- ],
-
- collide: function( x, y, size, angle, translation, padding ){
- var triPts = pointsToArr( transformPoints( this.points, size + 2*padding, angle, translation ) );
- var teePts = pointsToArr( transformPoints( this.pointsTee, size + 2*padding, angle, translation ) );
-
- var inside = math.pointInsidePolygonPoints( x, y, triPts ) || math.pointInsidePolygonPoints( x, y, teePts );
-
- return inside;
- },
-
- draw: function( context, size, angle, translation ){
- var triPts = transformPoints( this.points, size, angle, translation );
- var teePts = transformPoints( this.pointsTee, size, angle, translation );
-
- renderer.arrowShapeImpl( this.name )( context, triPts, teePts );
- }
- } );
-
- defineArrowShape( 'vee', {
- points: [
- -0.15, -0.3,
- 0, 0,
- 0.15, -0.3,
- 0, -0.15
- ],
-
- gap: function( edge ){
- return edge._private.style['width'].pfValue;
- }
- } );
-
- defineArrowShape( 'half-triangle-overshot', {
- points: [
- 0, -0.25,
- -0.5, -0.25,
- 0.5, 0.25
- ],
-
- leavePathOpen: true,
-
- matchEdgeWidth: true
- } );
-
- defineArrowShape( 'circle', {
- radius: 0.15,
-
- collide: function( x, y, size, angle, translation, padding ){
- var t = translation;
- var inside = ( Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2*padding) * this.radius, 2) );
-
- return inside;
- },
-
- draw: function( context, size, angle, translation ){
- renderer.arrowShapeImpl( this.name )( context, translation.x, translation.y, this.radius * size );
- },
-
- spacing: function( edge ){
- return renderer.getArrowWidth(edge._private.style['width'].pfValue)
- * this.radius;
- }
- } );
-
- defineArrowShape( 'inhibitor', {
- points: [
- -0.25, 0,
- -0.25, -0.1,
- 0.25, -0.1,
- 0.25, 0
- ],
-
- spacing: function( edge ){
- return 1;
- },
-
- gap: function( edge ){
- return 1;
- }
- } );
-
- defineArrowShape( 'tee', 'inhibitor' );
-
- defineArrowShape( 'square', {
- points: [
- -0.15, 0.00,
- 0.15, 0.00,
- 0.15, -0.3,
- -0.15, -0.3
- ]
- } );
-
- defineArrowShape( 'diamond', {
- points: [
- -0.15, -0.15,
- 0, -0.3,
- 0.15, -0.15,
- 0, 0
- ],
-
- gap: function( edge ){
- return edge._private.style['width'].pfValue;
- }
- } );
-
-};
-
-module.exports = BRp;
-
-},{"../../../is":77,"../../../math":79,"../../../util":94}],55:[function(_dereq_,module,exports){
-'use strict';
-
-var BRp = {};
-
-var delEleCache = function( r ){
- r.eleEache = null;
-};
-
-var getEleCache = function( r ){
- if( !r.eleEache ){
- r.eleEache = {
- nodes: r.cy.nodes(),
- edges: r.cy.edges()
- };
- }
-
- return r.eleEache;
-};
-
-BRp.getCachedElements = function(){
- return getEleCache( this );
-};
-
-BRp.getCachedNodes = function(){
- return getEleCache( this ).nodes;
-};
-
-BRp.getCachedEdges = function(){
- return getEleCache( this ).edges;
-};
-
-BRp.updateElementsCache = function(){
- var r = this;
-
- delEleCache( r );
-
- return getEleCache( r );
-};
-
-module.exports = BRp;
-
-},{}],56:[function(_dereq_,module,exports){
-'use strict';
-
-var math = _dereq_('../../../math');
-var is = _dereq_('../../../is');
-var zIndexSort = _dereq_('../../../collection/zsort');
-
-var BRp = {};
-
-// Project mouse
-BRp.projectIntoViewport = function(clientX, clientY) {
- var offsets = this.findContainerClientCoords();
- var offsetLeft = offsets[0];
- var offsetTop = offsets[1];
-
- var x = clientX - offsetLeft;
- var y = clientY - offsetTop;
-
- x -= this.cy.pan().x; y -= this.cy.pan().y; x /= this.cy.zoom(); y /= this.cy.zoom();
- return [x, y];
-};
-
-BRp.findContainerClientCoords = function() {
- var container = this.container;
-
- var bb = this.containerBB = this.containerBB || container.getBoundingClientRect();
-
- return [bb.left, bb.top, bb.right - bb.left, bb.bottom - bb.top];
-};
-
-BRp.invalidateContainerClientCoordsCache = function(){
- this.containerBB = null;
-};
-
-// Find nearest element
-BRp.findNearestElement = function(x, y, visibleElementsOnly, isTouch){
- var self = this;
- var r = this;
- var eles = r.getCachedZSortedEles();
- var near = [];
- var zoom = r.cy.zoom();
- var hasCompounds = r.cy.hasCompoundNodes();
- var edgeThreshold = (isTouch ? 24 : 8) / zoom;
- var nodeThreshold = (isTouch ? 8 : 2) / zoom;
- var labelThreshold = (isTouch ? 8 : 2) / zoom;
-
- function checkNode(node){
- var _p = node._private;
-
- if( _p.style['events'].strValue === 'no' ){ return; }
-
- var width = node.outerWidth() + 2*nodeThreshold;
- var height = node.outerHeight() + 2*nodeThreshold;
- var hw = width/2;
- var hh = height/2;
- var pos = _p.position;
-
- if(
- pos.x - hw <= x && x <= pos.x + hw // bb check x
- &&
- pos.y - hh <= y && y <= pos.y + hh // bb check y
- ){
- var visible = !visibleElementsOnly || ( node.visible() && !node.transparent() );
-
- // exit early if invisible edge and must be visible
- if( visibleElementsOnly && !visible ){
- return;
- }
-
- var shape = r.nodeShapes[ self.getNodeShape(node) ];
-
- if(
- shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)
- ){
- near.push( node );
- }
-
- }
- }
-
- function checkEdge(edge){
- var _p = edge._private;
-
- if( _p.style['events'].strValue === 'no' ){ return; }
-
- var rs = _p.rscratch;
- var style = _p.style;
- var width = style['width'].pfValue/2 + edgeThreshold; // more like a distance radius from centre
- var widthSq = width * width;
- var width2 = width * 2;
- var src = _p.source;
- var tgt = _p.target;
- var inEdgeBB = false;
- var sqDist;
-
- // exit early if invisible edge and must be visible
- var passedVisibilityCheck;
- var passesVisibilityCheck = function(){
- if( passedVisibilityCheck !== undefined ){
- return passedVisibilityCheck;
- }
-
- if( !visibleElementsOnly ){
- passedVisibilityCheck = true;
- return true;
- }
-
- var visible = edge.visible() && !edge.transparent();
- if( visible ){
- passedVisibilityCheck = true;
- return true;
- }
-
- passedVisibilityCheck = false;
- return false;
- };
-
- if( rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack' ){
- var pts = rs.allpts;
-
- for( var i = 0; i + 3 < pts.length; i += 2 ){
- if(
- (inEdgeBB = math.inLineVicinity(x, y, pts[i], pts[i+1], pts[i+2], pts[i+3], width2))
- && passesVisibilityCheck() &&
- widthSq > ( sqDist = math.sqDistanceToFiniteLine(x, y, pts[i], pts[i+1], pts[i+2], pts[i+3]) )
- ){
- near.push( edge );
- }
- }
-
- } else if( rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' ){
- var pts = rs.allpts;
- for( var i = 0; i + 5 < rs.allpts.length; i += 4 ){
- if(
- (inEdgeBB = math.inBezierVicinity(x, y, pts[i], pts[i+1], pts[i+2], pts[i+3], pts[i+4], pts[i+5], width2))
- && passesVisibilityCheck() &&
- (widthSq > (sqDist = math.sqDistanceToQuadraticBezier(x, y, pts[i], pts[i+1], pts[i+2], pts[i+3], pts[i+4], pts[i+5])) )
- ){
- near.push( edge );
- }
- }
- }
-
- // if we're close to the edge but didn't hit it, maybe we hit its arrows
- if( inEdgeBB && passesVisibilityCheck() && near.length === 0 || near[near.length - 1] !== edge ){
- var src = src || _p.source;
- var tgt = tgt || _p.target;
-
- var eWidth = style['width'].pfValue;
- var arSize = self.getArrowWidth( eWidth );
-
- var arrows = [
- { name: 'source', x: rs.arrowStartX, y: rs.arrowStartY, angle: rs.srcArrowAngle },
- { name: 'target', x: rs.arrowEndX, y: rs.arrowEndY, angle: rs.tgtArrowAngle },
- { name: 'mid-source', x: rs.midX, y: rs.midY, angle: rs.midsrcArrowAngle },
- { name: 'mid-target', x: rs.midX, y: rs.midY, angle: rs.midtgtArrowAngle }
- ];
-
- for( var i = 0; i < arrows.length; i++ ){
- var ar = arrows[i];
- var shape = r.arrowShapes[ style[ar.name+'-arrow-shape'].value ];
-
- if(
- shape.roughCollide(x, y, arSize, ar.angle, { x: ar.x, y: ar.y }, edgeThreshold)
- &&
- shape.collide(x, y, arSize, ar.angle, { x: ar.x, y: ar.y }, edgeThreshold)
- ){
- near.push( edge );
- break;
- }
- }
- }
-
- // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
- if( hasCompounds && near.length > 0 && near[ near.length - 1 ] === edge ){
- checkNode( src );
- checkNode( tgt );
- }
- }
-
- function checkLabel(ele){
- var _p = ele._private;
- var th = labelThreshold;
-
- if( _p.style['text-events'].strValue === 'no' ){ return; }
-
- // adjust bb w/ angle
- if( _p.group === 'edges' && _p.style['edge-text-rotation'].strValue === 'autorotate' ){
-
- var rstyle = _p.rstyle;
- var lw = rstyle.labelWidth + 2*th;
- var lh = rstyle.labelHeight + 2*th;
- var lx = rstyle.labelX;
- var ly = rstyle.labelY;
-
- var theta = _p.rscratch.labelAngle;
- var cos = Math.cos( theta );
- var sin = Math.sin( theta );
-
- var rotate = function( x, y ){
- x = x - lx;
- y = y - ly;
-
- return {
- x: x*cos - y*sin + lx,
- y: x*sin + y*cos + ly
- };
- };
-
- var lx1 = lx - lw/2;
- var lx2 = lx + lw/2;
- var ly1 = ly - lh/2;
- var ly2 = ly + lh/2;
-
- var px1y1 = rotate( lx1, ly1 );
- var px1y2 = rotate( lx1, ly2 );
- var px2y1 = rotate( lx2, ly1 );
- var px2y2 = rotate( lx2, ly2 );
-
- var points = [
- px1y1.x, px1y1.y,
- px2y1.x, px2y1.y,
- px2y2.x, px2y2.y,
- px1y2.x, px1y2.y
- ];
-
- if( math.pointInsidePolygonPoints( x, y, points ) ){
- near.push( ele );
- }
-
- } else {
- var bb = ele.boundingBox({
- includeLabels: true,
- includeNodes: false,
- includeEdges: false
- });
-
- // adjust bb w/ threshold
- bb.x1 -= th;
- bb.y1 -= th;
- bb.x2 += th;
- bb.y2 += th;
- bb.w = bb.x2 - bb.x1;
- bb.h = bb.y2 - bb.y1;
-
- if( math.inBoundingBox( bb, x, y ) ){
- near.push( ele );
- }
- }
-
- }
-
- for( var i = eles.length - 1; i >= 0; i-- ){ // reverse order for precedence
- var ele = eles[i];
- var _p = ele._private;
-
- if( near.length > 0 ){ break; } // since we check in z-order, first found is top and best result => exit early
-
- if( _p.group === 'nodes' ){
- checkNode( ele );
-
- } else { // then edge
- checkEdge( ele );
- }
-
- checkLabel( ele );
-
- }
-
-
- if( near.length > 0 ){
- return near[ near.length - 1 ];
- } else {
- return null;
- }
-};
-
-// 'Give me everything from this box'
-BRp.getAllInBox = function(x1, y1, x2, y2) {
- var nodes = this.getCachedNodes();
- var edges = this.getCachedEdges();
- var box = [];
-
- var x1c = Math.min(x1, x2);
- var x2c = Math.max(x1, x2);
- var y1c = Math.min(y1, y2);
- var y2c = Math.max(y1, y2);
-
- x1 = x1c;
- x2 = x2c;
- y1 = y1c;
- y2 = y2c;
-
- var boxBb = math.makeBoundingBox({
- x1: x1, y1: y1,
- x2: x2, y2: y2
- });
-
- for ( var i = 0; i < nodes.length; i++ ){
- var node = nodes[i];
- var nodeBb = node.boundingBox({
- includeNodes: true,
- includeEdges: false,
- includeLabels: false
- });
-
- if( math.boundingBoxesIntersect(boxBb, nodeBb) ){
- box.push(nodes[i]);
- }
- }
-
- for( var e = 0; e < edges.length; e++ ){
- var edge = edges[e];
- var _p = edge._private;
- var rs = _p.rscratch;
-
- if( rs.startX != null && rs.startY != null && !math.inBoundingBox( boxBb, rs.startX, rs.startY ) ){ continue; }
- if( rs.endX != null && rs.endY != null && !math.inBoundingBox( boxBb, rs.endX, rs.endY ) ){ continue; }
-
- if( rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack' ){
-
- var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
- var allInside = true;
-
- for( var i = 0; i < pts.length; i++ ){
- if( !math.pointInBoundingBox( boxBb, pts[i] ) ){
- allInside = false;
- break;
- }
- }
-
- if( allInside ){
- box.push( edge );
- }
-
- } else if( rs.edgeType === 'haystack' || rs.edgeType === 'straight' ){
- box.push( edge );
- }
-
- }
-
- return box;
-};
-
-
-/**
- * Returns the shape of the given node. If the height or width of the given node
- * is set to auto, the node is considered to be a compound.
- *
- * @param node a node
- * @return {String} shape of the node
- */
-BRp.getNodeShape = function( node ){
- var r = this;
- var style = node._private.style;
- var shape = style['shape'].value;
-
- if( node.isParent() ){
- if( shape === 'rectangle' || shape === 'roundrectangle' ){
- return shape;
- } else {
- return 'rectangle';
- }
- }
-
- if( shape === 'polygon' ){
- var points = style['shape-polygon-points'].value;
-
- return r.nodeShapes.makePolygon( points ).name;
- }
-
- return shape;
-};
-
-BRp.updateCachedZSortedEles = function(){
- this.getCachedZSortedEles( true );
-};
-
-BRp.getCachedZSortedEles = function( forceRecalc ){
- var lastNodes = this.lastZOrderCachedNodes;
- var lastEdges = this.lastZOrderCachedEdges;
- var nodes = this.getCachedNodes();
- var edges = this.getCachedEdges();
- var eles = [];
-
- if( forceRecalc || !lastNodes || !lastEdges || lastNodes !== nodes || lastEdges !== edges ){
- //console.time('cachezorder')
-
- for( var i = 0; i < nodes.length; i++ ){
- var n = nodes[i];
-
- if( n.animated() || (n.visible() && !n.transparent()) ){
- eles.push( n );
- }
- }
-
- for( var i = 0; i < edges.length; i++ ){
- var e = edges[i];
-
- if( e.animated() || (e.visible() && !e.transparent()) ){
- eles.push( e );
- }
- }
-
- eles.sort( zIndexSort );
- this.cachedZSortedEles = eles;
- //console.log('make cache')
-
- //console.timeEnd('cachezorder')
- } else {
- eles = this.cachedZSortedEles;
- //console.log('read cache')
- }
-
- this.lastZOrderCachedNodes = nodes;
- this.lastZOrderCachedEdges = edges;
-
- return eles;
-};
-
-function pushBezierPts(edge, pts){
- var qbezierAt = function( p1, p2, p3, t ){ return math.qbezierAt(p1, p2, p3, t); };
- var _p = edge._private;
- var bpts = _p.rstyle.bezierPts;
-
- bpts.push({
- x: qbezierAt( pts[0], pts[2], pts[4], 0.05 ),
- y: qbezierAt( pts[1], pts[3], pts[5], 0.05 )
- });
-
- bpts.push({
- x: qbezierAt( pts[0], pts[2], pts[4], 0.25 ),
- y: qbezierAt( pts[1], pts[3], pts[5], 0.25 )
- });
-
- bpts.push({
- x: qbezierAt( pts[0], pts[2], pts[4], 0.4 ),
- y: qbezierAt( pts[1], pts[3], pts[5], 0.4 )
- });
-
- bpts.push({
- x: qbezierAt( pts[0], pts[2], pts[4], 0.5 ),
- y: qbezierAt( pts[1], pts[3], pts[5], 0.5 )
- });
-
- bpts.push({
- x: qbezierAt( pts[0], pts[2], pts[4], 0.6 ),
- y: qbezierAt( pts[1], pts[3], pts[5], 0.6 )
- });
-
- bpts.push({
- x: qbezierAt( pts[0], pts[2], pts[4], 0.75 ),
- y: qbezierAt( pts[1], pts[3], pts[5], 0.75 )
- });
-
- bpts.push({
- x: qbezierAt( pts[0], pts[2], pts[4], 0.95 ),
- y: qbezierAt( pts[1], pts[3], pts[5], 0.95 )
- });
-}
-
-BRp.projectLines = function( edge ){
- var _p = edge._private;
- var rs = _p.rscratch;
- var et = rs.edgeType;
-
- if( et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound' ){
- var bpts = _p.rstyle.bezierPts = []; // jshint ignore:line
-
- for( var i = 0; i + 5 < rs.allpts.length; i += 4 ){
- pushBezierPts( edge, rs.allpts.slice(i, i+6) );
- }
- } else if( et === 'segments' ){
- var lpts = _p.rstyle.linePts = [];
-
- for( var i = 0; i + 1 < rs.allpts.length; i += 2 ){
- lpts.push({
- x: rs.allpts[i],
- y: rs.allpts[i+1]
- });
- }
- } else if( et === 'haystack' ){
- var hpts = rs.haystackPts;
-
- _p.rstyle.haystackPts = [
- { x: hpts[0], y: hpts[1] },
- { x: hpts[2], y: hpts[3] }
- ];
- }
-};
-
-BRp.projectBezier = BRp.projectLines;
-
-BRp.recalculateNodeLabelProjection = function( node ){
- var content = node._private.style['label'].strValue;
- if( !content || content.match(/^\s+$/) ){ return; }
-
- var textX, textY;
- var nodeWidth = node.outerWidth();
- var nodeHeight = node.outerHeight();
- var nodePos = node._private.position;
- var textHalign = node._private.style['text-halign'].strValue;
- var textValign = node._private.style['text-valign'].strValue;
- var rs = node._private.rscratch;
- var rstyle = node._private.rstyle;
-
- switch( textHalign ){
- case 'left':
- textX = nodePos.x - nodeWidth / 2;
- break;
-
- case 'right':
- textX = nodePos.x + nodeWidth / 2;
- break;
-
- default: // e.g. center
- textX = nodePos.x;
- }
-
- switch( textValign ){
- case 'top':
- textY = nodePos.y - nodeHeight / 2;
- break;
-
- case 'bottom':
- textY = nodePos.y + nodeHeight / 2;
- break;
-
- default: // e.g. middle
- textY = nodePos.y;
- }
-
- rs.labelX = textX;
- rs.labelY = textY;
- rstyle.labelX = textX;
- rstyle.labelY = textY;
-
- this.applyLabelDimensions( node );
-};
-
-BRp.recalculateEdgeLabelProjection = function( edge ){
- var content = edge._private.style['label'].strValue;
- if( !content || content.match(/^\s+$/) ){ return; }
-
- var textX, textY;
- var _p = edge._private;
- var rs = _p.rscratch;
- //var style = _p.style;
- var rstyle = _p.rstyle;
-
- textX = rs.midX;
- textY = rs.midY;
-
- // add center point to style so bounding box calculations can use it
- rs.labelX = textX;
- rs.labelY = textY;
- rstyle.labelX = textX;
- rstyle.labelY = textY;
-
- this.applyLabelDimensions( edge );
-};
-
-BRp.applyLabelDimensions = function( ele ){
- var rs = ele._private.rscratch;
- var rstyle = ele._private.rstyle;
-
- var text = this.getLabelText( ele );
- var labelDims = this.calculateLabelDimensions( ele, text );
-
- rstyle.labelWidth = labelDims.width;
- rs.labelWidth = labelDims.width;
-
- rstyle.labelHeight = labelDims.height;
- rs.labelHeight = labelDims.height;
-};
-
-BRp.getLabelText = function( ele ){
- var style = ele._private.style;
- var text = ele._private.style['label'].strValue;
- var textTransform = style['text-transform'].value;
- var rscratch = ele._private.rscratch;
-
- if (textTransform == 'none') {
- } else if (textTransform == 'uppercase') {
- text = text.toUpperCase();
- } else if (textTransform == 'lowercase') {
- text = text.toLowerCase();
- }
-
- if( style['text-wrap'].value === 'wrap' ){
- //console.log('wrap');
-
- // save recalc if the label is the same as before
- if( rscratch.labelWrapKey === rscratch.labelKey ){
- // console.log('wrap cache hit');
- return rscratch.labelWrapCachedText;
- }
- // console.log('wrap cache miss');
-
- var lines = text.split('\n');
- var maxW = style['text-max-width'].pfValue;
- var wrappedLines = [];
-
- for( var l = 0; l < lines.length; l++ ){
- var line = lines[l];
- var lineDims = this.calculateLabelDimensions( ele, line, 'line=' + line );
- var lineW = lineDims.width;
-
- if( lineW > maxW ){ // line is too long
- var words = line.split(/\s+/); // NB: assume collapsed whitespace into single space
- var subline = '';
-
- for( var w = 0; w < words.length; w++ ){
- var word = words[w];
- var testLine = subline.length === 0 ? word : subline + ' ' + word;
- var testDims = this.calculateLabelDimensions( ele, testLine, 'testLine=' + testLine );
- var testW = testDims.width;
-
- if( testW <= maxW ){ // word fits on current line
- subline += word + ' ';
- } else { // word starts new line
- wrappedLines.push( subline );
- subline = word + ' ';
- }
- }
-
- // if there's remaining text, put it in a wrapped line
- if( !subline.match(/^\s+$/) ){
- wrappedLines.push( subline );
- }
- } else { // line is already short enough
- wrappedLines.push( line );
- }
- } // for
-
- rscratch.labelWrapCachedLines = wrappedLines;
- rscratch.labelWrapCachedText = text = wrappedLines.join('\n');
- rscratch.labelWrapKey = rscratch.labelKey;
-
- // console.log(text)
- } // if wrap
-
- return text;
-};
-
-BRp.calculateLabelDimensions = function( ele, text, extraKey ){
- var r = this;
- var style = ele._private.style;
- var fStyle = style['font-style'].strValue;
- var size = style['font-size'].pfValue + 'px';
- var family = style['font-family'].strValue;
- // var variant = style['font-variant'].strValue;
- var weight = style['font-weight'].strValue;
-
- var cacheKey = ele._private.labelKey;
-
- if( extraKey ){
- cacheKey += '$@$' + extraKey;
- }
-
- var cache = r.labelDimCache || (r.labelDimCache = {});
-
- if( cache[cacheKey] ){
- return cache[cacheKey];
- }
-
- var div = this.labelCalcDiv;
-
- if( !div ){
- div = this.labelCalcDiv = document.createElement('div');
- document.body.appendChild( div );
- }
-
- var ds = div.style;
-
- // from ele style
- ds.fontFamily = family;
- ds.fontStyle = fStyle;
- ds.fontSize = size;
- // ds.fontVariant = variant;
- ds.fontWeight = weight;
-
- // forced style
- ds.position = 'absolute';
- ds.left = '-9999px';
- ds.top = '-9999px';
- ds.zIndex = '-1';
- ds.visibility = 'hidden';
- ds.pointerEvents = 'none';
- ds.padding = '0';
- ds.lineHeight = '1';
-
- if( style['text-wrap'].value === 'wrap' ){
- ds.whiteSpace = 'pre'; // so newlines are taken into account
- } else {
- ds.whiteSpace = 'normal';
- }
-
- // put label content in div
- div.textContent = text;
-
- cache[cacheKey] = {
- width: div.clientWidth,
- height: div.clientHeight
- };
-
- return cache[cacheKey];
-};
-
-BRp.recalculateRenderedStyle = function( eles ){
- var edges = [];
- var nodes = [];
- var handledEdge = {};
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var _p = ele._private;
- var style = _p.style;
- var rs = _p.rscratch;
- var rstyle = _p.rstyle;
- var id = _p.data.id;
- var bbStyleSame = rs.boundingBoxKey != null && _p.boundingBoxKey === rs.boundingBoxKey;
- var labelStyleSame = rs.labelKey != null && _p.labelKey === rs.labelKey;
- var styleSame = bbStyleSame && labelStyleSame;
-
- if( _p.group === 'nodes' ){
- var pos = _p.position;
- var posSame = rstyle.nodeX != null && rstyle.nodeY != null && pos.x === rstyle.nodeX && pos.y === rstyle.nodeY;
- var wSame = rstyle.nodeW != null && rstyle.nodeW === style['width'].pfValue;
- var hSame = rstyle.nodeH != null && rstyle.nodeH === style['height'].pfValue;
-
- if( !posSame || !styleSame || !wSame || !hSame ){
- nodes.push( ele );
- }
-
- rstyle.nodeX = pos.x;
- rstyle.nodeY = pos.y;
- rstyle.nodeW = style['width'].pfValue;
- rstyle.nodeH = style['height'].pfValue;
- } else { // edges
-
- var srcPos = _p.source._private.position;
- var tgtPos = _p.target._private.position;
- var srcSame = rstyle.srcX != null && rstyle.srcY != null && srcPos.x === rstyle.srcX && srcPos.y === rstyle.srcY;
- var tgtSame = rstyle.tgtX != null && rstyle.tgtY != null && tgtPos.x === rstyle.tgtX && tgtPos.y === rstyle.tgtY;
- var positionsSame = srcSame && tgtSame;
-
- if( !positionsSame || !styleSame ){
- if( rs.edgeType === 'bezier' || rs.edgeType === 'straight' || rs.edgeType === 'self' || rs.edgeType === 'compound' ){
- if( !handledEdge[ id ] ){
- edges.push( ele );
- handledEdge[ id ] = true;
-
- var parallelEdges = ele.parallelEdges();
- for( var i = 0; i < parallelEdges.length; i++ ){
- var pEdge = parallelEdges[i];
- var pId = pEdge._private.data.id;
-
- if( !handledEdge[ pId ] ){
- edges.push( pEdge );
- handledEdge[ pId ] = true;
- }
-
- }
- }
- } else {
- edges.push( ele );
- }
- } // if positions diff
-
- // update rstyle positions
- rstyle.srcX = srcPos.x;
- rstyle.srcY = srcPos.y;
- rstyle.tgtX = tgtPos.x;
- rstyle.tgtY = tgtPos.y;
-
- } // if edges
-
- rs.boundingBoxKey = _p.boundingBoxKey;
- rs.labelKey = _p.labelKey;
- }
-
- this.recalculateEdgeProjections( edges );
- this.recalculateLabelProjections( nodes, edges );
-};
-
-BRp.recalculateLabelProjections = function( nodes, edges ){
- for( var i = 0; i < nodes.length; i++ ){
- this.recalculateNodeLabelProjection( nodes[i] );
- }
-
- for( var i = 0; i < edges.length; i++ ){
- this.recalculateEdgeLabelProjection( edges[i] );
- }
-};
-
-BRp.recalculateEdgeProjections = function( edges ){
- this.findEdgeControlPoints( edges );
-};
-
-
-// Find edge control points
-BRp.findEdgeControlPoints = function(edges) {
- if( !edges || edges.length === 0 ){ return; }
-
- var r = this;
- var cy = r.cy;
- var hasCompounds = cy.hasCompoundNodes();
- var hashTable = {};
- var pairIds = [];
- var haystackEdges = [];
- var autorotateEdges = [];
-
- // create a table of edge (src, tgt) => list of edges between them
- var pairId;
- for (var i = 0; i < edges.length; i++){
- var edge = edges[i];
- var _p = edge._private;
- var data = _p.data;
- var style = _p.style;
- var curveStyle = style['curve-style'].value;
- var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments';
-
- // ignore edges who are not to be displayed
- // they shouldn't take up space
- if( style.display.value === 'none' ){
- continue;
- }
-
- if( style['edge-text-rotation'].strValue === 'autorotate' ){
- autorotateEdges.push( edge );
- }
-
- if( curveStyle === 'haystack' ){
- haystackEdges.push( edge );
- continue;
- }
-
- var srcId = data.source;
- var tgtId = data.target;
-
- pairId = srcId > tgtId ?
- tgtId + '$-$' + srcId :
- srcId + '$-$' + tgtId ;
-
- if( edgeIsUnbundled ){
- pairId = 'unbundled' + '$-$' + data.id;
- }
-
- if( hashTable[pairId] == null ){
- hashTable[pairId] = [];
- pairIds.push( pairId );
- }
-
- hashTable[pairId].push( edge );
-
- if( edgeIsUnbundled ){
- hashTable[pairId].hasUnbundled = true;
- }
- }
-
- var src, tgt, src_p, tgt_p, srcPos, tgtPos, srcW, srcH, tgtW, tgtH, srcShape, tgtShape;
- var vectorNormInverse;
- var badBezier;
-
- // for each pair (src, tgt), create the ctrl pts
- // Nested for loop is OK; total number of iterations for both loops = edgeCount
- for (var p = 0; p < pairIds.length; p++) {
- pairId = pairIds[p];
- var pairEdges = hashTable[pairId];
-
- // for each pair id, the edges should be sorted by index
- pairEdges.sort(function(edge1, edge2){
- return edge1._private.index - edge2._private.index;
- });
-
- src = pairEdges[0]._private.source;
- tgt = pairEdges[0]._private.target;
-
- src_p = src._private;
- tgt_p = tgt._private;
-
- // make sure src/tgt distinction is consistent
- // (src/tgt in this case are just for ctrlpts and don't actually have to be true src/tgt)
- if( src_p.data.id > tgt_p.data.id ){
- var temp = src;
- src = tgt;
- tgt = temp;
- }
-
- srcPos = src_p.position;
- tgtPos = tgt_p.position;
-
- srcW = src.outerWidth();
- srcH = src.outerHeight();
-
- tgtW = tgt.outerWidth();
- tgtH = tgt.outerHeight();
-
- srcShape = r.nodeShapes[ this.getNodeShape(src) ];
- tgtShape = r.nodeShapes[ this.getNodeShape(tgt) ];
-
- badBezier = false;
-
-
- if( (pairEdges.length > 1 && src !== tgt) || pairEdges.hasUnbundled ){
-
- // pt outside src shape to calc distance/displacement from src to tgt
- var srcOutside = srcShape.intersectLine(
- srcPos.x,
- srcPos.y,
- srcW,
- srcH,
- tgtPos.x,
- tgtPos.y,
- 0
- );
-
- // pt outside tgt shape to calc distance/displacement from src to tgt
- var tgtOutside = tgtShape.intersectLine(
- tgtPos.x,
- tgtPos.y,
- tgtW,
- tgtH,
- srcPos.x,
- srcPos.y,
- 0
- );
-
- var midptSrcPts = {
- x1: srcOutside[0],
- x2: tgtOutside[0],
- y1: srcOutside[1],
- y2: tgtOutside[1]
- };
-
- var dy = ( tgtOutside[1] - srcOutside[1] );
- var dx = ( tgtOutside[0] - srcOutside[0] );
- var l = Math.sqrt( dx*dx + dy*dy );
-
- var vector = {
- x: dx,
- y: dy
- };
-
- var vectorNorm = {
- x: vector.x/l,
- y: vector.y/l
- };
- vectorNormInverse = {
- x: -vectorNorm.y,
- y: vectorNorm.x
- };
-
-
- // if src intersection is inside tgt or tgt intersection is inside src, then no ctrl pts to draw
- if(
- tgtShape.checkPoint( srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y ) ||
- srcShape.checkPoint( tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y )
- ){
- vectorNormInverse = {};
- badBezier = true;
- }
-
- }
-
- var edge;
- var edge_p;
- var rs;
-
- for (var i = 0; i < pairEdges.length; i++) {
- edge = pairEdges[i];
- edge_p = edge._private;
- rs = edge_p.rscratch;
-
- var edgeIndex1 = rs.lastEdgeIndex;
- var edgeIndex2 = i;
-
- var numEdges1 = rs.lastNumEdges;
- var numEdges2 = pairEdges.length;
-
- var eStyle = edge_p.style;
- var style = eStyle;
- var curveStyle = eStyle['curve-style'].value;
- var ctrlptDists = eStyle['control-point-distances'];
- var ctrlptWs = eStyle['control-point-weights'];
- var bezierN = ctrlptDists && ctrlptWs ? Math.min( ctrlptDists.value.length, ctrlptWs.value.length ) : 1;
- var stepSize = eStyle['control-point-step-size'].pfValue;
- var ctrlptDist = ctrlptDists !== undefined ? ctrlptDists.pfValue[0] : undefined;
- var ctrlptWeight = ctrlptWs.value[0];
- var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments';
-
- var swappedDirection = edge_p.source !== src;
-
- if( swappedDirection && edgeIsUnbundled ){
- ctrlptDist *= -1;
- }
-
- var srcX1 = rs.lastSrcCtlPtX;
- var srcX2 = srcPos.x;
- var srcY1 = rs.lastSrcCtlPtY;
- var srcY2 = srcPos.y;
- var srcW1 = rs.lastSrcCtlPtW;
- var srcW2 = src.outerWidth();
- var srcH1 = rs.lastSrcCtlPtH;
- var srcH2 = src.outerHeight();
-
- var tgtX1 = rs.lastTgtCtlPtX;
- var tgtX2 = tgtPos.x;
- var tgtY1 = rs.lastTgtCtlPtY;
- var tgtY2 = tgtPos.y;
- var tgtW1 = rs.lastTgtCtlPtW;
- var tgtW2 = tgt.outerWidth();
- var tgtH1 = rs.lastTgtCtlPtH;
- var tgtH2 = tgt.outerHeight();
-
- var width1 = rs.lastW;
- var width2 = eStyle['control-point-step-size'].pfValue;
-
- if( badBezier ){
- rs.badBezier = true;
- } else {
- rs.badBezier = false;
- }
-
- if( srcX1 === srcX2 && srcY1 === srcY2 && srcW1 === srcW2 && srcH1 === srcH2
- && tgtX1 === tgtX2 && tgtY1 === tgtY2 && tgtW1 === tgtW2 && tgtH1 === tgtH2
- && width1 === width2
- && ((edgeIndex1 === edgeIndex2 && numEdges1 === numEdges2) || edgeIsUnbundled) ){
- // console.log('edge ctrl pt cache HIT')
- continue; // then the control points haven't changed and we can skip calculating them
- } else {
- rs.lastSrcCtlPtX = srcX2;
- rs.lastSrcCtlPtY = srcY2;
- rs.lastSrcCtlPtW = srcW2;
- rs.lastSrcCtlPtH = srcH2;
- rs.lastTgtCtlPtX = tgtX2;
- rs.lastTgtCtlPtY = tgtY2;
- rs.lastTgtCtlPtW = tgtW2;
- rs.lastTgtCtlPtH = tgtH2;
- rs.lastEdgeIndex = edgeIndex2;
- rs.lastNumEdges = numEdges2;
- rs.lastWidth = width2;
- // console.log('edge ctrl pt cache MISS')
- }
-
- if( src === tgt ){
- // Self-edge
-
- rs.edgeType = 'self';
-
- var j = i;
- var loopDist = stepSize;
-
- if( edgeIsUnbundled ){
- j = 0;
- loopDist = ctrlptDist;
- }
-
- rs.ctrlpts = [
- srcPos.x,
- srcPos.y - (1 + Math.pow(srcH, 1.12) / 100) * loopDist * (j / 3 + 1),
-
- srcPos.x - (1 + Math.pow(srcW, 1.12) / 100) * loopDist * (j / 3 + 1),
- srcPos.y
- ];
-
- } else if(
- hasCompounds &&
- ( src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild() ) &&
- ( src.parents().anySame(tgt) || tgt.parents().anySame(src) )
- ){
- // Compound edge
-
- rs.edgeType = 'compound';
-
- // because the line approximation doesn't apply for compound beziers
- // (loop/self edges are already elided b/c of cheap src==tgt check)
- rs.badBezier = false;
-
- var j = i;
- var loopDist = stepSize;
-
- if( edgeIsUnbundled ){
- j = 0;
- loopDist = ctrlptDist;
- }
-
- var loopW = 50;
-
- var loopaPos = {
- x: srcPos.x - srcW/2,
- y: srcPos.y - srcH/2
- };
-
- var loopbPos = {
- x: tgtPos.x - tgtW/2,
- y: tgtPos.y - tgtH/2
- };
-
- var loopPos = {
- x: Math.min( loopaPos.x, loopbPos.x ),
- y: Math.min( loopaPos.y, loopbPos.y )
- };
-
- // avoids cases with impossible beziers
- var minCompoundStretch = 0.5;
- var compoundStretchA = Math.max( minCompoundStretch, Math.log(srcW * 0.01) );
- var compoundStretchB = Math.max( minCompoundStretch, Math.log(tgtW * 0.01) );
-
- rs.ctrlpts = [
- loopPos.x,
- loopPos.y - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchA,
-
- loopPos.x - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchB,
- loopPos.y
- ];
-
- } else if( curveStyle === 'segments' ){
- // Segments (multiple straight lines)
-
- rs.edgeType = 'segments';
- rs.segpts = [];
-
- var segmentWs = eStyle['segment-weights'].pfValue;
- var segmentDs = eStyle['segment-distances'].pfValue;
- var segmentsN = Math.min( segmentWs.length, segmentDs.length );
-
- for( var s = 0; s < segmentsN; s++ ){
- var w = segmentWs[s];
- var d = segmentDs[s];
-
- // d = swappedDirection ? -d : d;
- //
- // d = Math.abs(d);
-
- // var w1 = !swappedDirection ? (1 - w) : w;
- // var w2 = !swappedDirection ? w : (1 - w);
-
- var w1 = (1 - w);
- var w2 = w;
-
- var adjustedMidpt = {
- x: midptSrcPts.x1 * w1 + midptSrcPts.x2 * w2,
- y: midptSrcPts.y1 * w1 + midptSrcPts.y2 * w2
- };
-
- rs.segpts.push(
- adjustedMidpt.x + vectorNormInverse.x * d,
- adjustedMidpt.y + vectorNormInverse.y * d
- );
- }
-
- // Straight edge
- } else if (
- pairEdges.length % 2 === 1
- && i === Math.floor(pairEdges.length / 2)
- && !edgeIsUnbundled
- ){
-
- rs.edgeType = 'straight';
-
- } else {
- // (Multi)bezier
-
- var multi = edgeIsUnbundled;
-
- rs.edgeType = multi ? 'multibezier' : 'bezier';
- rs.ctrlpts = [];
-
- for( var b = 0; b < bezierN; b++ ){
- var normctrlptDist = (0.5 - pairEdges.length / 2 + i) * stepSize;
- var manctrlptDist;
- var sign = math.signum( normctrlptDist );
-
- if( multi ){
- ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
- ctrlptWeight = ctrlptWs.value[b];
- }
-
- if( edgeIsUnbundled ){ // multi or single unbundled
- manctrlptDist = ctrlptDist;
- } else {
- manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
- }
-
- var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
-
- var w1 = !swappedDirection || edgeIsUnbundled ? (1 - ctrlptWeight) : ctrlptWeight;
- var w2 = !swappedDirection || edgeIsUnbundled ? ctrlptWeight : (1 - ctrlptWeight);
-
- var adjustedMidpt = {
- x: midptSrcPts.x1 * w1 + midptSrcPts.x2 * w2,
- y: midptSrcPts.y1 * w1 + midptSrcPts.y2 * w2
- };
-
- rs.ctrlpts.push(
- adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint,
- adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint
- );
- }
-
- }
-
- // find endpts for edge
- this.findEndpoints( edge );
-
- var badStart = !is.number( rs.startX ) || !is.number( rs.startY );
- var badAStart = !is.number( rs.arrowStartX ) || !is.number( rs.arrowStartY );
- var badEnd = !is.number( rs.endX ) || !is.number( rs.endY );
- var badAEnd = !is.number( rs.arrowEndX ) || !is.number( rs.arrowEndY );
-
- var minCpADistFactor = 3;
- var arrowW = this.getArrowWidth( eStyle['width'].pfValue ) * this.arrowShapeHeight;
- var minCpADist = minCpADistFactor * arrowW;
-
- if( rs.edgeType === 'bezier' ){
- var startACpDist = math.distance( { x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.startX, y: rs.startY } );
- var closeStartACp = startACpDist < minCpADist;
- var endACpDist = math.distance( { x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.endX, y: rs.endY } );
- var closeEndACp = endACpDist < minCpADist;
-
- var overlapping = false;
-
- if( badStart || badAStart || closeStartACp ){
- overlapping = true;
-
- // project control point along line from src centre to outside the src shape
- // (otherwise intersection will yield nothing)
- var cpD = { // delta
- x: rs.ctrlpts[0] - srcPos.x,
- y: rs.ctrlpts[1] - srcPos.y
- };
- var cpL = Math.sqrt( cpD.x*cpD.x + cpD.y*cpD.y ); // length of line
- var cpM = { // normalised delta
- x: cpD.x / cpL,
- y: cpD.y / cpL
- };
- var radius = Math.max(srcW, srcH);
- var cpProj = { // *2 radius guarantees outside shape
- x: rs.ctrlpts[0] + cpM.x * 2 * radius,
- y: rs.ctrlpts[1] + cpM.y * 2 * radius
- };
-
- var srcCtrlPtIntn = srcShape.intersectLine(
- srcPos.x,
- srcPos.y,
- srcW,
- srcH,
- cpProj.x,
- cpProj.y,
- 0
- );
-
- if( closeStartACp ){
- rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
- rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
- } else {
- rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
- rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
- }
- }
-
- if( badEnd || badAEnd || closeEndACp ){
- overlapping = true;
-
- // project control point along line from tgt centre to outside the tgt shape
- // (otherwise intersection will yield nothing)
- var cpD = { // delta
- x: rs.ctrlpts[0] - tgtPos.x,
- y: rs.ctrlpts[1] - tgtPos.y
- };
- var cpL = Math.sqrt( cpD.x*cpD.x + cpD.y*cpD.y ); // length of line
- var cpM = { // normalised delta
- x: cpD.x / cpL,
- y: cpD.y / cpL
- };
- var radius = Math.max(srcW, srcH);
- var cpProj = { // *2 radius guarantees outside shape
- x: rs.ctrlpts[0] + cpM.x * 2 * radius,
- y: rs.ctrlpts[1] + cpM.y * 2 * radius
- };
-
- var tgtCtrlPtIntn = tgtShape.intersectLine(
- tgtPos.x,
- tgtPos.y,
- tgtW,
- tgtH,
- cpProj.x,
- cpProj.y,
- 0
- );
-
- if( closeEndACp ){
- rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - endACpDist);
- rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - endACpDist);
- } else {
- rs.ctrlpts[0] = tgtCtrlPtIntn[0] + cpM.x * minCpADist;
- rs.ctrlpts[1] = tgtCtrlPtIntn[1] + cpM.y * minCpADist;
- }
-
- }
-
- if( overlapping ){
- // recalc endpts
- this.findEndpoints( edge );
- }
-
- }
-
- if( rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' ){
- rs.allpts = [];
-
- rs.allpts.push( rs.startX, rs.startY );
-
- for( var b = 0; b+1 < rs.ctrlpts.length; b += 2 ){
- // ctrl pt itself
- rs.allpts.push( rs.ctrlpts[b], rs.ctrlpts[b+1] );
-
- // the midpt between ctrlpts as intermediate destination pts
- if( b + 3 < rs.ctrlpts.length ){
- rs.allpts.push( (rs.ctrlpts[b] + rs.ctrlpts[b+2])/2, (rs.ctrlpts[b+1] + rs.ctrlpts[b+3])/2 );
- }
- }
-
- rs.allpts.push( rs.endX, rs.endY );
-
- var m, mt;
- if( rs.edgeType === 'bezier' ){
- rs.midX = math.qbezierAt( rs.arrowStartX, rs.ctrlpts[0], rs.arrowEndX, 0.5 );
- rs.midY = math.qbezierAt( rs.arrowStartY, rs.ctrlpts[1], rs.arrowEndY, 0.5 );
- } else if( rs.ctrlpts.length/2 % 2 === 0 ){
- m = rs.allpts.length/2 - 1;
-
- rs.midX = rs.allpts[m];
- rs.midY = rs.allpts[m+1];
- } else {
- m = rs.allpts.length/2 - 3;
- mt = 0.5;
-
- rs.midX = math.qbezierAt( rs.allpts[m], rs.allpts[m+2], rs.allpts[m+4], mt );
- rs.midY = math.qbezierAt( rs.allpts[m+1], rs.allpts[m+3], rs.allpts[m+5], mt );
- }
-
- } else if( rs.edgeType === 'straight' ){
- // need to calc these after endpts
- rs.allpts = [ rs.startX, rs.startY, rs.endX, rs.endY ];
-
- // default midpt for labels etc
- rs.midX = ( rs.arrowStartX + rs.arrowEndX )/2;
- rs.midY = ( rs.arrowStartY + rs.arrowEndY )/2;
-
- } else if( rs.edgeType === 'segments' ){
- rs.allpts = [];
- rs.allpts.push( rs.startX, rs.startY );
- rs.allpts.push.apply( rs.allpts, rs.segpts );
- rs.allpts.push( rs.endX, rs.endY );
-
- if( rs.segpts.length % 4 === 0 ){
- var i2 = rs.segpts.length / 2;
- var i1 = i2 - 2;
-
- rs.midX = ( rs.segpts[i1] + rs.segpts[i2] ) / 2;
- rs.midY = ( rs.segpts[i1+1] + rs.segpts[i2+1] ) / 2;
- } else {
- var i1 = rs.segpts.length / 2 - 1;
-
- rs.midX = rs.segpts[i1];
- rs.midY = rs.segpts[i1+1];
- }
-
-
- }
-
- this.projectLines( edge );
- this.calculateArrowAngles( edge );
- this.recalculateEdgeLabelProjection( edge );
-
- }
- }
-
- for( var i = 0; i < haystackEdges.length; i++ ){
- var edge = haystackEdges[i];
- var _p = edge._private;
- var style = _p.style;
- var rscratch = _p.rscratch;
- var rs = rscratch;
-
- if( !rscratch.haystack ){
- var angle = Math.random() * 2 * Math.PI;
-
- rscratch.source = {
- x: Math.cos(angle),
- y: Math.sin(angle)
- };
-
- var angle = Math.random() * 2 * Math.PI;
-
- rscratch.target = {
- x: Math.cos(angle),
- y: Math.sin(angle)
- };
-
- }
-
- var src = _p.source;
- var tgt = _p.target;
- var srcPos = src._private.position;
- var tgtPos = tgt._private.position;
- var srcW = src.width();
- var tgtW = tgt.width();
- var srcH = src.height();
- var tgtH = tgt.height();
- var radius = style['haystack-radius'].value;
- var halfRadius = radius/2; // b/c have to half width/height
-
- rs.haystackPts = rs.allpts = [
- rs.source.x * srcW * halfRadius + srcPos.x,
- rs.source.y * srcH * halfRadius + srcPos.y,
- rs.target.x * tgtW * halfRadius + tgtPos.x,
- rs.target.y * tgtH * halfRadius + tgtPos.y
- ];
-
- rs.midX = (rs.allpts[0] + rs.allpts[2])/2;
- rs.midY = (rs.allpts[1] + rs.allpts[3])/2;
-
- // always override as haystack in case set to different type previously
- rscratch.edgeType = 'haystack';
- rscratch.haystack = true;
-
- this.projectLines( edge );
- this.calculateArrowAngles( edge );
- this.recalculateEdgeLabelProjection( edge );
- }
-
- for( var i = 0 ; i < autorotateEdges.length; i++ ){
- var edge = autorotateEdges[i];
- var rs = edge._private.rscratch;
-
- rs.labelAngle = Math.atan( rs.midDispY / rs.midDispX );
- }
-
- return hashTable;
-};
-
-var getAngleFromDisp = function( dispX, dispY ){
- return Math.atan2( dispY, dispX ) - Math.PI/2;
-};
-
-BRp.calculateArrowAngles = function( edge ){
- var rs = edge._private.rscratch;
- var isHaystack = rs.edgeType === 'haystack';
- var isMultibezier = rs.edgeType === 'multibezier';
- var isSegments = rs.edgeType === 'segments';
- var isCompound = rs.edgeType === 'compound';
- var isSelf = rs.edgeType === 'self';
-
- // Displacement gives direction for arrowhead orientation
- var dispX, dispY;
- var startX, startY, endX, endY;
-
- var srcPos = edge.source().position();
- var tgtPos = edge.target().position();
-
- if( isHaystack ){
- startX = rs.haystackPts[0];
- startY = rs.haystackPts[1];
- endX = rs.haystackPts[2];
- endY = rs.haystackPts[3];
- } else {
- startX = rs.arrowStartX;
- startY = rs.arrowStartY;
- endX = rs.arrowEndX;
- endY = rs.arrowEndY;
- }
-
- // source
- //
-
- dispX = srcPos.x - startX;
- dispY = srcPos.y - startY;
-
- rs.srcArrowAngle = getAngleFromDisp( dispX, dispY );
-
- // mid target
- //
-
- var midX = rs.midX;
- var midY = rs.midY;
-
- if( isHaystack ){
- midX = ( startX + endX )/2;
- midY = ( startY + endY )/2;
- }
-
- dispX = endX - startX;
- dispY = endY - startY;
-
- if( isSelf ){
- dispX = -1;
- dispY = 1;
- } else if( isSegments ){
- var pts = rs.allpts;
-
- if( pts.length / 2 % 2 === 0 ){
- var i2 = pts.length / 2;
- var i1 = i2 - 2;
-
- dispX = ( pts[i2] - pts[i1] );
- dispY = ( pts[i2+1] - pts[i1+1] );
- } else {
- var i2 = pts.length / 2 - 1;
- var i1 = i2 - 2;
- var i3 = i2 + 2;
-
- dispX = ( pts[i2] - pts[i1] );
- dispY = ( pts[i2+1] - pts[i1+1] );
- }
- } else if( isMultibezier || isCompound ){
- var pts = rs.allpts;
- var cpts = rs.ctrlpts;
- var bp0x, bp0y;
- var bp1x, bp1y;
-
- if( cpts.length / 2 % 2 === 0 ){
- var p0 = pts.length / 2 - 1; // startpt
- var ic = p0 + 2;
- var p1 = ic + 2;
-
- bp0x = math.qbezierAt( pts[p0], pts[ic], pts[p1], 0.0 );
- bp0y = math.qbezierAt( pts[p0+1], pts[ic+1], pts[p1+1], 0.0 );
-
- bp1x = math.qbezierAt( pts[p0], pts[ic], pts[p1], 0.0001 );
- bp1y = math.qbezierAt( pts[p0+1], pts[ic+1], pts[p1+1], 0.0001 );
- } else {
- var ic = pts.length / 2 - 1; // ctrpt
- var p0 = ic - 2; // startpt
- var p1 = ic + 2; // endpt
-
- bp0x = math.qbezierAt( pts[p0], pts[ic], pts[p1], 0.4999 );
- bp0y = math.qbezierAt( pts[p0+1], pts[ic+1], pts[p1+1], 0.4999 );
-
- bp1x = math.qbezierAt( pts[p0], pts[ic], pts[p1], 0.5 );
- bp1y = math.qbezierAt( pts[p0+1], pts[ic+1], pts[p1+1], 0.5 );
- }
-
- dispX = ( bp1x - bp0x );
- dispY = ( bp1y - bp0y );
- }
-
- rs.midtgtArrowAngle = getAngleFromDisp( dispX, dispY );
-
- rs.midDispX = dispX;
- rs.midDispY = dispY;
-
- // mid source
- //
-
- dispX *= -1;
- dispY *= -1;
-
- if( isSegments ){
- var pts = rs.allpts;
-
- if( pts.length / 2 % 2 === 0 ){
- // already ok
- } else {
- var i2 = pts.length / 2 - 1;
- var i3 = i2 + 2;
-
- dispX = -( pts[i3] - pts[i2] );
- dispY = -( pts[i3+1] - pts[i2+1] );
- }
- }
-
- rs.midsrcArrowAngle = getAngleFromDisp( dispX, dispY );
-
- // target
- //
-
- dispX = tgtPos.x - endX;
- dispY = tgtPos.y - endY;
-
- rs.tgtArrowAngle = getAngleFromDisp( dispX, dispY );
-};
-
-
-BRp.findEndpoints = function( edge ){
- var r = this;
- var intersect;
-
- var source = edge.source()[0];
- var target = edge.target()[0];
-
- var src_p = source._private;
- var tgt_p = target._private;
-
- var srcPos = src_p.position;
- var tgtPos = tgt_p.position;
-
- var tgtArShape = edge._private.style['target-arrow-shape'].value;
- var srcArShape = edge._private.style['source-arrow-shape'].value;
-
- var rs = edge._private.rscratch;
-
- var et = rs.edgeType;
- var bezier = et === 'bezier' || et === 'multibezier' || et === 'self' || et === 'compound';
- var multi = et !== 'bezier';
- var lines = et === 'straight' || et === 'segments';
- var segments = et === 'segments';
-
- var p1, p2;
-
- if( bezier ){
- var cpStart = [ rs.ctrlpts[0], rs.ctrlpts[1] ];
- var cpEnd = multi ? [ rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1] ] : cpStart;
-
- p1 = cpEnd;
- p2 = cpStart;
- } else if( lines ){
- var srcArrowFromPt = !segments ? [ tgtPos.x, tgtPos.y ] : rs.segpts.slice( 0, 2 );
- var tgtArrowFromPt = !segments ? [ srcPos.x, srcPos.y ] : rs.segpts.slice( rs.segpts.length - 2 );
-
- p1 = tgtArrowFromPt;
- p2 = srcArrowFromPt;
- }
-
- intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(
- tgtPos.x,
- tgtPos.y,
- target.outerWidth(),
- target.outerHeight(),
- p1[0],
- p1[1],
- 0
- );
-
- var arrowEnd = math.shortenIntersection(intersect, p1,
- r.arrowShapes[tgtArShape].spacing(edge));
- var edgeEnd = math.shortenIntersection(intersect, p1,
- r.arrowShapes[tgtArShape].gap(edge));
-
- rs.endX = edgeEnd[0];
- rs.endY = edgeEnd[1];
-
- rs.arrowEndX = arrowEnd[0];
- rs.arrowEndY = arrowEnd[1];
-
- intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(
- srcPos.x,
- srcPos.y,
- source.outerWidth(),
- source.outerHeight(),
- p2[0],
- p2[1],
- 0
- );
-
- var arrowStart = math.shortenIntersection(
- intersect, p2,
- r.arrowShapes[srcArShape].spacing(edge)
- );
- var edgeStart = math.shortenIntersection(
- intersect, p2,
- r.arrowShapes[srcArShape].gap(edge)
- );
-
- rs.startX = edgeStart[0];
- rs.startY = edgeStart[1];
-
- rs.arrowStartX = arrowStart[0];
- rs.arrowStartY = arrowStart[1];
-
- if( lines ){
- if( !is.number(rs.startX) || !is.number(rs.startY) || !is.number(rs.endX) || !is.number(rs.endY) ){
- rs.badLine = true;
- } else {
- rs.badLine = false;
- }
- }
-};
-
-BRp.getArrowWidth = BRp.getArrowHeight = function(edgeWidth) {
- var cache = this.arrowWidthCache = this.arrowWidthCache || {};
-
- var cachedVal = cache[edgeWidth];
- if( cachedVal ){
- return cachedVal;
- }
-
- cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29);
- cache[edgeWidth] = cachedVal;
-
- return cachedVal;
-};
-
-module.exports = BRp;
-
-},{"../../../collection/zsort":29,"../../../is":77,"../../../math":79}],57:[function(_dereq_,module,exports){
-'use strict';
-
-var BRp = {};
-
-BRp.getCachedImage = function(url, onLoad) {
- var r = this;
- var imageCache = r.imageCache = r.imageCache || {};
-
- if( imageCache[url] && imageCache[url].image ){
- return imageCache[url].image;
- }
-
- var cache = imageCache[url] = imageCache[url] || {};
-
- var image = cache.image = new Image();
- image.addEventListener('load', onLoad);
- image.src = url;
-
- return image;
-};
-
-module.exports = BRp;
-
-},{}],58:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../../is');
-var util = _dereq_('../../../util');
-
-var BaseRenderer = function(){};
-var BR = BaseRenderer;
-var BRp = BR.prototype;
-
-BRp.clientFunctions = [ 'redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl' ];
-
-BRp.init = function( options ){
- var r = this;
-
- r.options = options;
-
- r.cy = options.cy;
-
- r.container = options.cy.container();
-
- r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
-
- //--Pointer-related data
- r.hoverData = {down: null, last: null,
- downTime: null, triggerMode: null,
- dragging: false,
- initialPan: [null, null], capture: false};
-
- r.dragData = {possibleDragElements: []};
-
- r.touchData = {
- start: null, capture: false,
-
- // These 3 fields related to tap, taphold events
- startPosition: [null, null, null, null, null, null],
- singleTouchStartTime: null,
- singleTouchMoved: true,
-
- now: [null, null, null, null, null, null],
- earlier: [null, null, null, null, null, null]
- };
-
- r.redraws = 0;
- r.showFps = options.showFps;
-
- r.hideEdgesOnViewport = options.hideEdgesOnViewport;
- r.hideLabelsOnViewport = options.hideLabelsOnViewport;
- r.textureOnViewport = options.textureOnViewport;
- r.wheelSensitivity = options.wheelSensitivity;
- r.motionBlurEnabled = options.motionBlur; // on by default
- r.forcedPixelRatio = options.pixelRatio;
- r.motionBlur = true; // for initial kick off
- r.motionBlurOpacity = options.motionBlurOpacity;
- r.motionBlurTransparency = 1 - r.motionBlurOpacity;
- r.motionBlurPxRatio = 1;
- r.mbPxRBlurry = 1; //0.8;
- r.minMbLowQualFrames = 4;
- r.fullQualityMb = false;
- r.clearedForMotionBlur = [];
- r.desktopTapThreshold = options.desktopTapThreshold;
- r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
- r.touchTapThreshold = options.touchTapThreshold;
- r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
- r.tapholdDuration = 500;
-
- r.bindings = [];
-
- r.registerNodeShapes();
- r.registerArrowShapes();
- r.load();
-};
-
-BRp.notify = function(params) {
- var types;
- var r = this;
-
- if( is.array( params.type ) ){
- types = params.type;
-
- } else {
- types = [ params.type ];
- }
-
- for( var i = 0; i < types.length; i++ ){
- var type = types[i];
-
- switch( type ){
- case 'destroy':
- r.destroy();
- return;
-
- case 'add':
- case 'remove':
- case 'load':
- r.updateElementsCache();
- break;
-
- case 'viewport':
- r.redrawHint('select', true);
- break;
-
- case 'style':
- r.updateCachedZSortedEles();
- break;
- }
-
- if( type === 'load' || type === 'resize' ){
- r.invalidateContainerClientCoordsCache();
- r.matchCanvasSize(r.container);
- }
- } // for
-
- r.redrawHint('eles', true);
- r.redrawHint('drag', true);
-
- this.startRenderLoop();
-
- this.redraw();
-};
-
-BRp.destroy = function(){
- this.destroyed = true;
-
- this.cy.stopAnimationLoop();
-
- for( var i = 0; i < this.bindings.length; i++ ){
- var binding = this.bindings[i];
- var b = binding;
-
- b.target.removeEventListener(b.event, b.handler, b.useCapture);
- }
-
- if( this.removeObserver ){
- this.removeObserver.disconnect();
- }
-
- if( this.labelCalcDiv ){
- try{
- document.body.removeChild(this.labelCalcDiv);
- } catch(e){
- // ie10 issue #1014
- }
- }
-};
-
-[
- _dereq_('./arrow-shapes'),
- _dereq_('./cached-eles'),
- _dereq_('./coord-ele-math'),
- _dereq_('./images'),
- _dereq_('./load-listeners'),
- _dereq_('./node-shapes'),
- _dereq_('./redraw')
-].forEach(function( props ){
- util.extend( BRp, props );
-});
-
-module.exports = BR;
-
-},{"../../../is":77,"../../../util":94,"./arrow-shapes":54,"./cached-eles":55,"./coord-ele-math":56,"./images":57,"./load-listeners":59,"./node-shapes":60,"./redraw":61}],59:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../../is');
-var util = _dereq_('../../../util');
-var Event = _dereq_('../../../event');
-var Collection = _dereq_('../../../collection');
-
-var BRp = {};
-
-BRp.registerBinding = function(target, event, handler, useCapture){
- this.bindings.push({
- target: target,
- event: event,
- handler: handler,
- useCapture: useCapture
- });
-
- target.addEventListener(event, handler, useCapture);
-};
-
-BRp.nodeIsDraggable = function(node) {
- if (node._private.style['opacity'].value !== 0
- && node._private.style['visibility'].value == 'visible'
- && node._private.style['display'].value == 'element'
- && !node.locked()
- && node.grabbable() ) {
-
- return true;
- }
-
- return false;
-};
-
-BRp.load = function() {
- var r = this;
-
- var triggerEvents = function( target, names, e, props ){
- if( target == null ){
- target = r.cy;
- }
-
- for( var i = 0; i < names.length; i++ ){
- var name = names[i];
-
- var event = Event( e, util.extend({ type: name }, props) );
- target.trigger( event );
- }
- };
-
- var isMultSelKeyDown = function( e ){
- return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
- };
-
- var getDragListIds = function(opts){
- var listHasId;
-
- if( opts.addToList && r.cy.hasCompoundNodes() ){ // only needed for compound graphs
- if( !opts.addToList.hasId ){ // build ids lookup if doesn't already exist
- opts.addToList.hasId = {};
-
- for( var i = 0; i < opts.addToList.length; i++ ){
- var ele = opts.addToList[i];
-
- opts.addToList.hasId[ ele.id() ] = true;
- }
- }
-
- listHasId = opts.addToList.hasId;
- }
-
- return listHasId || {};
- };
-
- // helper function to determine which child nodes and inner edges
- // of a compound node to be dragged as well as the grabbed and selected nodes
- var addDescendantsToDrag = function(node, opts){
- if( !node._private.cy.hasCompoundNodes() ){
- return;
- }
-
- if( opts.inDragLayer == null && opts.addToList == null ){ return; } // nothing to do
-
- var listHasId = getDragListIds( opts );
-
- var innerNodes = node.descendants();
-
- for( var i = 0; i < innerNodes.size(); i++ ){
- var iNode = innerNodes[i];
- var _p = iNode._private;
-
- if( opts.inDragLayer ){
- _p.rscratch.inDragLayer = true;
- }
-
- if( opts.addToList && !listHasId[ iNode.id() ] ){
- opts.addToList.push( iNode );
- listHasId[ iNode.id() ] = true;
-
- _p.grabbed = true;
- }
-
- var edges = _p.edges;
- for( var j = 0; opts.inDragLayer && j < edges.length; j++ ){
- edges[j]._private.rscratch.inDragLayer = true;
- }
- }
- };
-
- // adds the given nodes, and its edges to the drag layer
- var addNodeToDrag = function(node, opts){
-
- var _p = node._private;
- var listHasId = getDragListIds( opts );
-
- if( opts.inDragLayer ){
- _p.rscratch.inDragLayer = true;
- }
-
- if( opts.addToList && !listHasId[ node.id() ] ){
- opts.addToList.push( node );
- listHasId[ node.id() ] = true;
-
- _p.grabbed = true;
- }
-
- var edges = _p.edges;
- for( var i = 0; opts.inDragLayer && i < edges.length; i++ ){
- edges[i]._private.rscratch.inDragLayer = true;
- }
-
- addDescendantsToDrag( node, opts ); // always add to drag
-
- // also add nodes and edges related to the topmost ancestor
- updateAncestorsInDragLayer( node, {
- inDragLayer: opts.inDragLayer
- } );
- };
-
- var freeDraggedElements = function( draggedElements ){
- if( !draggedElements ){ return; }
-
- for (var i=0; i < draggedElements.length; i++) {
-
- var dEi_p = draggedElements[i]._private;
-
- if(dEi_p.group === 'nodes') {
- dEi_p.rscratch.inDragLayer = false;
- dEi_p.grabbed = false;
-
- var sEdges = dEi_p.edges;
- for( var j = 0; j < sEdges.length; j++ ){ sEdges[j]._private.rscratch.inDragLayer = false; }
-
- // for compound nodes, also remove related nodes and edges from the drag layer
- updateAncestorsInDragLayer(draggedElements[i], { inDragLayer: false });
-
- } else if( dEi_p.group === 'edges' ){
- dEi_p.rscratch.inDragLayer = false;
- }
-
- }
- };
-
- // helper function to determine which ancestor nodes and edges should go
- // to the drag layer (or should be removed from drag layer).
- var updateAncestorsInDragLayer = function(node, opts) {
-
- if( opts.inDragLayer == null && opts.addToList == null ){ return; } // nothing to do
-
- // find top-level parent
- var parent = node;
-
- if( !node._private.cy.hasCompoundNodes() ){
- return;
- }
-
- while( parent.parent().nonempty() ){
- parent = parent.parent()[0];
- }
-
- // no parent node: no nodes to add to the drag layer
- if( parent == node ){
- return;
- }
-
- var nodes = parent.descendants()
- .merge( parent )
- .unmerge( node )
- .unmerge( node.descendants() )
- ;
-
- var edges = nodes.connectedEdges();
-
- var listHasId = getDragListIds( opts );
-
- for( var i = 0; i < nodes.size(); i++ ){
- if( opts.inDragLayer !== undefined ){
- nodes[i]._private.rscratch.inDragLayer = opts.inDragLayer;
- }
-
- if( opts.addToList && !listHasId[ nodes[i].id() ] ){
- opts.addToList.push( nodes[i] );
- listHasId[ nodes[i].id() ] = true;
-
- nodes[i]._private.grabbed = true;
- }
- }
-
- for( var j = 0; opts.inDragLayer !== undefined && j < edges.length; j++ ) {
- edges[j]._private.rscratch.inDragLayer = opts.inDragLayer;
- }
- };
-
- if( typeof MutationObserver !== 'undefined' ){
- r.removeObserver = new MutationObserver(function( mutns ){
- for( var i = 0; i < mutns.length; i++ ){
- var mutn = mutns[i];
- var rNodes = mutn.removedNodes;
-
- if( rNodes ){ for( var j = 0; j < rNodes.length; j++ ){
- var rNode = rNodes[j];
-
- if( rNode === r.container ){
- r.destroy();
- break;
- }
- } }
- }
- });
-
- if( r.container.parentNode ){
- r.removeObserver.observe( r.container.parentNode, { childList: true } );
- }
- } else {
- r.registerBinding(r.container, 'DOMNodeRemoved', function(e){
- r.destroy();
- });
- }
-
-
-
- // auto resize
- r.registerBinding(window, 'resize', util.debounce( function(e) {
- r.invalidateContainerClientCoordsCache();
-
- r.matchCanvasSize(r.container);
- r.redrawHint('eles', true);
- r.redraw();
- }, 100 ) );
-
- var invalCtnrBBOnScroll = function(domEle){
- r.registerBinding(domEle, 'scroll', function(e){
- r.invalidateContainerClientCoordsCache();
- } );
- };
-
- var bbCtnr = r.cy.container();
-
- for( ;; ){
-
- invalCtnrBBOnScroll( bbCtnr );
-
- if( bbCtnr.parentNode ){
- bbCtnr = bbCtnr.parentNode;
- } else {
- break;
- }
-
- }
-
- // stop right click menu from appearing on cy
- r.registerBinding(r.container, 'contextmenu', function(e){
- e.preventDefault();
- });
-
- var inBoxSelection = function(){
- return r.selection[4] !== 0;
- };
-
- // Primary key
- r.registerBinding(r.container, 'mousedown', function(e) {
- e.preventDefault();
- r.hoverData.capture = true;
- r.hoverData.which = e.which;
-
- var cy = r.cy;
- var pos = r.projectIntoViewport(e.clientX, e.clientY);
- var select = r.selection;
- var near = r.findNearestElement(pos[0], pos[1], true, false);
- var draggedElements = r.dragData.possibleDragElements;
-
- r.hoverData.mdownPos = pos;
-
- var checkForTaphold = function(){
- r.hoverData.tapholdCancelled = false;
-
- clearTimeout( r.hoverData.tapholdTimeout );
-
- r.hoverData.tapholdTimeout = setTimeout(function(){
-
- if( r.hoverData.tapholdCancelled ){
- return;
- } else {
- var ele = r.hoverData.down;
-
- if( ele ){
- ele.trigger( Event(e, {
- type: 'taphold',
- cyPosition: { x: pos[0], y: pos[1] }
- }) );
- } else {
- cy.trigger( Event(e, {
- type: 'taphold',
- cyPosition: { x: pos[0], y: pos[1] }
- }) );
- }
- }
-
- }, r.tapholdDuration);
- };
-
- // Right click button
- if( e.which == 3 ){
-
- r.hoverData.cxtStarted = true;
-
- var cxtEvt = Event(e, {
- type: 'cxttapstart',
- cyPosition: { x: pos[0], y: pos[1] }
- });
-
- if( near ){
- near.activate();
- near.trigger( cxtEvt );
-
- r.hoverData.down = near;
- } else {
- cy.trigger( cxtEvt );
- }
-
- r.hoverData.downTime = (new Date()).getTime();
- r.hoverData.cxtDragged = false;
-
- // Primary button
- } else if (e.which == 1) {
-
- if( near ){
- near.activate();
- }
-
- // Element dragging
- {
- // If something is under the cursor and it is draggable, prepare to grab it
- if (near != null) {
-
- if( r.nodeIsDraggable(near) ){
-
- var grabEvent = Event(e, {
- type: 'grab',
- cyPosition: { x: pos[0], y: pos[1] }
- });
-
- if ( near.isNode() && !near.selected() ){
-
- draggedElements = r.dragData.possibleDragElements = [];
- addNodeToDrag( near, { addToList: draggedElements } );
-
- near.trigger(grabEvent);
-
- } else if ( near.isNode() && near.selected() ){
- draggedElements = r.dragData.possibleDragElements = [ ];
-
- var selectedNodes = cy.$(function(){ return this.isNode() && this.selected(); });
-
- for( var i = 0; i < selectedNodes.length; i++ ){
-
- // Only add this selected node to drag if it is draggable, eg. has nonzero opacity
- if( r.nodeIsDraggable( selectedNodes[i] ) ){
- addNodeToDrag( selectedNodes[i], { addToList: draggedElements } );
- }
- }
-
- near.trigger( grabEvent );
- }
-
- r.redrawHint('eles', true);
- r.redrawHint('drag', true);
-
- }
-
- }
-
- r.hoverData.down = near;
- r.hoverData.downTime = (new Date()).getTime();
- }
-
- triggerEvents( near, ['mousedown', 'tapstart', 'vmousedown'], e, {
- cyPosition: { x: pos[0], y: pos[1] }
- } );
-
- if ( near == null ) {
- select[4] = 1;
-
- r.data.bgActivePosistion = {
- x: pos[0],
- y: pos[1]
- };
-
- r.redrawHint('select', true);
-
- r.redraw();
- } else if( near.isEdge() ){
- select[4] = 1; // for future pan
- }
-
- checkForTaphold();
-
- }
-
- // Initialize selection box coordinates
- select[0] = select[2] = pos[0];
- select[1] = select[3] = pos[1];
-
- }, false);
-
- r.registerBinding(window, 'mousemove', function(e) {
- var preventDefault = false;
- var capture = r.hoverData.capture;
-
- // save cycles if mouse events aren't to be captured
- if ( !capture ){
- var containerPageCoords = r.findContainerClientCoords();
-
- if (e.clientX > containerPageCoords[0] && e.clientX < containerPageCoords[0] + r.canvasWidth
- && e.clientY > containerPageCoords[1] && e.clientY < containerPageCoords[1] + r.canvasHeight
- ) {
- // inside container bounds so OK
- } else {
- return;
- }
-
- var cyContainer = r.container;
- var target = e.target;
- var tParent = target.parentNode;
- var containerIsTarget = false;
-
- while( tParent ){
- if( tParent === cyContainer ){
- containerIsTarget = true;
- break;
- }
-
- tParent = tParent.parentNode;
- }
-
- if( !containerIsTarget ){ return; } // if target is outisde cy container, then this event is not for us
- }
-
- var cy = r.cy;
- var zoom = cy.zoom();
- var pos = r.projectIntoViewport(e.clientX, e.clientY);
- var select = r.selection;
-
- var near = null;
- if( !r.hoverData.draggingEles ){
- near = r.findNearestElement(pos[0], pos[1], true, false);
- }
- var last = r.hoverData.last;
- var down = r.hoverData.down;
-
- var disp = [pos[0] - select[2], pos[1] - select[3]];
-
- var draggedElements = r.dragData.possibleDragElements;
-
- var dx = select[2] - select[0];
- var dx2 = dx * dx;
- var dy = select[3] - select[1];
- var dy2 = dy * dy;
- var dist2 = dx2 + dy2;
- var rdist2 = dist2 * zoom * zoom;
-
- var multSelKeyDown = isMultSelKeyDown( e );
-
- r.hoverData.tapholdCancelled = true;
-
- var updateDragDelta = function(){
- var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
-
- if( dragDelta.length === 0 ){
- dragDelta.push( disp[0] );
- dragDelta.push( disp[1] );
- } else {
- dragDelta[0] += disp[0];
- dragDelta[1] += disp[1];
- }
- };
-
-
- preventDefault = true;
-
- triggerEvents( near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
- cyPosition: { x: pos[0], y: pos[1] }
- } );
-
- // trigger context drag if rmouse down
- if( r.hoverData.which === 3 ){
- var cxtEvt = Event(e, {
- type: 'cxtdrag',
- cyPosition: { x: pos[0], y: pos[1] }
- });
-
- if( down ){
- down.trigger( cxtEvt );
- } else {
- cy.trigger( cxtEvt );
- }
-
- r.hoverData.cxtDragged = true;
-
- if( !r.hoverData.cxtOver || near !== r.hoverData.cxtOver ){
-
- if( r.hoverData.cxtOver ){
- r.hoverData.cxtOver.trigger( Event(e, {
- type: 'cxtdragout',
- cyPosition: { x: pos[0], y: pos[1] }
- }) );
- }
-
- r.hoverData.cxtOver = near;
-
- if( near ){
- near.trigger( Event(e, {
- type: 'cxtdragover',
- cyPosition: { x: pos[0], y: pos[1] }
- }) );
- }
-
- }
-
- // Check if we are drag panning the entire graph
- } else if (r.hoverData.dragging) {
- preventDefault = true;
-
- if( cy.panningEnabled() && cy.userPanningEnabled() ){
- var deltaP;
-
- if( r.hoverData.justStartedPan ){
- var mdPos = r.hoverData.mdownPos;
-
- deltaP = {
- x: ( pos[0] - mdPos[0] ) * zoom,
- y: ( pos[1] - mdPos[1] ) * zoom
- };
-
- r.hoverData.justStartedPan = false;
-
- } else {
- deltaP = {
- x: disp[0] * zoom,
- y: disp[1] * zoom
- };
-
- }
-
- cy.panBy( deltaP );
-
- r.hoverData.dragged = true;
- }
-
- // Needs reproject due to pan changing viewport
- pos = r.projectIntoViewport(e.clientX, e.clientY);
-
- // Checks primary button down & out of time & mouse not moved much
- } else if(
- select[4] == 1 && (down == null || down.isEdge())
- ){
-
- if( !r.hoverData.dragging && cy.boxSelectionEnabled() && ( multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled() ) ){
- r.data.bgActivePosistion = undefined;
- r.hoverData.selecting = true;
-
- r.redrawHint('select', true);
- r.redraw();
-
- } else if( !r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled() ){
- r.hoverData.dragging = true;
- r.hoverData.justStartedPan = true;
- select[4] = 0;
-
- r.data.bgActivePosistion = {
- x: pos[0],
- y: pos[1]
- };
-
- r.redrawHint('select', true);
- r.redraw();
- }
-
- if( down && down.isEdge() && down.active() ){ down.unactivate(); }
-
- } else {
- if( down && down.isEdge() && down.active() ){ down.unactivate(); }
-
- if (near != last) {
-
- if (last) {
- triggerEvents( last, ['mouseout', 'tapdragout'], e, {
- cyPosition: { x: pos[0], y: pos[1] }
- } );
- }
-
- if (near) {
- triggerEvents( near, ['mouseover', 'tapdragover'], e, {
- cyPosition: { x: pos[0], y: pos[1] }
- } );
- }
-
- r.hoverData.last = near;
- }
-
- if( down && down.isNode() && r.nodeIsDraggable(down) ){
-
- if( rdist2 >= r.desktopTapThreshold2 ){ // then drag
-
- var justStartedDrag = !r.dragData.didDrag;
-
- if( justStartedDrag ) {
- r.redrawHint('eles', true);
- }
-
- r.dragData.didDrag = true; // indicate that we actually did drag the node
-
- var toTrigger = [];
-
- for( var i = 0; i < draggedElements.length; i++ ){
- var dEle = draggedElements[i];
-
- // now, add the elements to the drag layer if not done already
- if( !r.hoverData.draggingEles ){
- addNodeToDrag( dEle, { inDragLayer: true } );
- }
-
- // Locked nodes not draggable, as well as non-visible nodes
- if( dEle.isNode() && r.nodeIsDraggable(dEle) && dEle.grabbed() ){
- var dPos = dEle._private.position;
-
- toTrigger.push( dEle );
-
- if( is.number(disp[0]) && is.number(disp[1]) ){
- var updatePos = !dEle.isParent();
-
- if( updatePos ){
- dPos.x += disp[0];
- dPos.y += disp[1];
- }
-
- if( justStartedDrag ){
- var dragDelta = r.hoverData.dragDelta;
-
- if( updatePos && is.number(dragDelta[0]) && is.number(dragDelta[1]) ){
- dPos.x += dragDelta[0];
- dPos.y += dragDelta[1];
- }
- }
- }
-
- }
- }
-
- r.hoverData.draggingEles = true;
-
- var tcol = (Collection(cy, toTrigger));
-
- tcol.updateCompoundBounds();
- tcol.trigger('position drag');
-
- r.redrawHint('drag', true);
- r.redraw();
-
- } else { // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
- updateDragDelta();
- }
- }
-
- // prevent the dragging from triggering text selection on the page
- preventDefault = true;
- }
-
- select[2] = pos[0]; select[3] = pos[1];
-
- if( preventDefault ){
- if(e.stopPropagation) e.stopPropagation();
- if(e.preventDefault) e.preventDefault();
- return false;
- }
- }, false);
-
- r.registerBinding(window, 'mouseup', function(e) {
- var capture = r.hoverData.capture;
- if (!capture) { return; }
- r.hoverData.capture = false;
-
- var cy = r.cy; var pos = r.projectIntoViewport(e.clientX, e.clientY); var select = r.selection;
- var near = r.findNearestElement(pos[0], pos[1], true, false);
- var draggedElements = r.dragData.possibleDragElements; var down = r.hoverData.down;
- var multSelKeyDown = isMultSelKeyDown( e );
-
- if( r.data.bgActivePosistion ){
- r.redrawHint('select', true);
- r.redraw();
- }
-
- r.hoverData.tapholdCancelled = true;
-
- r.data.bgActivePosistion = undefined; // not active bg now
-
- if( down ){
- down.unactivate();
- }
-
- if( r.hoverData.which === 3 ){
- var cxtEvt = Event(e, {
- type: 'cxttapend',
- cyPosition: { x: pos[0], y: pos[1] }
- });
-
- if( down ){
- down.trigger( cxtEvt );
- } else {
- cy.trigger( cxtEvt );
- }
-
- if( !r.hoverData.cxtDragged ){
- var cxtTap = Event(e, {
- type: 'cxttap',
- cyPosition: { x: pos[0], y: pos[1] }
- });
-
- if( down ){
- down.trigger( cxtTap );
- } else {
- cy.trigger( cxtTap );
- }
- }
-
- r.hoverData.cxtDragged = false;
- r.hoverData.which = null;
-
- } else if( r.hoverData.which === 1 ) {
-
- // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
- if ( (down == null) // not mousedown on node
- && !r.dragData.didDrag // didn't move the node around
- && !r.hoverData.selecting // not box selection
- && !r.hoverData.dragged // didn't pan
- && !isMultSelKeyDown( e )
- ) {
-
- cy.$(function(){
- return this.selected();
- }).unselect();
-
- if (draggedElements.length > 0) {
- r.redrawHint('eles', true);
- }
-
- r.dragData.possibleDragElements = draggedElements = [];
- }
-
- triggerEvents( near, ['mouseup', 'tapend', 'vmouseup'], e, {
- cyPosition: { x: pos[0], y: pos[1] }
- } );
-
- if(
- !r.dragData.didDrag // didn't move a node around
- && !r.hoverData.dragged // didn't pan
- ){
- triggerEvents( near, ['click', 'tap', 'vclick'], e, {
- cyPosition: { x: pos[0], y: pos[1] }
- } );
- }
-
- // Single selection
- if( near == down && !r.dragData.didDrag && !r.hoverData.selecting ){
- if( near != null && near._private.selectable ){
-
- if( r.hoverData.dragging ){
- // if panning, don't change selection state
- } else if( cy.selectionType() === 'additive' || multSelKeyDown ){
- if( near.selected() ){
- near.unselect();
- } else {
- near.select();
- }
- } else {
- if( !multSelKeyDown ){
- cy.$(':selected').unmerge( near ).unselect();
- near.select();
- }
- }
-
- r.redrawHint('eles', true);
- }
- }
-
- if ( r.hoverData.selecting ) {
- var newlySelected = [];
- var box = r.getAllInBox( select[0], select[1], select[2], select[3] );
-
- r.redrawHint('select', true);
-
- if( box.length > 0 ) {
- r.redrawHint('eles', true);
- }
-
- for( var i = 0; i < box.length; i++ ){
- if( box[i]._private.selectable ){
- newlySelected.push( box[i] );
- }
- }
-
- var newlySelCol = Collection( cy, newlySelected );
-
- if( cy.selectionType() === 'additive' ){
- newlySelCol.select();
- } else {
- if( !multSelKeyDown ){
- cy.$(':selected').unmerge( newlySelCol ).unselect();
- }
-
- newlySelCol.select();
- }
-
- // always need redraw in case eles unselectable
- r.redraw();
-
- }
-
- // Cancel drag pan
- if( r.hoverData.dragging ){
- r.hoverData.dragging = false;
-
- r.redrawHint('select', true);
- r.redrawHint('eles', true);
-
- r.redraw();
- }
-
- if (!select[4]) {
-
-
- r.redrawHint('drag', true);
- r.redrawHint('eles', true);
-
- freeDraggedElements( draggedElements );
-
- if( down ){ down.trigger('free'); }
- }
-
- } // else not right mouse
-
- select[4] = 0; r.hoverData.down = null;
-
- r.hoverData.cxtStarted = false;
- r.hoverData.draggingEles = false;
- r.hoverData.selecting = false;
- r.dragData.didDrag = false;
- r.hoverData.dragged = false;
- r.hoverData.dragDelta = [];
-
- }, false);
-
- var wheelHandler = function(e) {
-
-
- if( r.scrollingPage ){ return; } // while scrolling, ignore wheel-to-zoom
-
- var cy = r.cy;
- var pos = r.projectIntoViewport(e.clientX, e.clientY);
- var rpos = [pos[0] * cy.zoom() + cy.pan().x,
- pos[1] * cy.zoom() + cy.pan().y];
-
- if( r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection() ){ // if pan dragging or cxt dragging, wheel movements make no zoom
- e.preventDefault();
- return;
- }
-
- if( cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled() ){
- e.preventDefault();
-
- r.data.wheelZooming = true;
- clearTimeout( r.data.wheelTimeout );
- r.data.wheelTimeout = setTimeout(function(){
- r.data.wheelZooming = false;
-
- r.redrawHint('eles', true);
- r.redraw();
- }, 150);
-
- var diff = e.deltaY / -250 || e.wheelDeltaY / 1000 || e.wheelDelta / 1000;
- diff = diff * r.wheelSensitivity;
-
- var needsWheelFix = e.deltaMode === 1;
- if( needsWheelFix ){ // fixes slow wheel events on ff/linux and ff/windows
- diff *= 33;
- }
-
- cy.zoom({
- level: cy.zoom() * Math.pow(10, diff),
- renderedPosition: { x: rpos[0], y: rpos[1] }
- });
- }
-
- };
-
- // Functions to help with whether mouse wheel should trigger zooming
- // --
- r.registerBinding(r.container, 'wheel', wheelHandler, true);
-
- // disable nonstandard wheel events
- // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
- // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
- // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
-
- r.registerBinding(window, 'scroll', function(e){
- r.scrollingPage = true;
-
- clearTimeout( r.scrollingPageTimeout );
- r.scrollingPageTimeout = setTimeout(function(){
- r.scrollingPage = false;
- }, 250);
- }, true);
-
- // Functions to help with handling mouseout/mouseover on the Cytoscape container
- // Handle mouseout on Cytoscape container
- r.registerBinding(r.container, 'mouseout', function(e) {
- var pos = r.projectIntoViewport(e.clientX, e.clientY);
-
- r.cy.trigger(Event(e, {
- type: 'mouseout',
- cyPosition: { x: pos[0], y: pos[1] }
- }));
- }, false);
-
- r.registerBinding(r.container, 'mouseover', function(e) {
- var pos = r.projectIntoViewport(e.clientX, e.clientY);
-
- r.cy.trigger(Event(e, {
- type: 'mouseover',
- cyPosition: { x: pos[0], y: pos[1] }
- }));
- }, false);
-
- var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
- var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
- var center1, modelCenter1; // center point on start pinch to zoom
- var offsetLeft, offsetTop;
- var containerWidth, containerHeight;
- var twoFingersStartInside;
-
- var distance = function(x1, y1, x2, y2){
- return Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
- };
-
- var distanceSq = function(x1, y1, x2, y2){
- return (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
- };
-
- var touchstartHandler;
- r.registerBinding(r.container, 'touchstart', touchstartHandler = function(e) {
- r.touchData.capture = true;
- r.data.bgActivePosistion = undefined;
-
- var cy = r.cy;
- var nodes = r.getCachedNodes();
- var edges = r.getCachedEdges();
- var now = r.touchData.now;
- var earlier = r.touchData.earlier;
-
- if (e.touches[0]) { var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY); now[0] = pos[0]; now[1] = pos[1]; }
- if (e.touches[1]) { var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY); now[2] = pos[0]; now[3] = pos[1]; }
- if (e.touches[2]) { var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY); now[4] = pos[0]; now[5] = pos[1]; }
-
-
- // record starting points for pinch-to-zoom
- if( e.touches[1] ){
-
- // anything in the set of dragged eles should be released
- var release = function( eles ){
- for( var i = 0; i < eles.length; i++ ){
- eles[i]._private.grabbed = false;
- eles[i]._private.rscratch.inDragLayer = false;
- if( eles[i].active() ){ eles[i].unactivate(); }
- }
- };
- release(nodes);
- release(edges);
-
- var offsets = r.findContainerClientCoords();
- offsetLeft = offsets[0];
- offsetTop = offsets[1];
- containerWidth = offsets[2];
- containerHeight = offsets[3];
-
- f1x1 = e.touches[0].clientX - offsetLeft;
- f1y1 = e.touches[0].clientY - offsetTop;
-
- f2x1 = e.touches[1].clientX - offsetLeft;
- f2y1 = e.touches[1].clientY - offsetTop;
-
- twoFingersStartInside =
- 0 <= f1x1 && f1x1 <= containerWidth
- && 0 <= f2x1 && f2x1 <= containerWidth
- && 0 <= f1y1 && f1y1 <= containerHeight
- && 0 <= f2y1 && f2y1 <= containerHeight
- ;
-
- var pan = cy.pan();
- var zoom = cy.zoom();
-
- distance1 = distance( f1x1, f1y1, f2x1, f2y1 );
- distance1Sq = distanceSq( f1x1, f1y1, f2x1, f2y1 );
- center1 = [ (f1x1 + f2x1)/2, (f1y1 + f2y1)/2 ];
- modelCenter1 = [
- (center1[0] - pan.x) / zoom,
- (center1[1] - pan.y) / zoom
- ];
-
- // consider context tap
- var cxtDistThreshold = 200;
- var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
- if( distance1Sq < cxtDistThresholdSq && !e.touches[2] ){
-
- var near1 = r.findNearestElement(now[0], now[1], true, true);
- var near2 = r.findNearestElement(now[2], now[3], true, true);
-
- if( near1 && near1.isNode() ){
- near1.activate().trigger( Event(e, {
- type: 'cxttapstart',
- cyPosition: { x: now[0], y: now[1] }
- }) );
- r.touchData.start = near1;
-
- } else if( near2 && near2.isNode() ){
- near2.activate().trigger( Event(e, {
- type: 'cxttapstart',
- cyPosition: { x: now[0], y: now[1] }
- }) );
- r.touchData.start = near2;
-
- } else {
- cy.trigger( Event(e, {
- type: 'cxttapstart',
- cyPosition: { x: now[0], y: now[1] }
- }) );
- r.touchData.start = null;
- }
-
- if( r.touchData.start ){ r.touchData.start._private.grabbed = false; }
- r.touchData.cxt = true;
- r.touchData.cxtDragged = false;
- r.data.bgActivePosistion = undefined;
-
- r.redraw();
- return;
-
- }
-
- }
-
- if (e.touches[2]) {
-
- } else if (e.touches[1]) {
-
- } else if (e.touches[0]) {
- var near = r.findNearestElement(now[0], now[1], true, true);
-
- if (near != null) {
- near.activate();
-
- r.touchData.start = near;
-
- if( near.isNode() && r.nodeIsDraggable(near) ){
-
- var draggedEles = r.dragData.touchDragEles = [];
-
- r.redrawHint('eles', true);
- r.redrawHint('drag', true);
-
- if( near.selected() ){
- // reset drag elements, since near will be added again
-
- var selectedNodes = cy.$(function(){
- return this.isNode() && this.selected();
- });
-
- for( var k = 0; k < selectedNodes.length; k++ ){
- var selectedNode = selectedNodes[k];
-
- if( r.nodeIsDraggable(selectedNode) ){
- addNodeToDrag( selectedNode, { addToList: draggedEles } );
- }
- }
- } else {
- addNodeToDrag( near, { addToList: draggedEles } );
- }
-
- near.trigger( Event(e, {
- type: 'grab',
- cyPosition: { x: now[0], y: now[1] }
- }) );
- }
- }
-
- triggerEvents( near, ['touchstart', 'tapstart', 'vmousedown'], e, {
- cyPosition: { x: now[0], y: now[1] }
- } );
-
- if (near == null) {
- r.data.bgActivePosistion = {
- x: pos[0],
- y: pos[1]
- };
-
- r.redrawHint('select', true);
- r.redraw();
- }
-
-
- // Tap, taphold
- // -----
-
- for (var i=0; i= factorThresholdSq || distance2Sq >= distThresholdSq ){
- r.touchData.cxt = false;
- if( r.touchData.start ){ r.touchData.start.unactivate(); r.touchData.start = null; }
- r.data.bgActivePosistion = undefined;
- r.redrawHint('select', true);
-
- var cxtEvt = Event(e, {
- type: 'cxttapend',
- cyPosition: { x: now[0], y: now[1] }
- });
- if( r.touchData.start ){
- r.touchData.start.trigger( cxtEvt );
- } else {
- cy.trigger( cxtEvt );
- }
- }
-
- }
-
- // context swipe
- if( capture && r.touchData.cxt ){
- var cxtEvt = Event(e, {
- type: 'cxtdrag',
- cyPosition: { x: now[0], y: now[1] }
- });
- r.data.bgActivePosistion = undefined;
- r.redrawHint('select', true);
-
- if( r.touchData.start ){
- r.touchData.start.trigger( cxtEvt );
- } else {
- cy.trigger( cxtEvt );
- }
-
- if( r.touchData.start ){ r.touchData.start._private.grabbed = false; }
- r.touchData.cxtDragged = true;
-
- var near = r.findNearestElement(now[0], now[1], true, true);
-
- if( !r.touchData.cxtOver || near !== r.touchData.cxtOver ){
-
- if( r.touchData.cxtOver ){
- r.touchData.cxtOver.trigger( Event(e, {
- type: 'cxtdragout',
- cyPosition: { x: now[0], y: now[1] }
- }) );
- }
-
- r.touchData.cxtOver = near;
-
- if( near ){
- near.trigger( Event(e, {
- type: 'cxtdragover',
- cyPosition: { x: now[0], y: now[1] }
- }) );
-
- }
-
- }
-
- // box selection
- } else if( capture && e.touches[2] && cy.boxSelectionEnabled() ){
- e.preventDefault();
-
- r.data.bgActivePosistion = undefined;
-
- this.lastThreeTouch = +new Date();
- r.touchData.selecting = true;
-
- r.redrawHint('select', true);
-
- if( !select || select.length === 0 || select[0] === undefined ){
- select[0] = (now[0] + now[2] + now[4])/3;
- select[1] = (now[1] + now[3] + now[5])/3;
- select[2] = (now[0] + now[2] + now[4])/3 + 1;
- select[3] = (now[1] + now[3] + now[5])/3 + 1;
- } else {
- select[2] = (now[0] + now[2] + now[4])/3;
- select[3] = (now[1] + now[3] + now[5])/3;
- }
-
- select[4] = 1;
- r.touchData.selecting = true;
-
- r.redraw();
-
- // pinch to zoom
- } else if ( capture && e.touches[1] && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled() ) { // two fingers => pinch to zoom
- e.preventDefault();
-
- r.data.bgActivePosistion = undefined;
- r.redrawHint('select', true);
-
- var draggedEles = r.dragData.touchDragEles;
- if( draggedEles ){
- r.redrawHint('drag', true);
-
- for( var i = 0; i < draggedEles.length; i++ ){
- draggedEles[i]._private.grabbed = false;
- draggedEles[i]._private.rscratch.inDragLayer = false;
- }
- }
-
- // (x2, y2) for fingers 1 and 2
- var f1x2 = e.touches[0].clientX - offsetLeft, f1y2 = e.touches[0].clientY - offsetTop;
- var f2x2 = e.touches[1].clientX - offsetLeft, f2y2 = e.touches[1].clientY - offsetTop;
-
-
- var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
- // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
- // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
- var factor = distance2 / distance1;
-
- if( factor != 1 && twoFingersStartInside){
- // delta finger1
- var df1x = f1x2 - f1x1;
- var df1y = f1y2 - f1y1;
-
- // delta finger 2
- var df2x = f2x2 - f2x1;
- var df2y = f2y2 - f2y1;
-
- // translation is the normalised vector of the two fingers movement
- // i.e. so pinching cancels out and moving together pans
- var tx = (df1x + df2x)/2;
- var ty = (df1y + df2y)/2;
-
- // adjust factor by the speed multiplier
- // var speed = 1.5;
- // if( factor > 1 ){
- // factor = (factor - 1) * speed + 1;
- // } else {
- // factor = 1 - (1 - factor) * speed;
- // }
-
- // now calculate the zoom
- var zoom1 = cy.zoom();
- var zoom2 = zoom1 * factor;
- var pan1 = cy.pan();
-
- // the model center point converted to the current rendered pos
- var ctrx = modelCenter1[0] * zoom1 + pan1.x;
- var ctry = modelCenter1[1] * zoom1 + pan1.y;
-
- var pan2 = {
- x: -zoom2/zoom1 * (ctrx - pan1.x - tx) + ctrx,
- y: -zoom2/zoom1 * (ctry - pan1.y - ty) + ctry
- };
-
- // remove dragged eles
- if( r.touchData.start ){
- var draggedEles = r.dragData.touchDragEles;
-
- if( draggedEles ){ for( var i = 0; i < draggedEles.length; i++ ){
- var dEi_p = draggedEles[i]._private;
-
- dEi_p.grabbed = false;
- dEi_p.rscratch.inDragLayer = false;
- } }
-
- var start_p = r.touchData.start._private;
- start_p.active = false;
- start_p.grabbed = false;
- start_p.rscratch.inDragLayer = false;
-
- r.redrawHint('drag', true);
-
- r.touchData.start
- .trigger('free')
- .trigger('unactivate')
- ;
- }
-
- cy.viewport({
- zoom: zoom2,
- pan: pan2,
- cancelOnFailedZoom: true
- });
-
- distance1 = distance2;
- f1x1 = f1x2;
- f1y1 = f1y2;
- f2x1 = f2x2;
- f2y1 = f2y2;
-
- r.pinching = true;
- }
-
- // Re-project
- if (e.touches[0]) { var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY); now[0] = pos[0]; now[1] = pos[1]; }
- if (e.touches[1]) { var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY); now[2] = pos[0]; now[3] = pos[1]; }
- if (e.touches[2]) { var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY); now[4] = pos[0]; now[5] = pos[1]; }
-
- } else if (e.touches[0]) {
- var start = r.touchData.start;
- var last = r.touchData.last;
- var near = near || r.findNearestElement(now[0], now[1], true, true);
-
- if( start != null ){
- e.preventDefault();
- }
-
- // dragging nodes
- if( start != null && start._private.group == 'nodes' && r.nodeIsDraggable(start) ){
-
- if( rdist2 >= r.touchTapThreshold2 ){ // then dragging can happen
- var draggedEles = r.dragData.touchDragEles;
- var justStartedDrag = !r.dragData.didDrag;
-
- for( var k = 0; k < draggedEles.length; k++ ){
- var draggedEle = draggedEles[k];
-
- if( justStartedDrag ){
- addNodeToDrag( draggedEle, { inDragLayer: true } );
- }
-
- if( r.nodeIsDraggable(draggedEle) && draggedEle.isNode() && draggedEle.grabbed() ){
- r.dragData.didDrag = true;
- var dPos = draggedEle._private.position;
- var updatePos = !draggedEle.isParent();
-
- if( updatePos && is.number(disp[0]) && is.number(disp[1]) ){
- dPos.x += disp[0];
- dPos.y += disp[1];
- }
-
- if( justStartedDrag ){
- r.redrawHint('eles', true);
-
- var dragDelta = r.touchData.dragDelta;
-
- if( updatePos && is.number(dragDelta[0]) && is.number(dragDelta[1]) ){
- dPos.x += dragDelta[0];
- dPos.y += dragDelta[1];
- }
-
- }
- }
- }
-
- var tcol = Collection(cy, draggedEles);
-
- tcol.updateCompoundBounds();
- tcol.trigger('position drag');
-
- r.hoverData.draggingEles = true;
-
- r.redrawHint('drag', true);
-
- if(
- r.touchData.startPosition[0] == earlier[0]
- && r.touchData.startPosition[1] == earlier[1]
- ){
-
- r.redrawHint('eles', true);
- }
-
- r.redraw();
- } else { // otherise keep track of drag delta for later
- var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
-
- if( dragDelta.length === 0 ){
- dragDelta.push( disp[0] );
- dragDelta.push( disp[1] );
- } else {
- dragDelta[0] += disp[0];
- dragDelta[1] += disp[1];
- }
- }
- }
-
- // touchmove
- {
- triggerEvents( (start || near), ['touchmove', 'tapdrag', 'vmousemove'], e, {
- cyPosition: { x: now[0], y: now[1] }
- } );
-
- if (near != last) {
- if (last) { last.trigger(Event(e, { type: 'tapdragout', cyPosition: { x: now[0], y: now[1] } })); }
- if (near) { near.trigger(Event(e, { type: 'tapdragover', cyPosition: { x: now[0], y: now[1] } })); }
- }
-
- r.touchData.last = near;
- }
-
- // check to cancel taphold
- for (var i=0;i r.touchTapThreshold2 ){
-
- r.touchData.singleTouchMoved = true;
- }
- }
-
- // panning
- if(
- capture
- && ( start == null || start.isEdge() )
- && cy.panningEnabled() && cy.userPanningEnabled()
- ){
-
- e.preventDefault();
-
- if( r.swipePanning ){
- cy.panBy({
- x: disp[0] * zoom,
- y: disp[1] * zoom
- });
-
- } else if( rdist2 >= r.touchTapThreshold2 ){
- r.swipePanning = true;
-
- cy.panBy({
- x: dx * zoom,
- y: dy * zoom
- });
-
- if( start ){
- start.unactivate();
-
- if( !r.data.bgActivePosistion ){
- r.data.bgActivePosistion = {
- x: now[0],
- y: now[1]
- };
- }
-
- r.redrawHint('select', true);
-
- r.touchData.start = null;
- }
- }
-
- // Re-project
- var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
- now[0] = pos[0]; now[1] = pos[1];
- }
- }
-
- for (var j=0; j 0 ) {
- r.redrawHint('eles', true);
- } else {
- r.redraw();
- }
- }
-
- var updateStartStyle = false;
-
- if( start != null ){
- start._private.active = false;
- updateStartStyle = true;
- start.unactivate();
- }
-
- if (e.touches[2]) {
- r.data.bgActivePosistion = undefined;
- r.redrawHint('select', true);
- } else if (e.touches[1]) {
-
- } else if (e.touches[0]) {
-
- // Last touch released
- } else if (!e.touches[0]) {
-
- r.data.bgActivePosistion = undefined;
- r.redrawHint('select', true);
-
- var draggedEles = r.dragData.touchDragEles;
-
- if (start != null ) {
-
- var startWasGrabbed = start._private.grabbed;
-
- freeDraggedElements( draggedEles );
-
- r.redrawHint('drag', true);
- r.redrawHint('eles', true);
-
- if( startWasGrabbed ){
- start.trigger('free');
- }
-
- triggerEvents( start, ['touchend', 'tapend', 'vmouseup'], e, {
- cyPosition: { x: now[0], y: now[1] }
- } );
-
- start.unactivate();
-
- r.touchData.start = null;
-
- } else {
- var near = r.findNearestElement(now[0], now[1], true, true);
-
- triggerEvents( near, ['touchend', 'tapend', 'vmouseup'], e, {
- cyPosition: { x: now[0], y: now[1] }
- } );
-
- }
-
- var dx = r.touchData.startPosition[0] - now[0];
- var dx2 = dx * dx;
- var dy = r.touchData.startPosition[1] - now[1];
- var dy2 = dy * dy;
- var dist2 = dx2 + dy2;
- var rdist2 = dist2 * zoom * zoom;
-
- // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
- if (start != null
- && !r.dragData.didDrag // didn't drag nodes around
- && start._private.selectable
- && rdist2 < r.touchTapThreshold2
- && !r.pinching // pinch to zoom should not affect selection
- ) {
-
- if( cy.selectionType() === 'single' ){
- cy.$(':selected').unmerge( start ).unselect();
- start.select();
- } else {
- if( start.selected() ){
- start.unselect();
- } else {
- start.select();
- }
- }
-
- updateStartStyle = true;
-
-
- r.redrawHint('eles', true);
- }
-
- // Tap event, roughly same as mouse click event for touch
- if( !r.touchData.singleTouchMoved ){
- triggerEvents( start, ['tap', 'vclick'], e, {
- cyPosition: { x: now[0], y: now[1] }
- } );
- }
-
- r.touchData.singleTouchMoved = true;
- }
-
- for( var j = 0; j < now.length; j++ ){ earlier[j] = now[j]; }
-
- r.dragData.didDrag = false; // reset for next mousedown
-
- if( e.touches.length === 0 ){
- r.touchData.dragDelta = [];
- }
-
- if( updateStartStyle && start ){
- start.updateStyle(false);
- }
-
- if( e.touches.length < 2 ){
- r.pinching = false;
- r.redrawHint('eles', true);
- r.redraw();
- }
-
- //r.redraw();
-
- }, false);
-
- // fallback compatibility layer for ms pointer events
- if( typeof TouchEvent === 'undefined' ){
-
- var pointers = [];
-
- var makeTouch = function( e ){
- return {
- clientX: e.clientX,
- clientY: e.clientY,
- force: 1,
- identifier: e.pointerId,
- pageX: e.pageX,
- pageY: e.pageY,
- radiusX: e.width/2,
- radiusY: e.height/2,
- screenX: e.screenX,
- screenY: e.screenY,
- target: e.target
- };
- };
-
- var makePointer = function( e ){
- return {
- event: e,
- touch: makeTouch(e)
- };
- };
-
- var addPointer = function( e ){
- pointers.push( makePointer(e) );
- };
-
- var removePointer = function( e ){
- for( var i = 0; i < pointers.length; i++ ){
- var p = pointers[i];
-
- if( p.event.pointerId === e.pointerId ){
- pointers.splice( i, 1 );
- return;
- }
- }
- };
-
- var updatePointer = function( e ){
- var p = pointers.filter(function( p ){
- return p.event.pointerId === e.pointerId;
- })[0];
-
- p.event = e;
- p.touch = makeTouch(e);
- };
-
- var addTouchesToEvent = function( e ){
- e.touches = pointers.map(function( p ){
- return p.touch;
- });
- };
-
- r.registerBinding(r.container, 'pointerdown', function(e){
- if( e.pointerType === 'mouse' ){ return; } // mouse already handled
-
- e.preventDefault();
-
- addPointer( e );
-
- addTouchesToEvent( e );
- touchstartHandler( e );
- });
-
- r.registerBinding(r.container, 'pointerup', function(e){
- if( e.pointerType === 'mouse' ){ return; } // mouse already handled
-
- removePointer( e );
-
- addTouchesToEvent( e );
- touchendHandler( e );
- });
-
- r.registerBinding(r.container, 'pointercancel', function(e){
- if( e.pointerType === 'mouse' ){ return; } // mouse already handled
-
- removePointer( e );
-
- addTouchesToEvent( e );
- touchcancelHandler( e );
- });
-
- r.registerBinding(r.container, 'pointermove', function(e){
- if( e.pointerType === 'mouse' ){ return; } // mouse already handled
-
- e.preventDefault();
-
- updatePointer( e );
-
- addTouchesToEvent( e );
- touchmoveHandler( e );
- });
-
- }
-};
-
-module.exports = BRp;
-
-},{"../../../collection":23,"../../../event":42,"../../../is":77,"../../../util":94}],60:[function(_dereq_,module,exports){
-'use strict';
-
-var math = _dereq_('../../../math');
-
-var BRp = {};
-
-BRp.registerNodeShapes = function(){
- var nodeShapes = this.nodeShapes = {};
- var renderer = this;
-
- nodeShapes['ellipse'] = {
- name: 'ellipse',
-
- draw: function( context, centerX, centerY, width, height ){
- renderer.nodeShapeImpl( this.name )( context, centerX, centerY, width, height );
- },
-
- intersectLine: function( nodeX, nodeY, width, height, x, y, padding ){
- return math.intersectLineEllipse(
- x, y,
- nodeX,
- nodeY,
- width / 2 + padding,
- height / 2 + padding)
- ;
- },
-
- checkPoint: function( x, y, padding, width, height, centerX, centerY ){
- x -= centerX;
- y -= centerY;
-
- x /= (width / 2 + padding);
- y /= (height / 2 + padding);
-
- return x*x + y*y <= 1;
- }
- };
-
- function generatePolygon( name, points ){
- return ( nodeShapes[name] = {
- name: name,
-
- points: points,
-
- draw: function( context, centerX, centerY, width, height ){
- renderer.nodeShapeImpl('polygon')( context, centerX, centerY, width, height, this.points );
- },
-
- intersectLine: function( nodeX, nodeY, width, height, x, y, padding ){
- return math.polygonIntersectLine(
- x, y,
- this.points,
- nodeX,
- nodeY,
- width / 2, height / 2,
- padding)
- ;
- },
-
- checkPoint: function( x, y, padding, width, height, centerX, centerY ){
- return math.pointInsidePolygon(x, y, nodeShapes[name].points,
- centerX, centerY, width, height, [0, -1], padding)
- ;
- }
- } );
- }
-
- generatePolygon( 'triangle', math.generateUnitNgonPointsFitToSquare(3, 0) );
-
- generatePolygon( 'square', math.generateUnitNgonPointsFitToSquare(4, 0) );
- nodeShapes['rectangle'] = nodeShapes['square'];
-
- nodeShapes['roundrectangle'] = {
- name: 'roundrectangle',
-
- points: math.generateUnitNgonPointsFitToSquare(4, 0),
-
- draw: function( context, centerX, centerY, width, height ){
- renderer.nodeShapeImpl( this.name )( context, centerX, centerY, width, height );
- },
-
- intersectLine: function( nodeX, nodeY, width, height, x, y, padding ){
- return math.roundRectangleIntersectLine(
- x, y,
- nodeX,
- nodeY,
- width, height,
- padding)
- ;
- },
-
- // Looks like the width passed into this function is actually the total width / 2
- checkPoint: function(
- x, y, padding, width, height, centerX, centerY ){
-
- var cornerRadius = math.getRoundRectangleRadius(width, height);
-
- // Check hBox
- if (math.pointInsidePolygon(x, y, this.points,
- centerX, centerY, width, height - 2 * cornerRadius, [0, -1], padding) ){
- return true;
- }
-
- // Check vBox
- if (math.pointInsidePolygon(x, y, this.points,
- centerX, centerY, width - 2 * cornerRadius, height, [0, -1], padding) ){
- return true;
- }
-
- var checkInEllipse = function( x, y, centerX, centerY, width, height, padding ){
- x -= centerX;
- y -= centerY;
-
- x /= (width / 2 + padding);
- y /= (height / 2 + padding);
-
- return (x*x + y*y <= 1);
- };
-
-
- // Check top left quarter circle
- if (checkInEllipse(x, y,
- centerX - width / 2 + cornerRadius,
- centerY - height / 2 + cornerRadius,
- cornerRadius * 2, cornerRadius * 2, padding) ){
-
- return true;
- }
-
- // Check top right quarter circle
- if (checkInEllipse(x, y,
- centerX + width / 2 - cornerRadius,
- centerY - height / 2 + cornerRadius,
- cornerRadius * 2, cornerRadius * 2, padding) ){
-
- return true;
- }
-
- // Check bottom right quarter circle
- if (checkInEllipse(x, y,
- centerX + width / 2 - cornerRadius,
- centerY + height / 2 - cornerRadius,
- cornerRadius * 2, cornerRadius * 2, padding) ){
-
- return true;
- }
-
- // Check bottom left quarter circle
- if (checkInEllipse(x, y,
- centerX - width / 2 + cornerRadius,
- centerY + height / 2 - cornerRadius,
- cornerRadius * 2, cornerRadius * 2, padding) ){
-
- return true;
- }
-
- return false;
- }
- };
-
- generatePolygon( 'diamond', [
- 0, 1,
- 1, 0,
- 0, -1,
- -1, 0
- ] );
-
- generatePolygon( 'pentagon', math.generateUnitNgonPointsFitToSquare(5, 0) );
-
- generatePolygon( 'hexagon', math.generateUnitNgonPointsFitToSquare(6, 0) );
-
- generatePolygon( 'heptagon', math.generateUnitNgonPointsFitToSquare(7, 0) );
-
- generatePolygon( 'octagon', math.generateUnitNgonPointsFitToSquare(8, 0) );
-
- var star5Points = new Array(20);
- {
- var outerPoints = math.generateUnitNgonPoints(5, 0);
- var innerPoints = math.generateUnitNgonPoints(5, Math.PI / 5);
-
- // Outer radius is 1; inner radius of star is smaller
- var innerRadius = 0.5 * (3 - Math.sqrt(5));
- innerRadius *= 1.57;
-
- for (var i=0;i redrawLimit ? minRedrawLimit : redrawLimit;
- redrawLimit = redrawLimit < maxRedrawLimit ? redrawLimit : maxRedrawLimit;
-
- if( r.lastDrawTime === undefined ){ r.lastDrawTime = 0; }
-
- var nowTime = Date.now();
- var timeElapsed = nowTime - r.lastDrawTime;
- var callAfterLimit = timeElapsed >= redrawLimit;
-
- if( !forcedContext ){
- if( !callAfterLimit || r.currentlyDrawing ){
- r.skipFrame = true;
- return;
- }
- }
-
- r.requestedFrame = true;
- r.currentlyDrawing = true;
- r.renderOptions = options;
-};
-
-BRp.startRenderLoop = function(){
- var r = this;
-
- var renderFn = function(){
- if( r.destroyed ){ return; }
-
- if( r.requestedFrame && !r.skipFrame ){
- var startTime = util.performanceNow();
-
- r.render( r.renderOptions );
-
- var endTime = r.lastRedrawTime = util.performanceNow();
-
- if( r.averageRedrawTime === undefined ){
- r.averageRedrawTime = endTime - startTime;
- }
-
- if( r.redrawCount === undefined ){
- r.redrawCount = 0;
- }
-
- r.redrawCount++;
-
- if( r.redrawTotalTime === undefined ){
- r.redrawTotalTime = 0;
- }
-
- var duration = endTime - startTime;
-
- r.redrawTotalTime += duration;
- r.lastRedrawTime = duration;
-
- // use a weighted average with a bias from the previous average so we don't spike so easily
- r.averageRedrawTime = r.averageRedrawTime/2 + duration/2;
-
- r.requestedFrame = false;
- }
-
- r.skipFrame = false;
-
- util.requestAnimationFrame( renderFn );
- };
-
- util.requestAnimationFrame( renderFn );
-
-};
-
-module.exports = BRp;
-
-},{"../../../util":94}],62:[function(_dereq_,module,exports){
-'use strict';
-
-var CRp = {};
-
-var impl;
-
-CRp.arrowShapeImpl = function( name ){
- return ( impl || (impl = {
- 'polygon': function( context, points ){
- for( var i = 0; i < points.length; i++ ){
- var pt = points[i];
-
- context.lineTo( pt.x, pt.y );
- }
- },
-
- 'triangle-backcurve': function( context, points, controlPoint ){
- var firstPt;
-
- for( var i = 0; i < points.length; i++ ){
- var pt = points[i];
-
- if( i === 0 ){
- firstPt = pt;
- }
-
- context.lineTo( pt.x, pt.y );
- }
-
- context.quadraticCurveTo( controlPoint.x, controlPoint.y, firstPt.x, firstPt.y );
- },
-
- 'triangle-tee': function( context, trianglePoints, teePoints ){
- var triPts = trianglePoints;
- for( var i = 0; i < triPts.length; i++ ){
- var pt = triPts[i];
-
- context.lineTo( pt.x, pt.y );
- }
-
- var teePts = teePoints;
- var firstTeePt = teePoints[0];
- context.moveTo( firstTeePt.x, firstTeePt.y );
-
- for( var i = 0; i < teePts.length; i++ ){
- var pt = teePts[i];
-
- context.lineTo( pt.x, pt.y );
- }
- },
-
- 'circle': function( context, rx, ry, r ){
- context.arc(rx, ry, r, 0, Math.PI * 2, false);
- }
- }) )[ name ];
-};
-
-module.exports = CRp;
-
-},{}],63:[function(_dereq_,module,exports){
-'use strict';
-
-var CRp = {};
-
-CRp.drawEdge = function(context, edge, drawOverlayInstead) {
- var rs = edge._private.rscratch;
- var usePaths = this.usePaths();
-
- // if bezier ctrl pts can not be calculated, then die
- if( rs.badBezier || rs.badLine || isNaN( rs.allpts[0] ) ){ // iNaN in case edge is impossible and browser bugs (e.g. safari)
- return;
- }
-
- var style = edge._private.style;
-
- // Edge line width
- if (style['width'].pfValue <= 0) {
- return;
- }
-
- var overlayPadding = style['overlay-padding'].pfValue;
- var overlayOpacity = style['overlay-opacity'].value;
- var overlayColor = style['overlay-color'].value;
-
- // Edge color & opacity
- if( drawOverlayInstead ){
-
- if( overlayOpacity === 0 ){ // exit early if no overlay
- return;
- }
-
- this.strokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
- context.lineCap = 'round';
-
- if( rs.edgeType == 'self' && !usePaths ){
- context.lineCap = 'butt';
- }
-
- } else {
- var lineColor = style['line-color'].value;
-
- this.strokeStyle(context, lineColor[0], lineColor[1], lineColor[2], style.opacity.value);
-
- context.lineCap = 'butt';
- }
-
- var edgeWidth = style['width'].pfValue + (drawOverlayInstead ? 2 * overlayPadding : 0);
- var lineStyle = drawOverlayInstead ? 'solid' : style['line-style'].value;
- context.lineWidth = edgeWidth;
-
- var shadowBlur = style['shadow-blur'].pfValue;
- var shadowOpacity = style['shadow-opacity'].value;
- var shadowColor = style['shadow-color'].value;
- var shadowOffsetX = style['shadow-offset-x'].pfValue;
- var shadowOffsetY = style['shadow-offset-y'].pfValue;
-
- this.shadowStyle(context, shadowColor, drawOverlayInstead ? 0 : shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY);
-
- this.drawEdgePath(
- edge,
- context,
- rs.allpts,
- lineStyle,
- edgeWidth
- );
-
- this.drawArrowheads(context, edge, drawOverlayInstead);
-
- this.shadowStyle(context, 'transparent', 0); // reset for next guy
-
-};
-
-
-CRp.drawEdgePath = function(edge, context, pts, type, width) {
- var rs = edge._private.rscratch;
- var canvasCxt = context;
- var path;
- var pathCacheHit = false;
- var usePaths = this.usePaths();
-
- if( usePaths ){
- var pathCacheKey = pts.join('$');
- var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
-
- if( keyMatches ){
- path = context = rs.pathCache;
- pathCacheHit = true;
- } else {
- path = context = new Path2D();
- rs.pathCacheKey = pathCacheKey;
- rs.pathCache = path;
- }
- }
-
- if( canvasCxt.setLineDash ){ // for very outofdate browsers
- switch( type ){
- case 'dotted':
- canvasCxt.setLineDash([ 1, 1 ]);
- break;
-
- case 'dashed':
- canvasCxt.setLineDash([ 6, 3 ]);
- break;
-
- case 'solid':
- canvasCxt.setLineDash([ ]);
- break;
- }
- }
-
- if( !pathCacheHit ){
- if( context.beginPath ){ context.beginPath(); }
- context.moveTo( pts[0], pts[1] );
-
- switch( rs.edgeType ){
- case 'bezier':
- case 'self':
- case 'compound':
- case 'multibezier':
- if( !rs.badBezier ){
- for( var i = 2; i + 3 < pts.length; i += 4 ){
- context.quadraticCurveTo( pts[i], pts[i+1], pts[i+2], pts[i+3] );
- }
- }
- break;
-
- case 'straight':
- case 'segments':
- case 'haystack':
- if( !rs.badLine ){
- for( var i = 2; i + 1 < pts.length; i += 2 ){
- context.lineTo( pts[i], pts[i+1] );
- }
- }
- break;
- }
- }
-
- context = canvasCxt;
- if( usePaths ){
- context.stroke( path );
- } else {
- context.stroke();
- }
-
- // reset any line dashes
- if( context.setLineDash ){ // for very outofdate browsers
- context.setLineDash([ ]);
- }
-
-};
-
-CRp.drawArrowheads = function(context, edge, drawOverlayInstead) {
- if( drawOverlayInstead ){ return; } // don't do anything for overlays
-
- var rs = edge._private.rscratch;
- var isHaystack = rs.edgeType === 'haystack';
-
- if( !isHaystack ){
- this.drawArrowhead( context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle );
- }
-
- this.drawArrowhead( context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle );
-
- this.drawArrowhead( context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle );
-
- if( !isHaystack ){
- this.drawArrowhead( context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle );
- }
-};
-
-CRp.drawArrowhead = function( context, edge, prefix, x, y, angle ){
- if( isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null ){ return; }
-
- var self = this;
- var style = edge._private.style;
- var arrowShape = style[prefix + '-arrow-shape'].value;
-
- if( arrowShape === 'none' ){
- return;
- }
-
- var gco = context.globalCompositeOperation;
-
- var arrowClearFill = style[prefix + '-arrow-fill'].value === 'hollow' ? 'both' : 'filled';
- var arrowFill = style[prefix + '-arrow-fill'].value;
-
- if( arrowShape === 'half-triangle-overshot' ){
- arrowFill = 'hollow';
- arrowClearFill = 'hollow';
- }
-
- if( style.opacity.value !== 1 || arrowFill === 'hollow' ){ // then extra clear is needed
- context.globalCompositeOperation = 'destination-out';
-
- self.fillStyle(context, 255, 255, 255, 1);
- self.strokeStyle(context, 255, 255, 255, 1);
-
- self.drawArrowShape( edge, prefix, context,
- arrowClearFill, style['width'].pfValue, style[prefix + '-arrow-shape'].value,
- x, y, angle
- );
-
- context.globalCompositeOperation = gco;
- } // otherwise, the opaque arrow clears it for free :)
-
- var color = style[prefix + '-arrow-color'].value;
- self.fillStyle(context, color[0], color[1], color[2], style.opacity.value);
- self.strokeStyle(context, color[0], color[1], color[2], style.opacity.value);
-
- self.drawArrowShape( edge, prefix, context,
- arrowFill, style['width'].pfValue, style[prefix + '-arrow-shape'].value,
- x, y, angle
- );
-};
-
-CRp.drawArrowShape = function(edge, arrowType, context, fill, edgeWidth, shape, x, y, angle) {
- var r = this;
- var usePaths = this.usePaths();
- var rs = edge._private.rscratch;
- var pathCacheHit = false;
- var path;
- var canvasContext = context;
- var translation = { x: x, y: y };
- var size = this.getArrowWidth( edgeWidth );
- var shapeImpl = r.arrowShapes[shape];
-
- if( usePaths ){
- var pathCacheKey = size + '$' + shape + '$' + angle + '$' + x + '$' + y;
- rs.arrowPathCacheKey = rs.arrowPathCacheKey || {};
- rs.arrowPathCache = rs.arrowPathCache || {};
-
- var alreadyCached = rs.arrowPathCacheKey[arrowType] === pathCacheKey;
- if( alreadyCached ){
- path = context = rs.arrowPathCache[arrowType];
- pathCacheHit = true;
- } else {
- path = context = new Path2D();
- rs.arrowPathCacheKey[arrowType] = pathCacheKey;
- rs.arrowPathCache[arrowType] = path;
- }
- }
-
- if( context.beginPath ){ context.beginPath(); }
-
- if( !pathCacheHit ){
- shapeImpl.draw(context, size, angle, translation);
- }
-
- if( !shapeImpl.leavePathOpen && context.closePath ){
- context.closePath();
- }
-
- context = canvasContext;
-
- if( fill === 'filled' || fill === 'both' ){
- if( usePaths ){
- context.fill( path );
- } else {
- context.fill();
- }
- }
-
- if( fill === 'hollow' || fill === 'both' ){
- context.lineWidth = ( shapeImpl.matchEdgeWidth ? edgeWidth : 1 );
- context.lineJoin = 'miter';
-
- if( usePaths ){
- context.stroke( path );
- } else {
- context.stroke();
- }
-
- }
-};
-
-module.exports = CRp;
-
-},{}],64:[function(_dereq_,module,exports){
-'use strict';
-
-var CRp = {};
-
-CRp.safeDrawImage = function( context, img, ix, iy, iw, ih, x, y, w, h ){
- var r = this;
-
- try {
- context.drawImage( img, ix, iy, iw, ih, x, y, w, h );
- } catch(e){
- r.data.canvasNeedsRedraw[r.NODE] = true;
- r.data.canvasNeedsRedraw[r.DRAG] = true;
-
- r.drawingImage = true;
-
- r.redraw();
- }
-};
-
-CRp.drawInscribedImage = function(context, img, node) {
- var r = this;
- var nodeX = node._private.position.x;
- var nodeY = node._private.position.y;
- var style = node._private.style;
- var fit = style['background-fit'].value;
- var xPos = style['background-position-x'];
- var yPos = style['background-position-y'];
- var repeat = style['background-repeat'].value;
- var nodeW = node.width();
- var nodeH = node.height();
- var rs = node._private.rscratch;
- var clip = style['background-clip'].value;
- var shouldClip = clip === 'node';
- var imgOpacity = style['background-image-opacity'].value;
-
- var imgW = img.width || img.cachedW;
- var imgH = img.height || img.cachedH;
-
- // workaround for broken browsers like ie
- if( null == imgW || null == imgH ){
- document.body.appendChild( img );
-
- imgW = img.cachedW = img.width || img.offsetWidth;
- imgH = img.cachedH = img.height || img.offsetHeight;
-
- document.body.removeChild( img );
- }
-
- var w = imgW;
- var h = imgH;
-
- var bgW = style['background-width'];
- if( bgW.value !== 'auto' ){
- if( bgW.units === '%' ){
- w = bgW.value/100 * nodeW;
- } else {
- w = bgW.pfValue;
- }
- }
-
- var bgH = style['background-height'];
- if( bgH.value !== 'auto' ){
- if( bgH.units === '%' ){
- h = bgH.value/100 * nodeH;
- } else {
- h = bgH.pfValue;
- }
- }
-
- if( w === 0 || h === 0 ){
- return; // no point in drawing empty image (and chrome is broken in this case)
- }
-
- if( fit === 'contain' ){
- var scale = Math.min( nodeW/w, nodeH/h );
-
- w *= scale;
- h *= scale;
-
- } else if( fit === 'cover' ){
- var scale = Math.max( nodeW/w, nodeH/h );
-
- w *= scale;
- h *= scale;
- }
-
- var x = (nodeX - nodeW/2); // left
- if( xPos.units === '%' ){
- x += (nodeW - w) * xPos.value/100;
- } else {
- x += xPos.pfValue;
- }
-
- var y = (nodeY - nodeH/2); // top
- if( yPos.units === '%' ){
- y += (nodeH - h) * yPos.value/100;
- } else {
- y += yPos.pfValue;
- }
-
- if( rs.pathCache ){
- x -= nodeX;
- y -= nodeY;
-
- nodeX = 0;
- nodeY = 0;
- }
-
- var gAlpha = context.globalAlpha;
-
- context.globalAlpha = imgOpacity;
-
- if( repeat === 'no-repeat' ){
-
- if( shouldClip ){
- context.save();
-
- if( rs.pathCache ){
- context.clip( rs.pathCache );
- } else {
- r.nodeShapes[r.getNodeShape(node)].draw(
- context,
- nodeX, nodeY,
- nodeW, nodeH);
-
- context.clip();
- }
- }
-
- r.safeDrawImage( context, img, 0, 0, imgW, imgH, x, y, w, h );
-
- if( shouldClip ){
- context.restore();
- }
- } else {
- var pattern = context.createPattern( img, repeat );
- context.fillStyle = pattern;
-
- r.nodeShapes[r.getNodeShape(node)].draw(
- context,
- nodeX, nodeY,
- nodeW, nodeH);
-
- context.translate(x, y);
- context.fill();
- context.translate(-x, -y);
- }
-
- context.globalAlpha = gAlpha;
-
-};
-
-module.exports = CRp;
-
-},{}],65:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../../is');
-
-var CRp = {};
-
-// Draw edge text
-CRp.drawEdgeText = function(context, edge) {
- var text = edge._private.style['label'].strValue;
-
- if( !text || text.match(/^\s+$/) ){
- return;
- }
-
- if( this.hideEdgesOnViewport && (this.dragData.didDrag || this.pinching || this.hoverData.dragging || this.data.wheel || this.swipePanning) ){ return; } // save cycles on pinching
-
- var computedSize = edge._private.style['font-size'].pfValue * edge.cy().zoom();
- var minSize = edge._private.style['min-zoomed-font-size'].pfValue;
-
- if( computedSize < minSize ){
- return;
- }
-
- // Calculate text draw position
-
- context.textAlign = 'center';
- context.textBaseline = 'middle';
-
- var rs = edge._private.rscratch;
- if( !is.number( rs.labelX ) || !is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered
-
- var style = edge._private.style;
- var autorotate = style['edge-text-rotation'].strValue === 'autorotate';
- var theta;
-
- if( autorotate ){
- theta = rs.labelAngle;
-
- context.translate(rs.labelX, rs.labelY);
- context.rotate(theta);
-
- this.drawText(context, edge, 0, 0);
-
- context.rotate(-theta);
- context.translate(-rs.labelX, -rs.labelY);
- } else {
- this.drawText(context, edge, rs.labelX, rs.labelY);
- }
-
-};
-
-// Draw node text
-CRp.drawNodeText = function(context, node) {
- var text = node._private.style['label'].strValue;
-
- if ( !text || text.match(/^\s+$/) ) {
- return;
- }
-
- var computedSize = node._private.style['font-size'].pfValue * node.cy().zoom();
- var minSize = node._private.style['min-zoomed-font-size'].pfValue;
-
- if( computedSize < minSize ){
- return;
- }
-
- // this.recalculateNodeLabelProjection( node );
-
- var textHalign = node._private.style['text-halign'].strValue;
- var textValign = node._private.style['text-valign'].strValue;
- var rs = node._private.rscratch;
- if( !is.number( rs.labelX ) || !is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered
-
- switch( textHalign ){
- case 'left':
- context.textAlign = 'right';
- break;
-
- case 'right':
- context.textAlign = 'left';
- break;
-
- default: // e.g. center
- context.textAlign = 'center';
- }
-
- switch( textValign ){
- case 'top':
- context.textBaseline = 'bottom';
- break;
-
- case 'bottom':
- context.textBaseline = 'top';
- break;
-
- default: // e.g. center
- context.textBaseline = 'middle';
- }
-
- this.drawText(context, node, rs.labelX, rs.labelY);
-};
-
-CRp.getFontCache = function(context){
- var cache;
-
- this.fontCaches = this.fontCaches || [];
-
- for( var i = 0; i < this.fontCaches.length; i++ ){
- cache = this.fontCaches[i];
-
- if( cache.context === context ){
- return cache;
- }
- }
-
- cache = {
- context: context
- };
- this.fontCaches.push(cache);
-
- return cache;
-};
-
-// set up canvas context with font
-// returns transformed text string
-CRp.setupTextStyle = function( context, element ){
- // Font style
- var parentOpacity = element.effectiveOpacity();
- var style = element._private.style;
- var labelStyle = style['font-style'].strValue;
- var labelSize = style['font-size'].pfValue + 'px';
- var labelFamily = style['font-family'].strValue;
- var labelWeight = style['font-weight'].strValue;
- var opacity = style['text-opacity'].value * style['opacity'].value * parentOpacity;
- var outlineOpacity = style['text-outline-opacity'].value * opacity;
- var color = style['color'].value;
- var outlineColor = style['text-outline-color'].value;
- var shadowBlur = style['text-shadow-blur'].pfValue;
- var shadowOpacity = style['text-shadow-opacity'].value;
- var shadowColor = style['text-shadow-color'].value;
- var shadowOffsetX = style['text-shadow-offset-x'].pfValue;
- var shadowOffsetY = style['text-shadow-offset-y'].pfValue;
-
- var fontCacheKey = element._private.fontKey;
- var cache = this.getFontCache(context);
-
- if( cache.key !== fontCacheKey ){
- context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
-
- cache.key = fontCacheKey;
- }
-
- var text = this.getLabelText( element );
-
- // Calculate text draw position based on text alignment
-
- // so text outlines aren't jagged
- context.lineJoin = 'round';
-
- this.fillStyle(context, color[0], color[1], color[2], opacity);
-
- this.strokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
-
- this.shadowStyle(context, shadowColor, shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY);
-
- return text;
-};
-
-function roundRect(ctx, x, y, width, height, radius) {
- var radius = radius || 5;
- ctx.beginPath();
- ctx.moveTo(x + radius, y);
- ctx.lineTo(x + width - radius, y);
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
- ctx.lineTo(x + width, y + height - radius);
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
- ctx.lineTo(x + radius, y + height);
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
- ctx.lineTo(x, y + radius);
- ctx.quadraticCurveTo(x, y, x + radius, y);
- ctx.closePath();
- ctx.fill();
-}
-
-// Draw text
-CRp.drawText = function(context, element, textX, textY) {
- var _p = element._private;
- var style = _p.style;
- var rstyle = _p.rstyle;
- var rscratch = _p.rscratch;
- var parentOpacity = element.effectiveOpacity();
- if( parentOpacity === 0 || style['text-opacity'].value === 0){ return; }
-
- var text = this.setupTextStyle( context, element );
- var halign = style['text-halign'].value;
- var valign = style['text-valign'].value;
-
- if( element.isEdge() ){
- halign = 'center';
- valign = 'center';
- }
-
- if( element.isNode() ){
- var pLeft = style['padding-left'].pfValue;
- var pRight = style['padding-right'].pfValue;
- var pTop = style['padding-top'].pfValue;
- var pBottom = style['padding-bottom'].pfValue;
-
- textX += pLeft/2;
- textX -= pRight/2;
-
- textY += pTop/2;
- textY -= pBottom/2;
- }
-
- if ( text != null && !isNaN(textX) && !isNaN(textY)) {
- var backgroundOpacity = style['text-background-opacity'].value;
- var borderOpacity = style['text-border-opacity'].value;
- var textBorderWidth = style['text-border-width'].pfValue;
-
- if( backgroundOpacity > 0 || (textBorderWidth > 0 && borderOpacity > 0) ){
- var margin = 4 + textBorderWidth/2;
-
- if (element.isNode()) {
- //Move textX, textY to include the background margins
- if (valign === 'top') {
- textY -= margin;
- } else if (valign === 'bottom') {
- textY += margin;
- }
- if (halign === 'left') {
- textX -= margin;
- } else if (halign === 'right') {
- textX += margin;
- }
- }
-
- var bgWidth = rstyle.labelWidth;
- var bgHeight = rstyle.labelHeight;
- var bgX = textX;
-
- if (halign) {
- if (halign == 'center') {
- bgX = bgX - bgWidth / 2;
- } else if (halign == 'left') {
- bgX = bgX- bgWidth;
- }
- }
-
- var bgY = textY;
-
- if (element.isNode()) {
- if (valign == 'top') {
- bgY = bgY - bgHeight;
- } else if (valign == 'center') {
- bgY = bgY- bgHeight / 2;
- }
- } else {
- bgY = bgY - bgHeight / 2;
- }
-
- if (style['edge-text-rotation'].strValue === 'autorotate') {
- textY = 0;
- bgWidth += 4;
- bgX = textX - bgWidth / 2;
- bgY = textY - bgHeight / 2;
- } else {
- // Adjust with border width & margin
- bgX -= margin;
- bgY -= margin;
- bgHeight += margin*2;
- bgWidth += margin*2;
- }
-
- if( backgroundOpacity > 0 ){
- var textFill = context.fillStyle;
- var textBackgroundColor = style['text-background-color'].value;
-
- context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
- var styleShape = style['text-background-shape'].strValue;
- if (styleShape == 'roundrectangle') {
- roundRect(context, bgX, bgY, bgWidth, bgHeight, 2);
- } else {
- context.fillRect(bgX,bgY,bgWidth,bgHeight);
- }
- context.fillStyle = textFill;
- }
-
- if( textBorderWidth > 0 && borderOpacity > 0 ){
- var textStroke = context.strokeStyle;
- var textLineWidth = context.lineWidth;
- var textBorderColor = style['text-border-color'].value;
- var textBorderStyle = style['text-border-style'].value;
-
- context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
- context.lineWidth = textBorderWidth;
-
- if( context.setLineDash ){ // for very outofdate browsers
- switch( textBorderStyle ){
- case 'dotted':
- context.setLineDash([ 1, 1 ]);
- break;
- case 'dashed':
- context.setLineDash([ 4, 2 ]);
- break;
- case 'double':
- context.lineWidth = textBorderWidth/4; // 50% reserved for white between the two borders
- context.setLineDash([ ]);
- break;
- case 'solid':
- context.setLineDash([ ]);
- break;
- }
- }
-
- context.strokeRect(bgX,bgY,bgWidth,bgHeight);
-
- if( textBorderStyle === 'double' ){
- var whiteWidth = textBorderWidth/2;
-
- context.strokeRect(bgX+whiteWidth,bgY+whiteWidth,bgWidth-whiteWidth*2,bgHeight-whiteWidth*2);
- }
-
- if( context.setLineDash ){ // for very outofdate browsers
- context.setLineDash([ ]);
- }
- context.lineWidth = textLineWidth;
- context.strokeStyle = textStroke;
- }
-
- }
-
- var lineWidth = 2 * style['text-outline-width'].pfValue; // *2 b/c the stroke is drawn centred on the middle
-
- if( lineWidth > 0 ){
- context.lineWidth = lineWidth;
- }
-
- if( style['text-wrap'].value === 'wrap' ){
- var lines = rscratch.labelWrapCachedLines;
- var lineHeight = rstyle.labelHeight / lines.length;
-
- switch( valign ){
- case 'top':
- textY -= (lines.length - 1) * lineHeight;
- break;
-
- case 'bottom':
- // nothing required
- break;
-
- default:
- case 'center':
- textY -= (lines.length - 1) * lineHeight / 2;
- }
-
- for( var l = 0; l < lines.length; l++ ){
- if( lineWidth > 0 ){
- context.strokeText( lines[l], textX, textY );
- }
-
- context.fillText( lines[l], textX, textY );
-
- textY += lineHeight;
- }
-
- } else {
- if( lineWidth > 0 ){
- context.strokeText( text, textX, textY );
- }
-
- context.fillText( text, textX, textY );
- }
-
-
- this.shadowStyle(context, 'transparent', 0); // reset for next guy
- }
-};
-
-
-module.exports = CRp;
-
-},{"../../../is":77}],66:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../../is');
-
-var CRp = {};
-
-// Draw node
-CRp.drawNode = function(context, node, drawOverlayInstead) {
-
- var r = this;
- var nodeWidth, nodeHeight;
- var style = node._private.style;
- var rs = node._private.rscratch;
- var _p = node._private;
- var pos = _p.position;
-
- if( !is.number(pos.x) || !is.number(pos.y) ){
- return; // can't draw node with undefined position
- }
-
- var usePaths = this.usePaths();
- var canvasContext = context;
- var path;
- var pathCacheHit = false;
-
- var overlayPadding = style['overlay-padding'].pfValue;
- var overlayOpacity = style['overlay-opacity'].value;
- var overlayColor = style['overlay-color'].value;
-
- if( drawOverlayInstead && overlayOpacity === 0 ){ // exit early if drawing overlay but none to draw
- return;
- }
-
- var parentOpacity = node.effectiveOpacity();
- if( parentOpacity === 0 ){ return; }
-
- nodeWidth = node.width() + style['padding-left'].pfValue + style['padding-right'].pfValue;
- nodeHeight = node.height() + style['padding-top'].pfValue + style['padding-bottom'].pfValue;
-
- context.lineWidth = style['border-width'].pfValue;
-
- if( drawOverlayInstead === undefined || !drawOverlayInstead ){
-
- var url = style['background-image'].value[2] ||
- style['background-image'].value[1];
- var image;
-
- if (url !== undefined) {
-
- // get image, and if not loaded then ask to redraw when later loaded
- image = this.getCachedImage(url, function(){
- r.data.canvasNeedsRedraw[r.NODE] = true;
- r.data.canvasNeedsRedraw[r.DRAG] = true;
-
- r.drawingImage = true;
-
- r.redraw();
- });
-
- var prevBging = _p.backgrounding;
- _p.backgrounding = !image.complete;
-
- if( prevBging !== _p.backgrounding ){ // update style b/c :backgrounding state changed
- node.updateStyle( false );
- }
- }
-
- // Node color & opacity
-
- var bgColor = style['background-color'].value;
- var borderColor = style['border-color'].value;
- var borderStyle = style['border-style'].value;
-
- this.fillStyle(context, bgColor[0], bgColor[1], bgColor[2], style['background-opacity'].value * parentOpacity);
-
- this.strokeStyle(context, borderColor[0], borderColor[1], borderColor[2], style['border-opacity'].value * parentOpacity);
-
- var shadowBlur = style['shadow-blur'].pfValue;
- var shadowOpacity = style['shadow-opacity'].value;
- var shadowColor = style['shadow-color'].value;
- var shadowOffsetX = style['shadow-offset-x'].pfValue;
- var shadowOffsetY = style['shadow-offset-y'].pfValue;
-
- this.shadowStyle(context, shadowColor, shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY);
-
- context.lineJoin = 'miter'; // so borders are square with the node shape
-
- if( context.setLineDash ){ // for very outofdate browsers
- switch( borderStyle ){
- case 'dotted':
- context.setLineDash([ 1, 1 ]);
- break;
-
- case 'dashed':
- context.setLineDash([ 4, 2 ]);
- break;
-
- case 'solid':
- case 'double':
- context.setLineDash([ ]);
- break;
- }
- }
-
-
- var styleShape = style['shape'].strValue;
-
- if( usePaths ){
- var pathCacheKey = styleShape + '$' + nodeWidth +'$' + nodeHeight;
-
- context.translate( pos.x, pos.y );
-
- if( rs.pathCacheKey === pathCacheKey ){
- path = context = rs.pathCache;
- pathCacheHit = true;
- } else {
- path = context = new Path2D();
- rs.pathCacheKey = pathCacheKey;
- rs.pathCache = path;
- }
- }
-
- if( !pathCacheHit ){
-
- var npos = pos;
-
- if( usePaths ){
- npos = {
- x: 0,
- y: 0
- };
- }
-
- r.nodeShapes[this.getNodeShape(node)].draw(
- context,
- npos.x,
- npos.y,
- nodeWidth,
- nodeHeight);
- }
-
- context = canvasContext;
-
- if( usePaths ){
- context.fill( path );
- } else {
- context.fill();
- }
-
- this.shadowStyle(context, 'transparent', 0); // reset for next guy
-
- if (url !== undefined) {
- if( image.complete ){
- this.drawInscribedImage(context, image, node);
- }
- }
-
- var darkness = style['background-blacken'].value;
- var borderWidth = style['border-width'].pfValue;
-
- if( this.hasPie(node) ){
- this.drawPie( context, node, parentOpacity );
-
- // redraw path for blacken and border
- if( darkness !== 0 || borderWidth !== 0 ){
-
- if( !usePaths ){
- r.nodeShapes[this.getNodeShape(node)].draw(
- context,
- pos.x,
- pos.y,
- nodeWidth,
- nodeHeight);
- }
- }
- }
-
- if( darkness > 0 ){
- this.fillStyle(context, 0, 0, 0, darkness);
-
- if( usePaths ){
- context.fill( path );
- } else {
- context.fill();
- }
-
- } else if( darkness < 0 ){
- this.fillStyle(context, 255, 255, 255, -darkness);
-
- if( usePaths ){
- context.fill( path );
- } else {
- context.fill();
- }
- }
-
- // Border width, draw border
- if (borderWidth > 0) {
-
- if( usePaths ){
- context.stroke( path );
- } else {
- context.stroke();
- }
-
- if( borderStyle === 'double' ){
- context.lineWidth = style['border-width'].pfValue/3;
-
- var gco = context.globalCompositeOperation;
- context.globalCompositeOperation = 'destination-out';
-
- if( usePaths ){
- context.stroke( path );
- } else {
- context.stroke();
- }
-
- context.globalCompositeOperation = gco;
- }
-
- }
-
- if( usePaths ){
- context.translate( -pos.x, -pos.y );
- }
-
- // reset in case we changed the border style
- if( context.setLineDash ){ // for very outofdate browsers
- context.setLineDash([ ]);
- }
-
- // draw the overlay
- } else {
-
- if( overlayOpacity > 0 ){
- this.fillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
-
- r.nodeShapes['roundrectangle'].draw(
- context,
- node._private.position.x,
- node._private.position.y,
- nodeWidth + overlayPadding * 2,
- nodeHeight + overlayPadding * 2
- );
-
- context.fill();
- }
- }
-
-};
-
-// does the node have at least one pie piece?
-CRp.hasPie = function(node){
- node = node[0]; // ensure ele ref
-
- return node._private.hasPie;
-};
-
-CRp.drawPie = function( context, node, nodeOpacity ){
- node = node[0]; // ensure ele ref
-
- var _p = node._private;
- var cyStyle = node.cy().style();
- var style = _p.style;
- var pieSize = style['pie-size'];
- var nodeW = node.width();
- var nodeH = node.height();
- var x = _p.position.x;
- var y = _p.position.y;
- var radius = Math.min( nodeW, nodeH ) / 2; // must fit in node
- var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
- var usePaths = this.usePaths();
-
- if( usePaths ){
- x = 0;
- y = 0;
- }
-
- if( pieSize.units === '%' ){
- radius = radius * pieSize.value / 100;
- } else if( pieSize.pfValue !== undefined ){
- radius = pieSize.pfValue / 2;
- }
-
- for( var i = 1; i <= cyStyle.pieBackgroundN; i++ ){ // 1..N
- var size = style['pie-' + i + '-background-size'].value;
- var color = style['pie-' + i + '-background-color'].value;
- var opacity = style['pie-' + i + '-background-opacity'].value * nodeOpacity;
- var percent = size / 100; // map integer range [0, 100] to [0, 1]
-
- // percent can't push beyond 1
- if( percent + lastPercent > 1 ){
- percent = 1 - lastPercent;
- }
-
- var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
- var angleDelta = 2 * Math.PI * percent;
- var angleEnd = angleStart + angleDelta;
-
- // ignore if
- // - zero size
- // - we're already beyond the full circle
- // - adding the current slice would go beyond the full circle
- if( size === 0 || lastPercent >= 1 || lastPercent + percent > 1 ){
- continue;
- }
-
- context.beginPath();
- context.moveTo(x, y);
- context.arc( x, y, radius, angleStart, angleEnd );
- context.closePath();
-
- this.fillStyle(context, color[0], color[1], color[2], opacity);
-
- context.fill();
-
- lastPercent += percent;
- }
-
-};
-
-
-module.exports = CRp;
-
-},{"../../../is":77}],67:[function(_dereq_,module,exports){
-'use strict';
-
-var CRp = {};
-
-var util = _dereq_('../../../util');
-var math = _dereq_('../../../math');
-
-var motionBlurDelay = 100;
-
-// var isFirefox = typeof InstallTrigger !== 'undefined';
-
-CRp.getPixelRatio = function(){
- var context = this.data.contexts[0];
-
- if( this.forcedPixelRatio != null ){
- return this.forcedPixelRatio;
- }
-
- var backingStore = context.backingStorePixelRatio ||
- context.webkitBackingStorePixelRatio ||
- context.mozBackingStorePixelRatio ||
- context.msBackingStorePixelRatio ||
- context.oBackingStorePixelRatio ||
- context.backingStorePixelRatio || 1;
-
- return (window.devicePixelRatio || 1) / backingStore;
-};
-
-CRp.paintCache = function(context){
- var caches = this.paintCaches = this.paintCaches || [];
- var needToCreateCache = true;
- var cache;
-
- for(var i = 0; i < caches.length; i++ ){
- cache = caches[i];
-
- if( cache.context === context ){
- needToCreateCache = false;
- break;
- }
- }
-
- if( needToCreateCache ){
- cache = {
- context: context
- };
- caches.push( cache );
- }
-
- return cache;
-};
-
-CRp.fillStyle = function(context, r, g, b, a){
- context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
-
- // turn off for now, seems context does its own caching
-
- // var cache = this.paintCache(context);
-
- // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
-
- // if( cache.fillStyle !== fillStyle ){
- // context.fillStyle = cache.fillStyle = fillStyle;
- // }
-};
-
-CRp.strokeStyle = function(context, r, g, b, a){
- context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
-
- // turn off for now, seems context does its own caching
-
- // var cache = this.paintCache(context);
-
- // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
-
- // if( cache.strokeStyle !== strokeStyle ){
- // context.strokeStyle = cache.strokeStyle = strokeStyle;
- // }
-};
-
-CRp.shadowStyle = function(context, color, opacity, blur, offsetX, offsetY){
- var zoom = this.cy.zoom();
-
- var cache = this.paintCache(context);
-
- // don't make expensive changes to the shadow style if it's not used
- if( cache.shadowOpacity === 0 && opacity === 0 ){
- return;
- }
-
- cache.shadowOpacity = opacity;
-
- if (opacity > 0) {
- context.shadowBlur = blur * zoom;
- context.shadowColor = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + opacity + ")";
- context.shadowOffsetX = offsetX * zoom;
- context.shadowOffsetY = offsetY * zoom;
- } else {
- context.shadowBlur = 0;
- context.shadowColor = "transparent";
- }
-};
-
-// Resize canvas
-CRp.matchCanvasSize = function(container) {
- var r = this;
- var data = r.data;
- var width = container.clientWidth;
- var height = container.clientHeight;
- var pixelRatio = r.getPixelRatio();
- var mbPxRatio = r.motionBlurPxRatio;
-
- if(
- container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] ||
- container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]
- ){
- pixelRatio = mbPxRatio;
- }
-
- var canvasWidth = width * pixelRatio;
- var canvasHeight = height * pixelRatio;
- var canvas;
-
- if( canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight ){
- return; // save cycles if same
- }
-
- r.fontCaches = null; // resizing resets the style
-
- var canvasContainer = data.canvasContainer;
- canvasContainer.style.width = width + 'px';
- canvasContainer.style.height = height + 'px';
-
- for (var i = 0; i < r.CANVAS_LAYERS; i++) {
-
- canvas = data.canvases[i];
-
- if (canvas.width !== canvasWidth || canvas.height !== canvasHeight) {
-
- canvas.width = canvasWidth;
- canvas.height = canvasHeight;
-
- canvas.style.width = width + 'px';
- canvas.style.height = height + 'px';
- }
- }
-
- for (var i = 0; i < r.BUFFER_COUNT; i++) {
-
- canvas = data.bufferCanvases[i];
-
- if (canvas.width !== canvasWidth || canvas.height !== canvasHeight) {
-
- canvas.width = canvasWidth;
- canvas.height = canvasHeight;
-
- canvas.style.width = width + 'px';
- canvas.style.height = height + 'px';
- }
- }
-
- r.textureMult = 1;
- if( pixelRatio <= 1 ){
- canvas = data.bufferCanvases[ r.TEXTURE_BUFFER ];
-
- r.textureMult = 2;
- canvas.width = canvasWidth * r.textureMult;
- canvas.height = canvasHeight * r.textureMult;
- }
-
- r.canvasWidth = canvasWidth;
- r.canvasHeight = canvasHeight;
-
-};
-
-CRp.renderTo = function( cxt, zoom, pan, pxRatio ){
- this.render({
- forcedContext: cxt,
- forcedZoom: zoom,
- forcedPan: pan,
- drawAllLayers: true,
- forcedPxRatio: pxRatio
- });
-};
-
-CRp.render = function( options ) {
- options = options || util.staticEmptyObject();
-
- var forcedContext = options.forcedContext;
- var drawAllLayers = options.drawAllLayers;
- var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
- var forcedZoom = options.forcedZoom;
- var forcedPan = options.forcedPan;
- var r = this;
- var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
- var cy = r.cy; var data = r.data;
- var needDraw = data.canvasNeedsRedraw;
- var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
- var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
- var mbPxRatio = r.motionBlurPxRatio;
- var hasCompoundNodes = cy.hasCompoundNodes();
- var inNodeDragGesture = r.hoverData.draggingEles;
- var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
- motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
- var motionBlurFadeEffect = motionBlur;
-
- if( !forcedContext && r.motionBlurTimeout ){
- clearTimeout( r.motionBlurTimeout );
- }
-
- if( motionBlur ){
- if( r.mbFrames == null ){
- r.mbFrames = 0;
- }
-
- if( !r.drawingImage ){ // image loading frames don't count towards motion blur blurry frames
- r.mbFrames++;
- }
-
- if( r.mbFrames < 3 ){ // need several frames before even high quality motionblur
- motionBlurFadeEffect = false;
- }
-
- // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
- if( r.mbFrames > r.minMbLowQualFrames ){
- //r.fullQualityMb = false;
- r.motionBlurPxRatio = r.mbPxRBlurry;
- }
- }
-
- if( r.clearingMotionBlur ){
- r.motionBlurPxRatio = 1;
- }
-
- // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
- // because a rogue async texture frame would clear needDraw
- if( r.textureDrawLastFrame && !textureDraw ){
- needDraw[r.NODE] = true;
- needDraw[r.SELECT_BOX] = true;
- }
-
- var edges = r.getCachedEdges();
- var coreStyle = cy.style()._private.coreStyle;
-
- var zoom = cy.zoom();
- var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
- var pan = cy.pan();
- var effectivePan = {
- x: pan.x,
- y: pan.y
- };
-
- var vp = {
- zoom: zoom,
- pan: {
- x: pan.x,
- y: pan.y
- }
- };
- var prevVp = r.prevViewport;
- var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y;
-
- // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed)
- if( !viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes) ){
- r.motionBlurPxRatio = 1;
- }
-
- if( forcedPan ){
- effectivePan = forcedPan;
- }
-
- // apply pixel ratio
-
- effectiveZoom *= pixelRatio;
- effectivePan.x *= pixelRatio;
- effectivePan.y *= pixelRatio;
-
- var eles = {
- drag: {
- nodes: [],
- edges: [],
- eles: []
- },
- nondrag: {
- nodes: [],
- edges: [],
- eles: []
- }
- };
-
- function mbclear( context, x, y, w, h ){
- var gco = context.globalCompositeOperation;
-
- context.globalCompositeOperation = 'destination-out';
- r.fillStyle( context, 255, 255, 255, r.motionBlurTransparency );
- context.fillRect(x, y, w, h);
-
- context.globalCompositeOperation = gco;
- }
-
- function setContextTransform(context, clear){
- var ePan, eZoom, w, h;
-
- if( !r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG]) ){
- ePan = {
- x: pan.x * mbPxRatio,
- y: pan.y * mbPxRatio
- };
-
- eZoom = zoom * mbPxRatio;
-
- w = r.canvasWidth * mbPxRatio;
- h = r.canvasHeight * mbPxRatio;
- } else {
- ePan = effectivePan;
- eZoom = effectiveZoom;
-
- w = r.canvasWidth;
- h = r.canvasHeight;
- }
-
- context.setTransform(1, 0, 0, 1, 0, 0);
-
- if( clear === 'motionBlur' ){
- mbclear(context, 0, 0, w, h);
- } else if( !forcedContext && (clear === undefined || clear) ){
- context.clearRect(0, 0, w, h);
- }
-
- if( !drawAllLayers ){
- context.translate( ePan.x, ePan.y );
- context.scale( eZoom, eZoom );
- }
- if( forcedPan ){
- context.translate( forcedPan.x, forcedPan.y );
- }
- if( forcedZoom ){
- context.scale( forcedZoom, forcedZoom );
- }
- }
-
- if( !textureDraw ){
- r.textureDrawLastFrame = false;
- }
-
- if( textureDraw ){
- r.textureDrawLastFrame = true;
-
- var bb;
-
- if( !r.textureCache ){
- r.textureCache = {};
-
- bb = r.textureCache.bb = cy.elements().boundingBox();
-
- r.textureCache.texture = r.data.bufferCanvases[ r.TEXTURE_BUFFER ];
-
- var cxt = r.data.bufferContexts[ r.TEXTURE_BUFFER ];
-
- cxt.setTransform(1, 0, 0, 1, 0, 0);
- cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
-
- r.render({
- forcedContext: cxt,
- drawOnlyNodeLayer: true,
- forcedPxRatio: pixelRatio * r.textureMult
- });
-
- var vp = r.textureCache.viewport = {
- zoom: cy.zoom(),
- pan: cy.pan(),
- width: r.canvasWidth,
- height: r.canvasHeight
- };
-
- vp.mpan = {
- x: (0 - vp.pan.x)/vp.zoom,
- y: (0 - vp.pan.y)/vp.zoom
- };
- }
-
- needDraw[r.DRAG] = false;
- needDraw[r.NODE] = false;
-
- var context = data.contexts[r.NODE];
-
- var texture = r.textureCache.texture;
- var vp = r.textureCache.viewport;
- bb = r.textureCache.bb;
-
- context.setTransform(1, 0, 0, 1, 0, 0);
-
- if( motionBlur ){
- mbclear(context, 0, 0, vp.width, vp.height);
- } else {
- context.clearRect(0, 0, vp.width, vp.height);
- }
-
- var outsideBgColor = coreStyle['outside-texture-bg-color'].value;
- var outsideBgOpacity = coreStyle['outside-texture-bg-opacity'].value;
- r.fillStyle( context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity );
- context.fillRect( 0, 0, vp.width, vp.height );
-
- var zoom = cy.zoom();
-
- setContextTransform( context, false );
-
- context.clearRect( vp.mpan.x, vp.mpan.y, vp.width/vp.zoom/pixelRatio, vp.height/vp.zoom/pixelRatio );
- context.drawImage( texture, vp.mpan.x, vp.mpan.y, vp.width/vp.zoom/pixelRatio, vp.height/vp.zoom/pixelRatio );
-
- } else if( r.textureOnViewport && !forcedContext ){ // clear the cache since we don't need it
- r.textureCache = null;
- }
-
- var vpManip = (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles);
- var hideEdges = r.hideEdgesOnViewport && vpManip;
- var hideLabels = r.hideLabelsOnViewport && vpManip;
-
- if (needDraw[r.DRAG] || needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer) {
- if( hideEdges ){
- } else {
- r.findEdgeControlPoints(edges);
- }
-
- var zEles = r.getCachedZSortedEles();
- var extent = cy.extent();
-
- for (var i = 0; i < zEles.length; i++) {
- var ele = zEles[i];
- var list;
- var bb = forcedContext ? null : ele.boundingBox();
- var insideExtent = forcedContext ? true : math.boundingBoxesIntersect( extent, bb );
-
- if( !insideExtent ){ continue; } // no need to render
-
- if ( ele._private.rscratch.inDragLayer ) {
- list = eles.drag;
- } else {
- list = eles.nondrag;
- }
-
- list.eles.push( ele );
- }
-
- }
-
-
- function drawElements( list, context ){
- var eles = list.eles;
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
-
- if( ele.isNode() ){
- r.drawNode(context, ele);
-
- if( !hideLabels ){
- r.drawNodeText(context, ele);
- }
-
- r.drawNode(context, ele, true);
- } else if( !hideEdges ) {
- r.drawEdge(context, ele);
-
- if( !hideLabels ){
- r.drawEdgeText(context, ele);
- }
-
- r.drawEdge(context, ele, true);
- }
-
-
- }
-
- }
-
- var needMbClear = [];
-
- needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
- if( needMbClear[r.NODE] ){ r.clearedForMotionBlur[r.NODE] = true; }
-
- needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
- if( needMbClear[r.DRAG] ){ r.clearedForMotionBlur[r.DRAG] = true; }
-
- if( needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE] ){
- var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
- var context = forcedContext || ( useBuffer ? r.data.bufferContexts[ r.MOTIONBLUR_BUFFER_NODE ] : data.contexts[r.NODE] );
- var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
-
- setContextTransform( context, clear );
- drawElements(eles.nondrag, context);
-
- if( !drawAllLayers && !motionBlur ){
- needDraw[r.NODE] = false;
- }
- }
-
- if ( !drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG]) ) {
- var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
- var context = forcedContext || ( useBuffer ? r.data.bufferContexts[ r.MOTIONBLUR_BUFFER_DRAG ] : data.contexts[r.DRAG] );
-
- setContextTransform( context, motionBlur && !useBuffer ? 'motionBlur' : undefined );
- drawElements(eles.drag, context);
-
- if( !drawAllLayers && !motionBlur ){
- needDraw[r.DRAG] = false;
- }
- }
-
- if( r.showFps || (!drawOnlyNodeLayer && (needDraw[r.SELECT_BOX] && !drawAllLayers)) ) {
- var context = forcedContext || data.contexts[r.SELECT_BOX];
-
- setContextTransform( context );
-
- if( r.selection[4] == 1 && ( r.hoverData.selecting || r.touchData.selecting ) ){
- var zoom = r.cy.zoom();
- var borderWidth = coreStyle['selection-box-border-width'].value / zoom;
-
- context.lineWidth = borderWidth;
- context.fillStyle = "rgba("
- + coreStyle['selection-box-color'].value[0] + ","
- + coreStyle['selection-box-color'].value[1] + ","
- + coreStyle['selection-box-color'].value[2] + ","
- + coreStyle['selection-box-opacity'].value + ")";
-
- context.fillRect(
- r.selection[0],
- r.selection[1],
- r.selection[2] - r.selection[0],
- r.selection[3] - r.selection[1]);
-
- if (borderWidth > 0) {
- context.strokeStyle = "rgba("
- + coreStyle['selection-box-border-color'].value[0] + ","
- + coreStyle['selection-box-border-color'].value[1] + ","
- + coreStyle['selection-box-border-color'].value[2] + ","
- + coreStyle['selection-box-opacity'].value + ")";
-
- context.strokeRect(
- r.selection[0],
- r.selection[1],
- r.selection[2] - r.selection[0],
- r.selection[3] - r.selection[1]);
- }
- }
-
- if( data.bgActivePosistion && !r.hoverData.selecting ){
- var zoom = r.cy.zoom();
- var pos = data.bgActivePosistion;
-
- context.fillStyle = "rgba("
- + coreStyle['active-bg-color'].value[0] + ","
- + coreStyle['active-bg-color'].value[1] + ","
- + coreStyle['active-bg-color'].value[2] + ","
- + coreStyle['active-bg-opacity'].value + ")";
-
- context.beginPath();
- context.arc(pos.x, pos.y, coreStyle['active-bg-size'].pfValue / zoom, 0, 2 * Math.PI);
- context.fill();
- }
-
- var timeToRender = r.lastRedrawTime;
- if( r.showFps && timeToRender ){
- timeToRender = Math.round( timeToRender );
- var fps = Math.round(1000/timeToRender);
-
- context.setTransform(1, 0, 0, 1, 0, 0);
-
- context.fillStyle = 'rgba(255, 0, 0, 0.75)';
- context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
- context.lineWidth = 1;
- context.fillText( '1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
-
- var maxFps = 60;
- context.strokeRect(0, 30, 250, 20);
- context.fillRect(0, 30, 250 * Math.min(fps/maxFps, 1), 20);
- }
-
- if( !drawAllLayers ){
- needDraw[r.SELECT_BOX] = false;
- }
- }
-
- // motionblur: blit rendered blurry frames
- if( motionBlur && mbPxRatio !== 1 ){
- var cxtNode = data.contexts[r.NODE];
- var txtNode = r.data.bufferCanvases[ r.MOTIONBLUR_BUFFER_NODE ];
-
- var cxtDrag = data.contexts[r.DRAG];
- var txtDrag = r.data.bufferCanvases[ r.MOTIONBLUR_BUFFER_DRAG ];
-
- var drawMotionBlur = function( cxt, txt, needClear ){
- cxt.setTransform(1, 0, 0, 1, 0, 0);
-
- if( needClear || !motionBlurFadeEffect ){
- cxt.clearRect( 0, 0, r.canvasWidth, r.canvasHeight );
- } else {
- mbclear( cxt, 0, 0, r.canvasWidth, r.canvasHeight );
- }
-
- var pxr = mbPxRatio;
-
- cxt.drawImage(
- txt, // img
- 0, 0, // sx, sy
- r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
- 0, 0, // x, y
- r.canvasWidth, r.canvasHeight // w, h
- );
- };
-
- if( needDraw[r.NODE] || needMbClear[r.NODE] ){
- drawMotionBlur( cxtNode, txtNode, needMbClear[r.NODE] );
- needDraw[r.NODE] = false;
- }
-
- if( needDraw[r.DRAG] || needMbClear[r.DRAG] ){
- drawMotionBlur( cxtDrag, txtDrag, needMbClear[r.DRAG] );
- needDraw[r.DRAG] = false;
- }
- }
-
- r.currentlyDrawing = false;
-
- r.prevViewport = vp;
-
- if( r.clearingMotionBlur ){
- r.clearingMotionBlur = false;
- r.motionBlurCleared = true;
- r.motionBlur = true;
- }
-
- if( motionBlur ){
- r.motionBlurTimeout = setTimeout(function(){
- r.motionBlurTimeout = null;
-
- r.clearedForMotionBlur[r.NODE] = false;
- r.clearedForMotionBlur[r.DRAG] = false;
- r.motionBlur = false;
- r.clearingMotionBlur = !textureDraw;
- r.mbFrames = 0;
-
- needDraw[r.NODE] = true;
- needDraw[r.DRAG] = true;
-
- r.redraw();
- }, motionBlurDelay);
- }
-
- r.drawingImage = false;
-
-
- if( !forcedContext && !r.initrender ){
- r.initrender = true;
- cy.trigger('initrender');
- }
-
- if( !forcedContext ){
- cy.triggerOnRender();
- }
-
-};
-
-module.exports = CRp;
-
-},{"../../../math":79,"../../../util":94}],68:[function(_dereq_,module,exports){
-'use strict';
-
- var math = _dereq_('../../../math');
-
- var CRp = {};
-
- // @O Polygon drawing
- CRp.drawPolygonPath = function(
- context, x, y, width, height, points) {
-
- var halfW = width / 2;
- var halfH = height / 2;
-
- if( context.beginPath ){ context.beginPath(); }
-
- context.moveTo( x + halfW * points[0], y + halfH * points[1] );
-
- for (var i = 1; i < points.length / 2; i++) {
- context.lineTo( x + halfW * points[i * 2], y + halfH * points[i * 2 + 1] );
- }
-
- context.closePath();
- };
-
- // Round rectangle drawing
- CRp.drawRoundRectanglePath = function(
- context, x, y, width, height, radius) {
-
- var halfWidth = width / 2;
- var halfHeight = height / 2;
- var cornerRadius = math.getRoundRectangleRadius(width, height);
-
- if( context.beginPath ){ context.beginPath(); }
-
- // Start at top middle
- context.moveTo(x, y - halfHeight);
- // Arc from middle top to right side
- context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius);
- // Arc from right side to bottom
- context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
- // Arc from bottom to left side
- context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
- // Arc from left side to topBorder
- context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius);
- // Join line
- context.lineTo(x, y - halfHeight);
-
-
- context.closePath();
- };
-
- var sin0 = Math.sin(0);
- var cos0 = Math.cos(0);
-
- var sin = {};
- var cos = {};
-
- var ellipseStepSize = Math.PI / 40;
-
- for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize ) {
- sin[i] = Math.sin(i);
- cos[i] = Math.cos(i);
- }
-
- CRp.drawEllipsePath = function(context, centerX, centerY, width, height){
- if( context.beginPath ){ context.beginPath(); }
-
- if( context.ellipse ){
- context.ellipse( centerX, centerY, width/2, height/2, 0, 0, 2*Math.PI );
- } else {
- var xPos, yPos;
- var rw = width/2;
- var rh = height/2;
- for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize ) {
- xPos = centerX - (rw * sin[i]) * sin0 + (rw * cos[i]) * cos0;
- yPos = centerY + (rh * cos[i]) * sin0 + (rh * sin[i]) * cos0;
-
- if (i === 0) {
- context.moveTo(xPos, yPos);
- } else {
- context.lineTo(xPos, yPos);
- }
- }
- }
-
- context.closePath();
- };
-
-module.exports = CRp;
-
-},{"../../../math":79}],69:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../../../is');
-
-var CRp = {};
-
-CRp.createBuffer = function(w, h) {
- var buffer = document.createElement('canvas');
- buffer.width = w;
- buffer.height = h;
-
- return [buffer, buffer.getContext('2d')];
-};
-
-CRp.bufferCanvasImage = function( options ){
- var cy = this.cy;
- var bb = cy.elements().boundingBox();
- var width = options.full ? Math.ceil(bb.w) : this.container.clientWidth;
- var height = options.full ? Math.ceil(bb.h) : this.container.clientHeight;
- var scale = 1;
-
- if( options.scale !== undefined ){
- width *= options.scale;
- height *= options.scale;
-
- scale = options.scale;
- } else if( is.number(options.maxWidth) || is.number(options.maxHeight) ){
- var maxScaleW = Infinity;
- var maxScaleH = Infinity;
-
- if( is.number(options.maxWidth) ){
- maxScaleW = scale * options.maxWidth / width;
- }
-
- if( is.number(options.maxHeight) ){
- maxScaleH = scale * options.maxHeight / height;
- }
-
- scale = Math.min( maxScaleW, maxScaleH );
-
- width *= scale;
- height *= scale;
- }
-
- var buffCanvas = document.createElement('canvas');
-
- buffCanvas.width = width;
- buffCanvas.height = height;
-
- buffCanvas.style.width = width + 'px';
- buffCanvas.style.height = height + 'px';
-
- var buffCxt = buffCanvas.getContext('2d');
-
- // Rasterize the layers, but only if container has nonzero size
- if (width > 0 && height > 0) {
-
- buffCxt.clearRect( 0, 0, width, height );
-
- if( options.bg ){
- buffCxt.fillStyle = options.bg;
- buffCxt.rect( 0, 0, width, height );
- buffCxt.fill();
- }
-
- buffCxt.globalCompositeOperation = 'source-over';
-
- if( options.full ){ // draw the full bounds of the graph
- this.render({
- forcedContext: buffCxt,
- drawAllLayers: true,
- forcedZoom: scale,
- forcedPan: { x: -bb.x1*scale, y: -bb.y1*scale },
- forcedPxRatio: 1
- });
- } else { // draw the current view
- var cyPan = cy.pan();
- var pan = {
- x: cyPan.x * scale,
- y: cyPan.y * scale
- };
- var zoom = cy.zoom() * scale;
-
- this.render({
- forcedContext: buffCxt,
- drawAllLayers: true,
- forcedZoom: zoom,
- forcedPan: pan,
- forcedPxRatio: 1
- });
- }
- }
-
- return buffCanvas;
-};
-
-CRp.png = function( options ){
- return this.bufferCanvasImage( options ).toDataURL('image/png');
-};
-
-CRp.jpg = function( options ){
- return this.bufferCanvasImage( options ).toDataURL('image/jpeg');
-};
-
-module.exports = CRp;
-
-},{"../../../is":77}],70:[function(_dereq_,module,exports){
-/*
-The canvas renderer was written by Yue Dong.
-
-Modifications tracked on Github.
-*/
-
-'use strict';
-
-var util = _dereq_('../../../util');
-var is = _dereq_('../../../is');
-
-var CR = CanvasRenderer;
-var CRp = CanvasRenderer.prototype;
-
-CRp.CANVAS_LAYERS = 3;
-//
-CRp.SELECT_BOX = 0;
-CRp.DRAG = 1;
-CRp.NODE = 2;
-
-CRp.BUFFER_COUNT = 3;
-//
-CRp.TEXTURE_BUFFER = 0;
-CRp.MOTIONBLUR_BUFFER_NODE = 1;
-CRp.MOTIONBLUR_BUFFER_DRAG = 2;
-
-function CanvasRenderer(options) {
- var r = this;
-
- r.data = {
- canvases: new Array(CRp.CANVAS_LAYERS),
- contexts: new Array(CRp.CANVAS_LAYERS),
- canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS),
-
- bufferCanvases: new Array(CRp.BUFFER_COUNT),
- bufferContexts: new Array(CRp.CANVAS_LAYERS)
- };
-
- r.data.canvasContainer = document.createElement('div');
- var containerStyle = r.data.canvasContainer.style;
- r.data.canvasContainer.setAttribute('style', '-webkit-tap-highlight-color: rgba(0,0,0,0);');
- containerStyle.position = 'relative';
- containerStyle.zIndex = '0';
- containerStyle.overflow = 'hidden';
-
- var container = options.cy.container();
- container.appendChild( r.data.canvasContainer );
- container.setAttribute('style', ( container.getAttribute('style') || '' ) + '-webkit-tap-highlight-color: rgba(0,0,0,0);');
-
- for (var i = 0; i < CRp.CANVAS_LAYERS; i++) {
- var canvas = r.data.canvases[i] = document.createElement('canvas');
- r.data.contexts[i] = canvas.getContext('2d');
- canvas.setAttribute( 'style', '-webkit-user-select: none; -moz-user-select: -moz-none; user-select: none; -webkit-tap-highlight-color: rgba(0,0,0,0); outline-style: none;' + ( is.ms() ? ' -ms-touch-action: none; touch-action: none; ' : '' ) );
- canvas.style.position = 'absolute';
- canvas.setAttribute('data-id', 'layer' + i);
- canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i);
- r.data.canvasContainer.appendChild(canvas);
-
- r.data.canvasNeedsRedraw[i] = false;
- }
- r.data.topCanvas = r.data.canvases[0];
-
- r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node');
- r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox');
- r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag');
-
- for (var i = 0; i < CRp.BUFFER_COUNT; i++) {
- r.data.bufferCanvases[i] = document.createElement('canvas');
- r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
- r.data.bufferCanvases[i].style.position = 'absolute';
- r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
- r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
- r.data.bufferCanvases[i].style.visibility = 'hidden';
- //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
- }
-
- r.pathsEnabled = true;
-}
-
-CRp.redrawHint = function( group, bool ){
- var r = this;
-
- switch( group ){
- case 'eles':
- r.data.canvasNeedsRedraw[ CRp.NODE ] = bool;
- break;
- case 'drag':
- r.data.canvasNeedsRedraw[ CRp.DRAG ] = bool;
- break;
- case 'select':
- r.data.canvasNeedsRedraw[ CRp.SELECT_BOX ] = bool;
- break;
- }
-};
-
-// whether to use Path2D caching for drawing
-var pathsImpld = typeof Path2D !== 'undefined';
-
-CRp.path2dEnabled = function( on ){
- if( on === undefined ){
- return this.pathsEnabled;
- }
-
- this.pathsEnabled = on ? true : false;
-};
-
-CRp.usePaths = function(){
- return pathsImpld && this.pathsEnabled;
-};
-
-[
- _dereq_('./arrow-shapes'),
- _dereq_('./drawing-edges'),
- _dereq_('./drawing-images'),
- _dereq_('./drawing-label-text'),
- _dereq_('./drawing-nodes'),
- _dereq_('./drawing-redraw'),
- _dereq_('./drawing-shapes'),
- _dereq_('./export-image'),
- _dereq_('./node-shapes')
-].forEach(function( props ){
- util.extend( CRp, props );
-});
-
-module.exports = CR;
-
-},{"../../../is":77,"../../../util":94,"./arrow-shapes":62,"./drawing-edges":63,"./drawing-images":64,"./drawing-label-text":65,"./drawing-nodes":66,"./drawing-redraw":67,"./drawing-shapes":68,"./export-image":69,"./node-shapes":71}],71:[function(_dereq_,module,exports){
-'use strict';
-
-var CRp = {};
-
-var impl;
-
-CRp.nodeShapeImpl = function( name ){
- var self = this;
-
- return ( impl || (impl = {
- 'ellipse': function( context, centerX, centerY, width, height ){
- self.drawEllipsePath( context, centerX, centerY, width, height );
- },
-
- 'polygon': function( context, centerX, centerY, width, height, points ){
- self.drawPolygonPath( context, centerX, centerY, width, height, points );
- },
-
- 'roundrectangle': function( context, centerX, centerY, width, height ){
- self.drawRoundRectanglePath( context, centerX, centerY, width, height, 10 );
- }
- }) )[ name ];
-};
-
-module.exports = CRp;
-
-},{}],72:[function(_dereq_,module,exports){
-'use strict';
-
-module.exports = [
- { name: 'null', impl: _dereq_('./null') },
- { name: 'base', impl: _dereq_('./base') },
- { name: 'canvas', impl: _dereq_('./canvas') }
-];
-
-},{"./base":58,"./canvas":70,"./null":73}],73:[function(_dereq_,module,exports){
-'use strict';
-
-function NullRenderer(options){
- this.options = options;
- this.notifications = 0; // for testing
-}
-
-var noop = function(){};
-
-NullRenderer.prototype = {
- recalculateRenderedStyle: noop,
- notify: function(){ this.notifications++; },
- init: noop
-};
-
-module.exports = NullRenderer;
-
-},{}],74:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('./is');
-var util = _dereq_('./util');
-var Thread = _dereq_('./thread');
-var Promise = _dereq_('./promise');
-var define = _dereq_('./define');
-
-var Fabric = function( N ){
- if( !(this instanceof Fabric) ){
- return new Fabric( N );
- }
-
- this._private = {
- pass: []
- };
-
- var defN = 4;
-
- if( is.number(N) ){
- // then use the specified number of threads
- } if( typeof navigator !== 'undefined' && navigator.hardwareConcurrency != null ){
- N = navigator.hardwareConcurrency;
- } else {
- try{
- N = _dereq_('os').cpus().length;
- } catch( err ){
- N = defN;
- }
- } // TODO could use an estimation here but would the additional expense be worth it?
-
- for( var i = 0; i < N; i++ ){
- this[i] = new Thread();
- }
-
- this.length = N;
-};
-
-var fabfn = Fabric.prototype; // short alias
-
-util.extend(fabfn, {
-
- instanceString: function(){ return 'fabric'; },
-
- // require fn in all threads
- require: function( fn, as ){
- for( var i = 0; i < this.length; i++ ){
- var thread = this[i];
-
- thread.require( fn, as );
- }
-
- return this;
- },
-
- // get a random thread
- random: function(){
- var i = Math.round( (this.length - 1) * Math.random() );
- var thread = this[i];
-
- return thread;
- },
-
- // run on random thread
- run: function( fn ){
- var pass = this._private.pass.shift();
-
- return this.random().pass( pass ).run( fn );
- },
-
- // sends a random thread a message
- message: function( m ){
- return this.random().message( m );
- },
-
- // send all threads a message
- broadcast: function( m ){
- for( var i = 0; i < this.length; i++ ){
- var thread = this[i];
-
- thread.message( m );
- }
-
- return this; // chaining
- },
-
- // stop all threads
- stop: function(){
- for( var i = 0; i < this.length; i++ ){
- var thread = this[i];
-
- thread.stop();
- }
-
- return this; // chaining
- },
-
- // pass data to be used with .spread() etc.
- pass: function( data ){
- var pass = this._private.pass;
-
- if( is.array(data) ){
- pass.push( data );
- } else {
- throw 'Only arrays may be used with fabric.pass()';
- }
-
- return this; // chaining
- },
-
- spreadSize: function(){
- var subsize = Math.ceil( this._private.pass[0].length / this.length );
-
- subsize = Math.max( 1, subsize ); // don't pass less than one ele to each thread
-
- return subsize;
- },
-
- // split the data into slices to spread the data equally among threads
- spread: function( fn ){
- var self = this;
- var _p = self._private;
- var subsize = self.spreadSize(); // number of pass eles to handle in each thread
- var pass = _p.pass.shift().concat([]); // keep a copy
- var runPs = [];
-
- for( var i = 0; i < this.length; i++ ){
- var thread = this[i];
- var slice = pass.splice( 0, subsize );
-
- var runP = thread.pass( slice ).run( fn );
-
- runPs.push( runP );
-
- var doneEarly = pass.length === 0;
- if( doneEarly ){ break; }
- }
-
- return Promise.all( runPs ).then(function( thens ){
- var postpass = [];
- var p = 0;
-
- // fill postpass with the total result joined from all threads
- for( var i = 0; i < thens.length; i++ ){
- var then = thens[i]; // array result from thread i
-
- for( var j = 0; j < then.length; j++ ){
- var t = then[j]; // array element
-
- postpass[ p++ ] = t;
- }
- }
-
- return postpass;
- });
- },
-
- // parallel version of array.map()
- map: function( fn ){
- var self = this;
-
- self.require( fn, '_$_$_fabmap' );
-
- return self.spread(function( split ){
- var mapped = [];
- var origResolve = resolve; // jshint ignore:line
-
- resolve = function( val ){ // jshint ignore:line
- mapped.push( val );
- };
-
- for( var i = 0; i < split.length; i++ ){
- var oldLen = mapped.length;
- var ret = _$_$_fabmap( split[i] ); // jshint ignore:line
- var nothingInsdByResolve = oldLen === mapped.length;
-
- if( nothingInsdByResolve ){
- mapped.push( ret );
- }
- }
-
- resolve = origResolve; // jshint ignore:line
-
- return mapped;
- });
-
- },
-
- // parallel version of array.filter()
- filter: function( fn ){
- var _p = this._private;
- var pass = _p.pass[0];
-
- return this.map( fn ).then(function( include ){
- var ret = [];
-
- for( var i = 0; i < pass.length; i++ ){
- var datum = pass[i];
- var incDatum = include[i];
-
- if( incDatum ){
- ret.push( datum );
- }
- }
-
- return ret;
- });
- },
-
- // sorts the passed array using a divide and conquer strategy
- sort: function( cmp ){
- var self = this;
- var P = this._private.pass[0].length;
- var subsize = this.spreadSize();
-
- cmp = cmp || function( a, b ){ // default comparison function
- if( a < b ){
- return -1;
- } else if( a > b ){
- return 1;
- }
-
- return 0;
- };
-
- self.require( cmp, '_$_$_cmp' );
-
- return self.spread(function( split ){ // sort each split normally
- var sortedSplit = split.sort( _$_$_cmp ); // jshint ignore:line
- resolve( sortedSplit ); // jshint ignore:line
-
- }).then(function( joined ){
- // do all the merging in the main thread to minimise data transfer
-
- // TODO could do merging in separate threads but would incur add'l cost of data transfer
- // for each level of the merge
-
- var merge = function( i, j, max ){
- // don't overflow array
- j = Math.min( j, P );
- max = Math.min( max, P );
-
- // left and right sides of merge
- var l = i;
- var r = j;
-
- var sorted = [];
-
- for( var k = l; k < max; k++ ){
-
- var eleI = joined[i];
- var eleJ = joined[j];
-
- if( i < r && ( j >= max || cmp(eleI, eleJ) <= 0 ) ){
- sorted.push( eleI );
- i++;
- } else {
- sorted.push( eleJ );
- j++;
- }
-
- }
-
- // in the array proper, put the sorted values
- for( var k = 0; k < sorted.length; k++ ){ // kth sorted item
- var index = l + k;
-
- joined[ index ] = sorted[k];
- }
- };
-
- for( var splitL = subsize; splitL < P; splitL *= 2 ){ // merge until array is "split" as 1
-
- for( var i = 0; i < P; i += 2*splitL ){
- merge( i, i + splitL, i + 2*splitL );
- }
-
- }
-
- return joined;
- });
- }
-
-
-});
-
-var defineRandomPasser = function( opts ){
- opts = opts || {};
-
- return function( fn, arg1 ){
- var pass = this._private.pass.shift();
-
- return this.random().pass( pass )[ opts.threadFn ]( fn, arg1 );
- };
-};
-
-util.extend(fabfn, {
- randomMap: defineRandomPasser({ threadFn: 'map' }),
-
- reduce: defineRandomPasser({ threadFn: 'reduce' }),
-
- reduceRight: defineRandomPasser({ threadFn: 'reduceRight' })
-});
-
-// aliases
-var fn = fabfn;
-fn.promise = fn.run;
-fn.terminate = fn.halt = fn.stop;
-fn.include = fn.require;
-
-// pull in event apis
-util.extend(fabfn, {
- on: define.on(),
- one: define.on({ unbindSelfOnTrigger: true }),
- off: define.off(),
- trigger: define.trigger()
-});
-
-define.eventAliasesOn( fabfn );
-
-module.exports = Fabric;
-
-},{"./define":41,"./is":77,"./promise":80,"./thread":92,"./util":94,"os":undefined}],75:[function(_dereq_,module,exports){
-'use strict';
-/* jshint ignore:start */
-
-// Generated by CoffeeScript 1.8.0
-(function() {
- var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
-
- floor = Math.floor, min = Math.min;
-
-
- /*
- Default comparison function to be used
- */
-
- defaultCmp = function(x, y) {
- if (x < y) {
- return -1;
- }
- if (x > y) {
- return 1;
- }
- return 0;
- };
-
-
- /*
- Insert item x in list a, and keep it sorted assuming a is sorted.
-
- If x is already in a, insert it to the right of the rightmost x.
-
- Optional args lo (default 0) and hi (default a.length) bound the slice
- of a to be searched.
- */
-
- insort = function(a, x, lo, hi, cmp) {
- var mid;
- if (lo == null) {
- lo = 0;
- }
- if (cmp == null) {
- cmp = defaultCmp;
- }
- if (lo < 0) {
- throw new Error('lo must be non-negative');
- }
- if (hi == null) {
- hi = a.length;
- }
- while (lo < hi) {
- mid = floor((lo + hi) / 2);
- if (cmp(x, a[mid]) < 0) {
- hi = mid;
- } else {
- lo = mid + 1;
- }
- }
- return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
- };
-
-
- /*
- Push item onto heap, maintaining the heap invariant.
- */
-
- heappush = function(array, item, cmp) {
- if (cmp == null) {
- cmp = defaultCmp;
- }
- array.push(item);
- return _siftdown(array, 0, array.length - 1, cmp);
- };
-
-
- /*
- Pop the smallest item off the heap, maintaining the heap invariant.
- */
-
- heappop = function(array, cmp) {
- var lastelt, returnitem;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- lastelt = array.pop();
- if (array.length) {
- returnitem = array[0];
- array[0] = lastelt;
- _siftup(array, 0, cmp);
- } else {
- returnitem = lastelt;
- }
- return returnitem;
- };
-
-
- /*
- Pop and return the current smallest value, and add the new item.
-
- This is more efficient than heappop() followed by heappush(), and can be
- more appropriate when using a fixed size heap. Note that the value
- returned may be larger than item! That constrains reasonable use of
- this routine unless written as part of a conditional replacement:
- if item > array[0]
- item = heapreplace(array, item)
- */
-
- heapreplace = function(array, item, cmp) {
- var returnitem;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- returnitem = array[0];
- array[0] = item;
- _siftup(array, 0, cmp);
- return returnitem;
- };
-
-
- /*
- Fast version of a heappush followed by a heappop.
- */
-
- heappushpop = function(array, item, cmp) {
- var _ref;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- if (array.length && cmp(array[0], item) < 0) {
- _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
- _siftup(array, 0, cmp);
- }
- return item;
- };
-
-
- /*
- Transform list into a heap, in-place, in O(array.length) time.
- */
-
- heapify = function(array, cmp) {
- var i, _i, _j, _len, _ref, _ref1, _results, _results1;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- _ref1 = (function() {
- _results1 = [];
- for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
- return _results1;
- }).apply(this).reverse();
- _results = [];
- for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
- i = _ref1[_i];
- _results.push(_siftup(array, i, cmp));
- }
- return _results;
- };
-
-
- /*
- Update the position of the given item in the heap.
- This function should be called every time the item is being modified.
- */
-
- updateItem = function(array, item, cmp) {
- var pos;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- pos = array.indexOf(item);
- if (pos === -1) {
- return;
- }
- _siftdown(array, 0, pos, cmp);
- return _siftup(array, pos, cmp);
- };
-
-
- /*
- Find the n largest elements in a dataset.
- */
-
- nlargest = function(array, n, cmp) {
- var elem, result, _i, _len, _ref;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- result = array.slice(0, n);
- if (!result.length) {
- return result;
- }
- heapify(result, cmp);
- _ref = array.slice(n);
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- elem = _ref[_i];
- heappushpop(result, elem, cmp);
- }
- return result.sort(cmp).reverse();
- };
-
-
- /*
- Find the n smallest elements in a dataset.
- */
-
- nsmallest = function(array, n, cmp) {
- var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- if (n * 10 <= array.length) {
- result = array.slice(0, n).sort(cmp);
- if (!result.length) {
- return result;
- }
- los = result[result.length - 1];
- _ref = array.slice(n);
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- elem = _ref[_i];
- if (cmp(elem, los) < 0) {
- insort(result, elem, 0, null, cmp);
- result.pop();
- los = result[result.length - 1];
- }
- }
- return result;
- }
- heapify(array, cmp);
- _results = [];
- for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
- _results.push(heappop(array, cmp));
- }
- return _results;
- };
-
- _siftdown = function(array, startpos, pos, cmp) {
- var newitem, parent, parentpos;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- newitem = array[pos];
- while (pos > startpos) {
- parentpos = (pos - 1) >> 1;
- parent = array[parentpos];
- if (cmp(newitem, parent) < 0) {
- array[pos] = parent;
- pos = parentpos;
- continue;
- }
- break;
- }
- return array[pos] = newitem;
- };
-
- _siftup = function(array, pos, cmp) {
- var childpos, endpos, newitem, rightpos, startpos;
- if (cmp == null) {
- cmp = defaultCmp;
- }
- endpos = array.length;
- startpos = pos;
- newitem = array[pos];
- childpos = 2 * pos + 1;
- while (childpos < endpos) {
- rightpos = childpos + 1;
- if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
- childpos = rightpos;
- }
- array[pos] = array[childpos];
- pos = childpos;
- childpos = 2 * pos + 1;
- }
- array[pos] = newitem;
- return _siftdown(array, startpos, pos, cmp);
- };
-
- Heap = (function() {
- Heap.push = heappush;
-
- Heap.pop = heappop;
-
- Heap.replace = heapreplace;
-
- Heap.pushpop = heappushpop;
-
- Heap.heapify = heapify;
-
- Heap.updateItem = updateItem;
-
- Heap.nlargest = nlargest;
-
- Heap.nsmallest = nsmallest;
-
- function Heap(cmp) {
- this.cmp = cmp != null ? cmp : defaultCmp;
- this.nodes = [];
- }
-
- Heap.prototype.push = function(x) {
- return heappush(this.nodes, x, this.cmp);
- };
-
- Heap.prototype.pop = function() {
- return heappop(this.nodes, this.cmp);
- };
-
- Heap.prototype.peek = function() {
- return this.nodes[0];
- };
-
- Heap.prototype.contains = function(x) {
- return this.nodes.indexOf(x) !== -1;
- };
-
- Heap.prototype.replace = function(x) {
- return heapreplace(this.nodes, x, this.cmp);
- };
-
- Heap.prototype.pushpop = function(x) {
- return heappushpop(this.nodes, x, this.cmp);
- };
-
- Heap.prototype.heapify = function() {
- return heapify(this.nodes, this.cmp);
- };
-
- Heap.prototype.updateItem = function(x) {
- return updateItem(this.nodes, x, this.cmp);
- };
-
- Heap.prototype.clear = function() {
- return this.nodes = [];
- };
-
- Heap.prototype.empty = function() {
- return this.nodes.length === 0;
- };
-
- Heap.prototype.size = function() {
- return this.nodes.length;
- };
-
- Heap.prototype.clone = function() {
- var heap;
- heap = new Heap();
- heap.nodes = this.nodes.slice(0);
- return heap;
- };
-
- Heap.prototype.toArray = function() {
- return this.nodes.slice(0);
- };
-
- Heap.prototype.insert = Heap.prototype.push;
-
- Heap.prototype.top = Heap.prototype.peek;
-
- Heap.prototype.front = Heap.prototype.peek;
-
- Heap.prototype.has = Heap.prototype.contains;
-
- Heap.prototype.copy = Heap.prototype.clone;
-
- return Heap;
-
- })();
-
- (function(root, factory) {
- if (typeof define === 'function' && define.amd) {
- return define([], factory);
- } else if (typeof exports === 'object') {
- return module.exports = factory();
- } else {
- return root.Heap = factory();
- }
- })(this, function() {
- return Heap;
- });
-
-}).call(this);
-
-/* jshint ignore:end */
-
-},{}],76:[function(_dereq_,module,exports){
-'use strict';
-
-var window = _dereq_('./window');
-var is = _dereq_('./is');
-var Core = _dereq_('./core');
-var extension = _dereq_('./extension');
-var registerJquery = _dereq_('./jquery-plugin');
-var Stylesheet = _dereq_('./stylesheet');
-var Thread = _dereq_('./thread');
-var Fabric = _dereq_('./fabric');
-
-var cytoscape = function( options ){ // jshint ignore:line
- // if no options specified, use default
- if( options === undefined ){
- options = {};
- }
-
- // create instance
- if( is.plainObject( options ) ){
- return new Core( options );
- }
-
- // allow for registration of extensions
- else if( is.string( options ) ) {
- return extension.apply(extension, arguments);
- }
-};
-
-// replaced by build system
-cytoscape.version = '2.5.3';
-
-// try to register w/ jquery
-if( window && window.jQuery ){
- registerJquery( window.jQuery, cytoscape );
-}
-
-// expose register api
-cytoscape.registerJquery = function( jQuery ){
- registerJquery( jQuery, cytoscape );
-};
-
-// expose public apis (mostly for extensions)
-cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
-cytoscape.thread = cytoscape.Thread = Thread;
-cytoscape.fabric = cytoscape.Fabric = Fabric;
-
-module.exports = cytoscape;
-
-},{"./core":34,"./extension":43,"./fabric":74,"./is":77,"./jquery-plugin":78,"./stylesheet":91,"./thread":92,"./window":100}],77:[function(_dereq_,module,exports){
-'use strict';
-
-var window = _dereq_('./window');
-var navigator = window ? window.navigator : null;
-
-var typeofstr = typeof '';
-var typeofobj = typeof {};
-var typeoffn = typeof function(){};
-var typeofhtmlele = typeof HTMLElement;
-
-var instanceStr = function( obj ){
- return obj && obj.instanceString && is.fn( obj.instanceString ) ? obj.instanceString() : null;
-};
-
-var is = {
- defined: function(obj){
- return obj != null; // not undefined or null
- },
-
- string: function(obj){
- return obj != null && typeof obj == typeofstr;
- },
-
- fn: function(obj){
- return obj != null && typeof obj === typeoffn;
- },
-
- array: function(obj){
- return Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array;
- },
-
- plainObject: function(obj){
- return obj != null && typeof obj === typeofobj && !is.array(obj) && obj.constructor === Object;
- },
-
- object: function(obj){
- return obj != null && typeof obj === typeofobj;
- },
-
- number: function(obj){
- return obj != null && typeof obj === typeof 1 && !isNaN(obj);
- },
-
- integer: function( obj ){
- return is.number(obj) && Math.floor(obj) === obj;
- },
-
- bool: function(obj){
- return obj != null && typeof obj === typeof true;
- },
-
- htmlElement: function(obj){
- if( 'undefined' === typeofhtmlele ){
- return undefined;
- } else {
- return null != obj && obj instanceof HTMLElement;
- }
- },
-
- elementOrCollection: function(obj){
- return is.element(obj) || is.collection(obj);
- },
-
- element: function(obj){
- return instanceStr(obj) === 'collection' && obj._private.single;
- },
-
- collection: function(obj){
- return instanceStr(obj) === 'collection' && !obj._private.single;
- },
-
- core: function(obj){
- return instanceStr(obj) === 'core';
- },
-
- style: function(obj){
- return instanceStr(obj) === 'style';
- },
-
- stylesheet: function(obj){
- return instanceStr(obj) === 'stylesheet';
- },
-
- event: function(obj){
- return instanceStr(obj) === 'event';
- },
-
- thread: function(obj){
- return instanceStr(obj) === 'thread';
- },
-
- fabric: function(obj){
- return instanceStr(obj) === 'fabric';
- },
-
- emptyString: function(obj){
- if( !obj ){ // null is empty
- return true;
- } else if( is.string(obj) ){
- if( obj === '' || obj.match(/^\s+$/) ){
- return true; // empty string is empty
- }
- }
-
- return false; // otherwise, we don't know what we've got
- },
-
- nonemptyString: function(obj){
- if( obj && is.string(obj) && obj !== '' && !obj.match(/^\s+$/) ){
- return true;
- }
-
- return false;
- },
-
- domElement: function(obj){
- if( typeof HTMLElement === 'undefined' ){
- return false; // we're not in a browser so it doesn't matter
- } else {
- return obj instanceof HTMLElement;
- }
- },
-
- boundingBox: function(obj){
- return is.plainObject(obj) &&
- is.number(obj.x1) && is.number(obj.x2) &&
- is.number(obj.y1) && is.number(obj.y2)
- ;
- },
-
- promise: function(obj){
- return is.object(obj) && is.fn(obj.then);
- },
-
- touch: function(){
- return window && ( ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch );
- },
-
- gecko: function(){
- return typeof InstallTrigger !== 'undefined' || ('MozAppearance' in document.documentElement.style);
- },
-
- webkit: function(){
- return typeof webkitURL !== 'undefined' || ('WebkitAppearance' in document.documentElement.style);
- },
-
- chromium: function(){
- return typeof chrome !== 'undefined';
- },
-
- khtml: function(){
- return navigator && navigator.vendor.match(/kde/i); // probably a better way to detect this...
- },
-
- khtmlEtc: function(){
- return is.khtml() || is.webkit() || is.chromium();
- },
-
- ms: function(){
- return navigator && navigator.userAgent.match(/msie|trident|edge/i); // probably a better way to detect this...
- },
-
- windows: function(){
- return navigator && navigator.appVersion.match(/Win/i);
- },
-
- mac: function(){
- return navigator && navigator.appVersion.match(/Mac/i);
- },
-
- linux: function(){
- return navigator && navigator.appVersion.match(/Linux/i);
- },
-
- unix: function(){
- return navigator && navigator.appVersion.match(/X11/i);
- }
-};
-
-module.exports = is;
-
-},{"./window":100}],78:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('./is');
-
-var cyReg = function( $ele ){
- var d = $ele[0]._cyreg = $ele[0]._cyreg || {};
-
- return d;
-};
-
-var registerJquery = function( $, cytoscape ){
- if( !$ ){ return; } // no jquery => don't need this
-
- if( $.fn.cytoscape ){ return; } // already registered
-
- // allow calls on a jQuery selector by proxying calls to $.cytoscape
- // e.g. $("#foo").cytoscape(options) => $.cytoscape(options) on #foo
- $.fn.cytoscape = function(opts){
- var $this = $(this);
-
- // get object
- if( opts === 'get' ){
- return cyReg( $this ).cy;
- }
-
- // bind to ready
- else if( is.fn(opts) ){
-
- var ready = opts;
- var cy = cyReg( $this ).cy;
-
- if( cy && cy.isReady() ){ // already ready so just trigger now
- cy.trigger('ready', [], ready);
-
- } else { // not yet ready, so add to readies list
- var data = cyReg( $this );
- var readies = data.readies = data.readies || [];
-
- readies.push( ready );
- }
-
- }
-
- // proxy to create instance
- else if( is.plainObject(opts) ){
- return $this.each(function(){
- var options = $.extend({}, opts, {
- container: $(this)[0]
- });
-
- cytoscape(options);
- });
- }
- };
-
- // allow access to the global cytoscape object under jquery for legacy reasons
- $.cytoscape = cytoscape;
-
- // use short alias (cy) if not already defined
- if( $.fn.cy == null && $.cy == null ){
- $.fn.cy = $.fn.cytoscape;
- $.cy = $.cytoscape;
- }
-};
-
-module.exports = registerJquery;
-
-},{"./is":77}],79:[function(_dereq_,module,exports){
-'use strict';
-
-var math = {};
-
-math.signum = function(x){
- if( x > 0 ){
- return 1;
- } else if( x < 0 ){
- return -1;
- } else {
- return 0;
- }
-};
-
-math.distance = function( p1, p2 ){
- return Math.sqrt( math.sqDistance(p1, p2) );
-};
-
-math.sqDistance = function( p1, p2 ){
- var dx = p2.x - p1.x;
- var dy = p2.y - p1.y;
-
- return dx*dx + dy*dy;
-};
-
-// from http://en.wikipedia.org/wiki/Bézier_curve#Quadratic_curves
-math.qbezierAt = function(p0, p1, p2, t){
- return (1 - t)*(1 - t)*p0 + 2*(1 - t)*t*p1 + t*t*p2;
-};
-
-math.qbezierPtAt = function(p0, p1, p2, t){
- return {
- x: math.qbezierAt( p0.x, p1.x, p2.x, t ),
- y: math.qbezierAt( p0.y, p1.y, p2.y, t )
- };
-};
-
-// makes a full bb (x1, y1, x2, y2, w, h) from implicit params
-math.makeBoundingBox = function( bb ){
- if( bb.x1 != null && bb.y1 != null ){
- if( bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1 ){
- return {
- x1: bb.x1,
- y1: bb.y1,
- x2: bb.x2,
- y2: bb.y2,
- w: bb.x2 - bb.x1,
- h: bb.y2 - bb.y1
- };
- } else if( bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0 ){
- return {
- x1: bb.x1,
- y1: bb.y1,
- x2: bb.x1 + bb.w,
- y2: bb.y1 + bb.h,
- w: bb.w,
- h: bb.h
- };
- }
- }
-};
-
-math.boundingBoxesIntersect = function( bb1, bb2 ){
- // case: one bb to right of other
- if( bb1.x1 > bb2.x2 ){ return false; }
- if( bb2.x1 > bb1.x2 ){ return false; }
-
- // case: one bb to left of other
- if( bb1.x2 < bb2.x1 ){ return false; }
- if( bb2.x2 < bb1.x1 ){ return false; }
-
- // case: one bb above other
- if( bb1.y2 < bb2.y1 ){ return false; }
- if( bb2.y2 < bb1.y1 ){ return false; }
-
- // case: one bb below other
- if( bb1.y1 > bb2.y2 ){ return false; }
- if( bb2.y1 > bb1.y2 ){ return false; }
-
- // otherwise, must have some overlap
- return true;
-};
-
-math.inBoundingBox = function( bb, x, y ){
- return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
-};
-
-math.pointInBoundingBox = function( bb, pt ){
- return this.inBoundingBox( bb, pt.x, pt.y );
-};
-
-math.roundRectangleIntersectLine = function(
- x, y, nodeX, nodeY, width, height, padding) {
-
- var cornerRadius = this.getRoundRectangleRadius(width, height);
-
- var halfWidth = width / 2;
- var halfHeight = height / 2;
-
- // Check intersections with straight line segments
- var straightLineIntersections;
-
- // Top segment, left to right
- {
- var topStartX = nodeX - halfWidth + cornerRadius - padding;
- var topStartY = nodeY - halfHeight - padding;
- var topEndX = nodeX + halfWidth - cornerRadius + padding;
- var topEndY = topStartY;
-
- straightLineIntersections = this.finiteLinesIntersect(
- x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
-
- if (straightLineIntersections.length > 0) {
- return straightLineIntersections;
- }
- }
-
- // Right segment, top to bottom
- {
- var rightStartX = nodeX + halfWidth + padding;
- var rightStartY = nodeY - halfHeight + cornerRadius - padding;
- var rightEndX = rightStartX;
- var rightEndY = nodeY + halfHeight - cornerRadius + padding;
-
- straightLineIntersections = this.finiteLinesIntersect(
- x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
-
- if (straightLineIntersections.length > 0) {
- return straightLineIntersections;
- }
- }
-
- // Bottom segment, left to right
- {
- var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
- var bottomStartY = nodeY + halfHeight + padding;
- var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
- var bottomEndY = bottomStartY;
-
- straightLineIntersections = this.finiteLinesIntersect(
- x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
-
- if (straightLineIntersections.length > 0) {
- return straightLineIntersections;
- }
- }
-
- // Left segment, top to bottom
- {
- var leftStartX = nodeX - halfWidth - padding;
- var leftStartY = nodeY - halfHeight + cornerRadius - padding;
- var leftEndX = leftStartX;
- var leftEndY = nodeY + halfHeight - cornerRadius + padding;
-
- straightLineIntersections = this.finiteLinesIntersect(
- x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
-
- if (straightLineIntersections.length > 0) {
- return straightLineIntersections;
- }
- }
-
- // Check intersections with arc segments
- var arcIntersections;
-
- // Top Left
- {
- var topLeftCenterX = nodeX - halfWidth + cornerRadius;
- var topLeftCenterY = nodeY - halfHeight + cornerRadius;
- arcIntersections = this.intersectLineCircle(
- x, y, nodeX, nodeY,
- topLeftCenterX, topLeftCenterY, cornerRadius + padding);
-
- // Ensure the intersection is on the desired quarter of the circle
- if (arcIntersections.length > 0
- && arcIntersections[0] <= topLeftCenterX
- && arcIntersections[1] <= topLeftCenterY) {
- return [arcIntersections[0], arcIntersections[1]];
- }
- }
-
- // Top Right
- {
- var topRightCenterX = nodeX + halfWidth - cornerRadius;
- var topRightCenterY = nodeY - halfHeight + cornerRadius;
- arcIntersections = this.intersectLineCircle(
- x, y, nodeX, nodeY,
- topRightCenterX, topRightCenterY, cornerRadius + padding);
-
- // Ensure the intersection is on the desired quarter of the circle
- if (arcIntersections.length > 0
- && arcIntersections[0] >= topRightCenterX
- && arcIntersections[1] <= topRightCenterY) {
- return [arcIntersections[0], arcIntersections[1]];
- }
- }
-
- // Bottom Right
- {
- var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
- var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
- arcIntersections = this.intersectLineCircle(
- x, y, nodeX, nodeY,
- bottomRightCenterX, bottomRightCenterY, cornerRadius + padding);
-
- // Ensure the intersection is on the desired quarter of the circle
- if (arcIntersections.length > 0
- && arcIntersections[0] >= bottomRightCenterX
- && arcIntersections[1] >= bottomRightCenterY) {
- return [arcIntersections[0], arcIntersections[1]];
- }
- }
-
- // Bottom Left
- {
- var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
- var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
- arcIntersections = this.intersectLineCircle(
- x, y, nodeX, nodeY,
- bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding);
-
- // Ensure the intersection is on the desired quarter of the circle
- if (arcIntersections.length > 0
- && arcIntersections[0] <= bottomLeftCenterX
- && arcIntersections[1] >= bottomLeftCenterY) {
- return [arcIntersections[0], arcIntersections[1]];
- }
- }
-
- return []; // if nothing
-};
-
-math.inLineVicinity = function(x, y, lx1, ly1, lx2, ly2, tolerance){
- var t = tolerance;
-
- var x1 = Math.min(lx1, lx2);
- var x2 = Math.max(lx1, lx2);
- var y1 = Math.min(ly1, ly2);
- var y2 = Math.max(ly1, ly2);
-
- return x1 - t <= x && x <= x2 + t
- && y1 - t <= y && y <= y2 + t;
-};
-
-math.inBezierVicinity = function(
- x, y, x1, y1, x2, y2, x3, y3, tolerance) {
-
- var bb = {
- x1: Math.min( x1, x3, x2 ) - tolerance,
- x2: Math.max( x1, x3, x2 ) + tolerance,
- y1: Math.min( y1, y3, y2 ) - tolerance,
- y2: Math.max( y1, y3, y2 ) + tolerance
- };
-
- // if outside the rough bounding box for the bezier, then it can't be a hit
- if( x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2 ){
- // console.log('bezier out of rough bb')
- return false;
- } else {
- // console.log('do more expensive check');
- return true;
- }
-
-};
-
-math.solveCubic = function(a, b, c, d, result) {
-
- // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
- // r is the real component, i is the imaginary component
-
- // An implementation of the Cardano method from the year 1545
- // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
-
- b /= a;
- c /= a;
- d /= a;
-
- var discriminant, q, r, dum1, s, t, term1, r13;
-
- q = (3.0 * c - (b * b)) / 9.0;
- r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
- r /= 54.0;
-
- discriminant = q * q * q + r * r;
- result[1] = 0;
- term1 = (b / 3.0);
-
- if (discriminant > 0) {
- s = r + Math.sqrt(discriminant);
- s = ((s < 0) ? -Math.pow(-s, (1.0 / 3.0)) : Math.pow(s, (1.0 / 3.0)));
- t = r - Math.sqrt(discriminant);
- t = ((t < 0) ? -Math.pow(-t, (1.0 / 3.0)) : Math.pow(t, (1.0 / 3.0)));
- result[0] = -term1 + s + t;
- term1 += (s + t) / 2.0;
- result[4] = result[2] = -term1;
- term1 = Math.sqrt(3.0) * (-t + s) / 2;
- result[3] = term1;
- result[5] = -term1;
- return;
- }
-
- result[5] = result[3] = 0;
-
- if (discriminant === 0) {
- r13 = ((r < 0) ? -Math.pow(-r, (1.0 / 3.0)) : Math.pow(r, (1.0 / 3.0)));
- result[0] = -term1 + 2.0 * r13;
- result[4] = result[2] = -(r13 + term1);
- return;
- }
-
- q = -q;
- dum1 = q * q * q;
- dum1 = Math.acos(r / Math.sqrt(dum1));
- r13 = 2.0 * Math.sqrt(q);
- result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
- result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
- result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
-
- return;
-};
-
-math.sqDistanceToQuadraticBezier = function(
- x, y, x1, y1, x2, y2, x3, y3) {
-
- // Find minimum distance by using the minimum of the distance
- // function between the given point and the curve
-
- // This gives the coefficients of the resulting cubic equation
- // whose roots tell us where a possible minimum is
- // (Coefficients are divided by 4)
-
- var a = 1.0 * x1*x1 - 4*x1*x2 + 2*x1*x3 + 4*x2*x2 - 4*x2*x3 + x3*x3
- + y1*y1 - 4*y1*y2 + 2*y1*y3 + 4*y2*y2 - 4*y2*y3 + y3*y3;
-
- var b = 1.0 * 9*x1*x2 - 3*x1*x1 - 3*x1*x3 - 6*x2*x2 + 3*x2*x3
- + 9*y1*y2 - 3*y1*y1 - 3*y1*y3 - 6*y2*y2 + 3*y2*y3;
-
- var c = 1.0 * 3*x1*x1 - 6*x1*x2 + x1*x3 - x1*x + 2*x2*x2 + 2*x2*x - x3*x
- + 3*y1*y1 - 6*y1*y2 + y1*y3 - y1*y + 2*y2*y2 + 2*y2*y - y3*y;
-
- var d = 1.0 * x1*x2 - x1*x1 + x1*x - x2*x
- + y1*y2 - y1*y1 + y1*y - y2*y;
-
- // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a);
-
- var roots = [];
-
- // Use the cubic solving algorithm
- this.solveCubic(a, b, c, d, roots);
-
- var zeroThreshold = 0.0000001;
-
- var params = [];
-
- for (var index = 0; index < 6; index += 2) {
- if (Math.abs(roots[index + 1]) < zeroThreshold
- && roots[index] >= 0
- && roots[index] <= 1.0) {
- params.push(roots[index]);
- }
- }
-
- params.push(1.0);
- params.push(0.0);
-
- var minDistanceSquared = -1;
- var closestParam;
-
- var curX, curY, distSquared;
- for (var i = 0; i < params.length; i++) {
- curX = Math.pow(1.0 - params[i], 2.0) * x1
- + 2.0 * (1 - params[i]) * params[i] * x2
- + params[i] * params[i] * x3;
-
- curY = Math.pow(1 - params[i], 2.0) * y1
- + 2 * (1.0 - params[i]) * params[i] * y2
- + params[i] * params[i] * y3;
-
- distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2);
- // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
- if (minDistanceSquared >= 0) {
- if (distSquared < minDistanceSquared) {
- minDistanceSquared = distSquared;
- closestParam = params[i];
- }
- } else {
- minDistanceSquared = distSquared;
- closestParam = params[i];
- }
- }
-
- return minDistanceSquared;
-};
-
-math.sqDistanceToFiniteLine = function(x, y, x1, y1, x2, y2) {
- var offset = [x - x1, y - y1];
- var line = [x2 - x1, y2 - y1];
-
- var lineSq = line[0] * line[0] + line[1] * line[1];
- var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
-
- var dotProduct = offset[0] * line[0] + offset[1] * line[1];
- var adjSq = dotProduct * dotProduct / lineSq;
-
- if (dotProduct < 0) {
- return hypSq;
- }
-
- if (adjSq > lineSq) {
- return (x - x2) * (x - x2) + (y - y2) * (y - y2);
- }
-
- return hypSq - adjSq;
-};
-
-math.pointInsidePolygonPoints = function(x, y, points){
- var x1, y1, x2, y2;
- var y3;
-
- // Intersect with vertical line through (x, y)
- var up = 0;
- var down = 0;
- for (var i = 0; i < points.length / 2; i++) {
-
- x1 = points[i * 2];
- y1 = points[i * 2 + 1];
-
- if (i + 1 < points.length / 2) {
- x2 = points[(i + 1) * 2];
- y2 = points[(i + 1) * 2 + 1];
- } else {
- x2 = points[(i + 1 - points.length / 2) * 2];
- y2 = points[(i + 1 - points.length / 2) * 2 + 1];
- }
-
- if (x1 == x && x2 == x) {
-
- } else if ((x1 >= x && x >= x2)
- || (x1 <= x && x <= x2)) {
-
- y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
-
- if (y3 > y) {
- up++;
- }
-
- if (y3 < y) {
- down++;
- }
-
- } else {
- continue;
- }
-
- }
-
- if (up % 2 === 0) {
- return false;
- } else {
- return true;
- }
-};
-
-math.pointInsidePolygon = function(
- x, y, basePoints, centerX, centerY, width, height, direction, padding) {
-
- //var direction = arguments[6];
- var transformedPoints = new Array(basePoints.length);
-
- // Gives negative angle
- var angle;
-
- if( direction[0] != null ){
- angle = Math.atan(direction[1] / direction[0]);
-
- if (direction[0] < 0) {
- angle = angle + Math.PI / 2;
- } else {
- angle = -angle - Math.PI / 2;
- }
- } else {
- angle = direction;
- }
-
- var cos = Math.cos(-angle);
- var sin = Math.sin(-angle);
-
- // console.log("base: " + basePoints);
- for (var i = 0; i < transformedPoints.length / 2; i++) {
- transformedPoints[i * 2] =
- width / 2 * (basePoints[i * 2] * cos
- - basePoints[i * 2 + 1] * sin);
-
- transformedPoints[i * 2 + 1] =
- height / 2 * (basePoints[i * 2 + 1] * cos
- + basePoints[i * 2] * sin);
-
- transformedPoints[i * 2] += centerX;
- transformedPoints[i * 2 + 1] += centerY;
- }
-
- var points;
-
- if (padding > 0) {
- var expandedLineSet = this.expandPolygon(
- transformedPoints,
- -padding);
-
- points = this.joinLines(expandedLineSet);
- } else {
- points = transformedPoints;
- }
-
- return math.pointInsidePolygonPoints( x, y, points );
-};
-
-math.joinLines = function(lineSet) {
-
- var vertices = new Array(lineSet.length / 2);
-
- var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
- var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
-
- for (var i = 0; i < lineSet.length / 4; i++) {
- currentLineStartX = lineSet[i * 4];
- currentLineStartY = lineSet[i * 4 + 1];
- currentLineEndX = lineSet[i * 4 + 2];
- currentLineEndY = lineSet[i * 4 + 3];
-
- if (i < lineSet.length / 4 - 1) {
- nextLineStartX = lineSet[(i + 1) * 4];
- nextLineStartY = lineSet[(i + 1) * 4 + 1];
- nextLineEndX = lineSet[(i + 1) * 4 + 2];
- nextLineEndY = lineSet[(i + 1) * 4 + 3];
- } else {
- nextLineStartX = lineSet[0];
- nextLineStartY = lineSet[1];
- nextLineEndX = lineSet[2];
- nextLineEndY = lineSet[3];
- }
-
- var intersection = this.finiteLinesIntersect(
- currentLineStartX, currentLineStartY,
- currentLineEndX, currentLineEndY,
- nextLineStartX, nextLineStartY,
- nextLineEndX, nextLineEndY,
- true);
-
- vertices[i * 2] = intersection[0];
- vertices[i * 2 + 1] = intersection[1];
- }
-
- return vertices;
-};
-
-math.expandPolygon = function(points, pad) {
-
- var expandedLineSet = new Array(points.length * 2);
-
- var currentPointX, currentPointY, nextPointX, nextPointY;
-
- for (var i = 0; i < points.length / 2; i++) {
- currentPointX = points[i * 2];
- currentPointY = points[i * 2 + 1];
-
- if (i < points.length / 2 - 1) {
- nextPointX = points[(i + 1) * 2];
- nextPointY = points[(i + 1) * 2 + 1];
- } else {
- nextPointX = points[0];
- nextPointY = points[1];
- }
-
- // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
-
- // Assume CCW polygon winding
-
- var offsetX = (nextPointY - currentPointY);
- var offsetY = -(nextPointX - currentPointX);
-
- // Normalize
- var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
- var normalizedOffsetX = offsetX / offsetLength;
- var normalizedOffsetY = offsetY / offsetLength;
-
- expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
- expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
- expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
- expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
- }
-
- return expandedLineSet;
-};
-
-math.intersectLineEllipse = function(
- x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
-
- var dispX = centerX - x;
- var dispY = centerY - y;
-
- dispX /= ellipseWradius;
- dispY /= ellipseHradius;
-
- var len = Math.sqrt(dispX * dispX + dispY * dispY);
-
- var newLength = len - 1;
-
- if (newLength < 0) {
- return [];
- }
-
- var lenProportion = newLength / len;
-
- return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
-};
-
-// Returns intersections of increasing distance from line's start point
-math.intersectLineCircle = function(
- x1, y1, x2, y2, centerX, centerY, radius) {
-
- // Calculate d, direction vector of line
- var d = [x2 - x1, y2 - y1]; // Direction vector of line
- var c = [centerX, centerY]; // Center of circle
- var f = [x1 - centerX, y1 - centerY];
-
- var a = d[0] * d[0] + d[1] * d[1];
- var b = 2 * (f[0] * d[0] + f[1] * d[1]);
- var c = (f[0] * f[0] + f[1] * f[1]) - radius * radius ;
-
- var discriminant = b*b-4*a*c;
-
- if (discriminant < 0) {
- return [];
- }
-
- var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
- var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
-
- var tMin = Math.min(t1, t2);
- var tMax = Math.max(t1, t2);
- var inRangeParams = [];
-
- if (tMin >= 0 && tMin <= 1) {
- inRangeParams.push(tMin);
- }
-
- if (tMax >= 0 && tMax <= 1) {
- inRangeParams.push(tMax);
- }
-
- if (inRangeParams.length === 0) {
- return [];
- }
-
- var nearIntersectionX = inRangeParams[0] * d[0] + x1;
- var nearIntersectionY = inRangeParams[0] * d[1] + y1;
-
- if (inRangeParams.length > 1) {
-
- if (inRangeParams[0] == inRangeParams[1]) {
- return [nearIntersectionX, nearIntersectionY];
- } else {
-
- var farIntersectionX = inRangeParams[1] * d[0] + x1;
- var farIntersectionY = inRangeParams[1] * d[1] + y1;
-
- return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
- }
-
- } else {
- return [nearIntersectionX, nearIntersectionY];
- }
-
-};
-
-math.findCircleNearPoint = function(centerX, centerY,
- radius, farX, farY) {
-
- var displacementX = farX - centerX;
- var displacementY = farY - centerY;
- var distance = Math.sqrt(displacementX * displacementX
- + displacementY * displacementY);
-
- var unitDisplacementX = displacementX / distance;
- var unitDisplacementY = displacementY / distance;
-
- return [centerX + unitDisplacementX * radius,
- centerY + unitDisplacementY * radius];
-};
-
-math.findMaxSqDistanceToOrigin = function(points) {
- var maxSqDistance = 0.000001;
- var sqDistance;
-
- for (var i = 0; i < points.length / 2; i++) {
-
- sqDistance = points[i * 2] * points[i * 2]
- + points[i * 2 + 1] * points[i * 2 + 1];
-
- if (sqDistance > maxSqDistance) {
- maxSqDistance = sqDistance;
- }
- }
-
- return maxSqDistance;
-};
-
-math.finiteLinesIntersect = function(
- x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
-
- var ua_t = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
- var ub_t = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);
- var u_b = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
-
- if (u_b !== 0) {
- var ua = ua_t / u_b;
- var ub = ub_t / u_b;
-
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
- return [x1 + ua * (x2 - x1), y1 + ua * (y2 - y1)];
-
- } else {
- if (!infiniteLines) {
- return [];
- } else {
- return [x1 + ua * (x2 - x1), y1 + ua * (y2 - y1)];
- }
- }
- } else {
- if (ua_t === 0 || ub_t === 0) {
-
- // Parallel, coincident lines. Check if overlap
-
- // Check endpoint of second line
- if ([x1, x2, x4].sort()[1] === x4) {
- return [x4, y4];
- }
-
- // Check start point of second line
- if ([x1, x2, x3].sort()[1] === x3) {
- return [x3, y3];
- }
-
- // Endpoint of first line
- if ([x3, x4, x2].sort()[1] === x2) {
- return [x2, y2];
- }
-
- return [];
- } else {
-
- // Parallel, non-coincident
- return [];
- }
- }
-};
-
-math.polygonIntersectLine = function(
- x, y, basePoints, centerX, centerY, width, height, padding) {
-
- var intersections = [];
- var intersection;
-
- var transformedPoints = new Array(basePoints.length);
-
- for (var i = 0; i < transformedPoints.length / 2; i++) {
- transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
- transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
- }
-
- var points;
-
- if (padding > 0) {
- var expandedLineSet = math.expandPolygon(
- transformedPoints,
- -padding);
-
- points = math.joinLines(expandedLineSet);
- } else {
- points = transformedPoints;
- }
- // var points = transformedPoints;
-
- var currentX, currentY, nextX, nextY;
-
- for (var i = 0; i < points.length / 2; i++) {
-
- currentX = points[i * 2];
- currentY = points[i * 2 + 1];
-
- if (i < points.length / 2 - 1) {
- nextX = points[(i + 1) * 2];
- nextY = points[(i + 1) * 2 + 1];
- } else {
- nextX = points[0];
- nextY = points[1];
- }
-
- intersection = this.finiteLinesIntersect(
- x, y, centerX, centerY,
- currentX, currentY,
- nextX, nextY);
-
- if (intersection.length !== 0) {
- intersections.push(intersection[0], intersection[1]);
- }
- }
-
- return intersections;
-};
-
-math.shortenIntersection = function(
- intersection, offset, amount) {
-
- var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
-
- var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
-
- var lenRatio = (length - amount) / length;
-
- if (lenRatio < 0) {
- lenRatio = 0.00001;
- }
-
- return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
-};
-
-math.generateUnitNgonPointsFitToSquare = function(sides, rotationRadians) {
- var points = math.generateUnitNgonPoints(sides, rotationRadians);
- points = math.fitPolygonToSquare(points);
-
- return points;
-};
-
-math.fitPolygonToSquare = function(points){
- var x, y;
- var sides = points.length/2;
- var minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
-
- for (var i = 0; i < sides; i++) {
- x = points[2 * i];
- y = points[2 * i + 1];
-
- minX = Math.min( minX, x );
- maxX = Math.max( maxX, x );
- minY = Math.min( minY, y );
- maxY = Math.max( maxY, y );
- }
-
- // stretch factors
- var sx = 2 / (maxX - minX);
- var sy = 2 / (maxY - minY);
-
- for (var i = 0; i < sides; i++){
- x = points[2 * i] = points[2 * i] * sx;
- y = points[2 * i + 1] = points[2 * i + 1] * sy;
-
- minX = Math.min( minX, x );
- maxX = Math.max( maxX, x );
- minY = Math.min( minY, y );
- maxY = Math.max( maxY, y );
- }
-
- if( minY < -1 ){
- for (var i = 0; i < sides; i++){
- y = points[2 * i + 1] = points[2 * i + 1] + (-1 -minY);
- }
- }
-
- return points;
-};
-
-math.generateUnitNgonPoints = function(sides, rotationRadians) {
-
- var increment = 1.0 / sides * 2 * Math.PI;
- var startAngle = sides % 2 === 0 ?
- Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
- // console.log(nodeShapes['square']);
- startAngle += rotationRadians;
-
- var points = new Array(sides * 2);
-
- var currentAngle, x, y;
- for (var i = 0; i < sides; i++) {
- currentAngle = i * increment + startAngle;
-
- x = points[2 * i] = Math.cos(currentAngle);// * (1 + i/2);
- y = points[2 * i + 1] = Math.sin(-currentAngle);// * (1 + i/2);
- }
-
- return points;
-};
-
-math.getRoundRectangleRadius = function(width, height) {
-
- // Set the default radius, unless half of width or height is smaller than default
- return Math.min(width / 4, height / 4, 8);
-};
-
-module.exports = math;
-
-},{}],80:[function(_dereq_,module,exports){
-// internal, minimal Promise impl s.t. apis can return promises in old envs
-// based on thenable (http://github.com/rse/thenable)
-
-'use strict';
-
-/* promise states [Promises/A+ 2.1] */
-var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
-var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
-var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
-
-/* promise object constructor */
-var api = function (executor) {
- /* optionally support non-constructor/plain-function call */
- if (!(this instanceof api))
- return new api(executor);
-
- /* initialize object */
- this.id = "Thenable/1.0.7";
- this.state = STATE_PENDING; /* initial state */
- this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
- this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
- this.onFulfilled = []; /* initial handlers */
- this.onRejected = []; /* initial handlers */
-
- /* provide optional information-hiding proxy */
- this.proxy = {
- then: this.then.bind(this)
- };
-
- /* support optional executor function */
- if (typeof executor === "function")
- executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
-};
-
-/* promise API methods */
-api.prototype = {
- /* promise resolving methods */
- fulfill: function (value) { return deliver(this, STATE_FULFILLED, "fulfillValue", value); },
- reject: function (value) { return deliver(this, STATE_REJECTED, "rejectReason", value); },
-
- /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
- then: function (onFulfilled, onRejected) {
- var curr = this;
- var next = new api(); /* [Promises/A+ 2.2.7] */
- curr.onFulfilled.push(
- resolver(onFulfilled, next, "fulfill")); /* [Promises/A+ 2.2.2/2.2.6] */
- curr.onRejected.push(
- resolver(onRejected, next, "reject" )); /* [Promises/A+ 2.2.3/2.2.6] */
- execute(curr);
- return next.proxy; /* [Promises/A+ 2.2.7, 3.3] */
- }
-};
-
-/* deliver an action */
-var deliver = function (curr, state, name, value) {
- if (curr.state === STATE_PENDING) {
- curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
- curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
- execute(curr);
- }
- return curr;
-};
-
-/* execute all handlers */
-var execute = function (curr) {
- if (curr.state === STATE_FULFILLED)
- execute_handlers(curr, "onFulfilled", curr.fulfillValue);
- else if (curr.state === STATE_REJECTED)
- execute_handlers(curr, "onRejected", curr.rejectReason);
-};
-
-/* execute particular set of handlers */
-var execute_handlers = function (curr, name, value) {
- /* global setImmediate: true */
- /* global setTimeout: true */
-
- /* short-circuit processing */
- if (curr[name].length === 0)
- return;
-
- /* iterate over all handlers, exactly once */
- var handlers = curr[name];
- curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
- var func = function () {
- for (var i = 0; i < handlers.length; i++)
- handlers[i](value); /* [Promises/A+ 2.2.5] */
- };
-
- /* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
- if (typeof setImmediate === "function")
- setImmediate(func);
- else
- setTimeout(func, 0);
-};
-
-/* generate a resolver function */
-var resolver = function (cb, next, method) {
- return function (value) {
- if (typeof cb !== "function") /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
- next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
- else {
- var result;
- try { result = cb(value); } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
- catch (e) {
- next.reject(e); /* [Promises/A+ 2.2.7.2] */
- return;
- }
- resolve(next, result); /* [Promises/A+ 2.2.7.1] */
- }
- };
-};
-
-/* "Promise Resolution Procedure" */ /* [Promises/A+ 2.3] */
-var resolve = function (promise, x) {
- /* sanity check arguments */ /* [Promises/A+ 2.3.1] */
- if (promise === x || promise.proxy === x) {
- promise.reject(new TypeError("cannot resolve promise with itself"));
- return;
- }
-
- /* surgically check for a "then" method
- (mainly to just call the "getter" of "then" only once) */
- var then;
- if ((typeof x === "object" && x !== null) || typeof x === "function") {
- try { then = x.then; } /* [Promises/A+ 2.3.3.1, 3.5] */
- catch (e) {
- promise.reject(e); /* [Promises/A+ 2.3.3.2] */
- return;
- }
- }
-
- /* handle own Thenables [Promises/A+ 2.3.2]
- and similar "thenables" [Promises/A+ 2.3.3] */
- if (typeof then === "function") {
- var resolved = false;
- try {
- /* call retrieved "then" method */ /* [Promises/A+ 2.3.3.3] */
- then.call(x,
- /* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
- function (y) {
- if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
- if (y === x) /* [Promises/A+ 3.6] */
- promise.reject(new TypeError("circular thenable chain"));
- else
- resolve(promise, y);
- },
-
- /* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
- function (r) {
- if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
- promise.reject(r);
- }
- );
- }
- catch (e) {
- if (!resolved) /* [Promises/A+ 2.3.3.3.3] */
- promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */
- }
- return;
- }
-
- /* handle other values */
- promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */
-};
-
-// use native promises where possible
-var Promise = typeof Promise === 'undefined' ? api : Promise;
-
-// so we always have Promise.all()
-Promise.all = Promise.all || function( ps ){
- return new Promise(function( resolveAll, rejectAll ){
- var vals = new Array( ps.length );
- var doneCount = 0;
-
- var fulfill = function( i, val ){
- vals[i] = val;
- doneCount++;
-
- if( doneCount === ps.length ){
- resolveAll( vals );
- }
- };
-
- for( var i = 0; i < ps.length; i++ ){
- (function( i ){
- var p = ps[i];
- var isPromise = p.then != null;
-
- if( isPromise ){
- p.then(function( val ){
- fulfill( i, val );
- }, function( err ){
- rejectAll( err );
- });
- } else {
- var val = p;
- fulfill( i, val );
- }
- })( i );
- }
-
- });
-};
-
-module.exports = Promise;
-
-},{}],81:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('./is');
-var util = _dereq_('./util');
-
-var Selector = function( onlyThisGroup, selector ){
-
- if( !(this instanceof Selector) ){
- return new Selector(onlyThisGroup, selector);
- }
-
- if( selector === undefined && onlyThisGroup !== undefined ){
- selector = onlyThisGroup;
- onlyThisGroup = undefined;
- }
-
- var self = this;
-
- self._private = {
- selectorText: null,
- invalid: true
- };
-
- if( !selector || ( is.string(selector) && selector.match(/^\s*$/) ) ){
-
- if( onlyThisGroup == null ){
- // ignore
- self.length = 0;
- } else {
- self[0] = newQuery();
- self[0].group = onlyThisGroup;
- self.length = 1;
- }
-
- } else if( is.elementOrCollection( selector ) ){
- var collection = selector.collection();
-
- self[0] = newQuery();
- self[0].collection = collection;
- self.length = 1;
-
- } else if( is.fn( selector ) ) {
- self[0] = newQuery();
- self[0].filter = selector;
- self.length = 1;
-
- } else if( is.string( selector ) ){
-
- // the current subject in the query
- var currentSubject = null;
-
- // storage for parsed queries
- var newQuery = function(){
- return {
- classes: [],
- colonSelectors: [],
- data: [],
- group: null,
- ids: [],
- meta: [],
-
- // fake selectors
- collection: null, // a collection to match against
- filter: null, // filter function
-
- // these are defined in the upward direction rather than down (e.g. child)
- // because we need to go up in Selector.filter()
- parent: null, // parent query obj
- ancestor: null, // ancestor query obj
- subject: null, // defines subject in compound query (subject query obj; points to self if subject)
-
- // use these only when subject has been defined
- child: null,
- descendant: null
- };
- };
-
- // tokens in the query language
- var tokens = {
- metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]', // chars we need to escape in var names, etc
- comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=', // binary comparison op (used in data selectors)
- boolOp: '\\?|\\!|\\^', // boolean (unary) operators (used in data selectors)
- string: '"(?:\\\\"|[^"])+"' + '|' + "'(?:\\\\'|[^'])+'", // string literals (used in data selectors) -- doublequotes | singlequotes
- number: util.regex.number, // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
- meta: 'degree|indegree|outdegree', // allowed metadata fields (i.e. allowed functions to use from Collection)
- separator: '\\s*,\\s*', // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
- descendant: '\\s+',
- child: '\\s+>\\s+',
- subject: '\\$'
- };
- tokens.variable = '(?:[\\w-]|(?:\\\\'+ tokens.metaChar +'))+'; // a variable name
- tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
- tokens.className = tokens.variable; // a class name (follows variable conventions)
- tokens.id = tokens.variable; // an element id (follows variable conventions)
-
- // when a token like a variable has escaped meta characters, we need to clean the backslashes out
- // so that values get compared properly in Selector.filter()
- var cleanMetaChars = function(str){
- return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function(match, $1, offset, original){
- return $1;
- });
- };
-
- // add @ variants to comparatorOp
- var ops = tokens.comparatorOp.split('|');
- for( var i = 0; i < ops.length; i++ ){
- var op = ops[i];
- tokens.comparatorOp += '|@' + op;
- }
-
- // add ! variants to comparatorOp
- var ops = tokens.comparatorOp.split('|');
- for( var i = 0; i < ops.length; i++ ){
- var op = ops[i];
-
- if( op.indexOf('!') >= 0 ){ continue; } // skip ops that explicitly contain !
- if( op === '=' ){ continue; } // skip = b/c != is explicitly defined
-
- tokens.comparatorOp += '|\\!' + op;
- }
-
- // NOTE: add new expression syntax here to have it recognised by the parser;
- // - a query contains all adjacent (i.e. no separator in between) expressions;
- // - the current query is stored in self[i] --- you can use the reference to `this` in the populate function;
- // - you need to check the query objects in Selector.filter() for it actually filter properly, but that's pretty straight forward
- // - when you add something here, also add to Selector.toString()
- var exprs = [
- {
- name: 'group',
- query: true,
- regex: '(node|edge|\\*)',
- populate: function( group ){
- this.group = group == "*" ? group : group + 's';
- }
- },
-
- {
- name: 'state',
- query: true,
- // NB: if one colon selector is a substring of another from its start, place the longer one first
- // e.g. :foobar|:foo
- regex: '(:selected|:unselected|:locked|:unlocked|:visible|:hidden|:transparent|:grabbed|:free|:removed|:inside|:grabbable|:ungrabbable|:animated|:unanimated|:selectable|:unselectable|:orphan|:nonorphan|:parent|:child|:loop|:simple|:active|:inactive|:touch|:backgrounding|:nonbackgrounding)',
- populate: function( state ){
- this.colonSelectors.push( state );
- }
- },
-
- {
- name: 'id',
- query: true,
- regex: '\\#('+ tokens.id +')',
- populate: function( id ){
- this.ids.push( cleanMetaChars(id) );
- }
- },
-
- {
- name: 'className',
- query: true,
- regex: '\\.('+ tokens.className +')',
- populate: function( className ){
- this.classes.push( cleanMetaChars(className) );
- }
- },
-
- {
- name: 'dataExists',
- query: true,
- regex: '\\[\\s*('+ tokens.variable +')\\s*\\]',
- populate: function( variable ){
- this.data.push({
- field: cleanMetaChars(variable)
- });
- }
- },
-
- {
- name: 'dataCompare',
- query: true,
- regex: '\\[\\s*('+ tokens.variable +')\\s*('+ tokens.comparatorOp +')\\s*('+ tokens.value +')\\s*\\]',
- populate: function( variable, comparatorOp, value ){
- var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
-
- if( valueIsString ){
- value = value.substring(1, value.length - 1);
- } else {
- value = parseFloat(value);
- }
-
- this.data.push({
- field: cleanMetaChars(variable),
- operator: comparatorOp,
- value: value
- });
- }
- },
-
- {
- name: 'dataBool',
- query: true,
- regex: '\\[\\s*('+ tokens.boolOp +')\\s*('+ tokens.variable +')\\s*\\]',
- populate: function( boolOp, variable ){
- this.data.push({
- field: cleanMetaChars(variable),
- operator: boolOp
- });
- }
- },
-
- {
- name: 'metaCompare',
- query: true,
- regex: '\\[\\[\\s*('+ tokens.meta +')\\s*('+ tokens.comparatorOp +')\\s*('+ tokens.number +')\\s*\\]\\]',
- populate: function( meta, comparatorOp, number ){
- this.meta.push({
- field: cleanMetaChars(meta),
- operator: comparatorOp,
- value: parseFloat(number)
- });
- }
- },
-
- {
- name: 'nextQuery',
- separator: true,
- regex: tokens.separator,
- populate: function(){
- // go on to next query
- self[++i] = newQuery();
- currentSubject = null;
- }
- },
-
- {
- name: 'child',
- separator: true,
- regex: tokens.child,
- populate: function(){
- // this query is the parent of the following query
- var childQuery = newQuery();
- childQuery.parent = this;
- childQuery.subject = currentSubject;
-
- // we're now populating the child query with expressions that follow
- self[i] = childQuery;
- }
- },
-
- {
- name: 'descendant',
- separator: true,
- regex: tokens.descendant,
- populate: function(){
- // this query is the ancestor of the following query
- var descendantQuery = newQuery();
- descendantQuery.ancestor = this;
- descendantQuery.subject = currentSubject;
-
- // we're now populating the descendant query with expressions that follow
- self[i] = descendantQuery;
- }
- },
-
- {
- name: 'subject',
- modifier: true,
- regex: tokens.subject,
- populate: function(){
- if( currentSubject != null && this.subject != this ){
- util.error('Redefinition of subject in selector `' + selector + '`');
- return false;
- }
-
- currentSubject = this;
- this.subject = this;
- }
-
- }
- ];
-
- self._private.selectorText = selector;
- var remaining = selector;
- var i = 0;
-
- // of all the expressions, find the first match in the remaining text
- var consumeExpr = function( expectation ){
- var expr;
- var match;
- var name;
-
- for( var j = 0; j < exprs.length; j++ ){
- var e = exprs[j];
- var n = e.name;
-
- // ignore this expression if it doesn't meet the expectation function
- if( is.fn( expectation ) && !expectation(n, e) ){ continue; }
-
- var m = remaining.match(new RegExp( '^' + e.regex ));
-
- if( m != null ){
- match = m;
- expr = e;
- name = n;
-
- var consumed = m[0];
- remaining = remaining.substring( consumed.length );
-
- break; // we've consumed one expr, so we can return now
- }
- }
-
- return {
- expr: expr,
- match: match,
- name: name
- };
- };
-
- // consume all leading whitespace
- var consumeWhitespace = function(){
- var match = remaining.match(/^\s+/);
-
- if( match ){
- var consumed = match[0];
- remaining = remaining.substring( consumed.length );
- }
- };
-
- self[0] = newQuery(); // get started
-
- consumeWhitespace(); // get rid of leading whitespace
- for(;;){
- var check = consumeExpr();
-
- if( check.expr == null ){
- util.error('The selector `'+ selector +'`is invalid');
- return;
- } else {
- var args = [];
- for(var j = 1; j < check.match.length; j++){
- args.push( check.match[j] );
- }
-
- // let the token populate the selector object (i.e. in self[i])
- var ret = check.expr.populate.apply( self[i], args );
-
- if( ret === false ){ return; } // exit if population failed
- }
-
- // we're done when there's nothing left to parse
- if( remaining.match(/^\s*$/) ){
- break;
- }
- }
-
- self.length = i + 1;
-
- // adjust references for subject
- for(var j = 0; j < self.length; j++){
- var query = self[j];
-
- if( query.subject != null ){
- // go up the tree until we reach the subject
- for(;;){
- if( query.subject == query ){ break; } // done if subject is self
-
- if( query.parent != null ){ // swap parent/child reference
- var parent = query.parent;
- var child = query;
-
- child.parent = null;
- parent.child = child;
-
- query = parent; // go up the tree
- } else if( query.ancestor != null ){ // swap ancestor/descendant
- var ancestor = query.ancestor;
- var descendant = query;
-
- descendant.ancestor = null;
- ancestor.descendant = descendant;
-
- query = ancestor; // go up the tree
- } else {
- util.error('When adjusting references for the selector `'+ query +'`, neither parent nor ancestor was found');
- break;
- }
- } // for
-
- self[j] = query.subject; // subject should be the root query
- } // if
- } // for
-
- // make sure for each query that the subject group matches the implicit group if any
- if( onlyThisGroup != null ){
- for(var j = 0; j < self.length; j++){
- if( self[j].group != null && self[j].group != onlyThisGroup ){
- util.error('Group `'+ self[j].group +'` conflicts with implicit group `'+ onlyThisGroup +'` in selector `'+ selector +'`');
- return;
- }
-
- self[j].group = onlyThisGroup; // set to implicit group
- }
- }
-
- } else {
- util.error('A selector must be created from a string; found ' + selector);
- return;
- }
-
- self._private.invalid = false;
-
-};
-
-var selfn = Selector.prototype;
-
-selfn.size = function(){
- return this.length;
-};
-
-selfn.eq = function(i){
- return this[i];
-};
-
-var queryMatches = function(query, element){
- // check group
- if( query.group != null && query.group != '*' && query.group != element._private.group ){
- return false;
- }
-
- var cy = element.cy();
-
- // check colon selectors
- var allColonSelectorsMatch = true;
- for(var k = 0; k < query.colonSelectors.length; k++){
- var sel = query.colonSelectors[k];
-
- switch(sel){
- case ':selected':
- allColonSelectorsMatch = element.selected();
- break;
- case ':unselected':
- allColonSelectorsMatch = !element.selected();
- break;
- case ':selectable':
- allColonSelectorsMatch = element.selectable();
- break;
- case ':unselectable':
- allColonSelectorsMatch = !element.selectable();
- break;
- case ':locked':
- allColonSelectorsMatch = element.locked();
- break;
- case ':unlocked':
- allColonSelectorsMatch = !element.locked();
- break;
- case ':visible':
- allColonSelectorsMatch = element.visible();
- break;
- case ':hidden':
- allColonSelectorsMatch = !element.visible();
- break;
- case ':transparent':
- allColonSelectorsMatch = element.transparent();
- break;
- case ':grabbed':
- allColonSelectorsMatch = element.grabbed();
- break;
- case ':free':
- allColonSelectorsMatch = !element.grabbed();
- break;
- case ':removed':
- allColonSelectorsMatch = element.removed();
- break;
- case ':inside':
- allColonSelectorsMatch = !element.removed();
- break;
- case ':grabbable':
- allColonSelectorsMatch = element.grabbable();
- break;
- case ':ungrabbable':
- allColonSelectorsMatch = !element.grabbable();
- break;
- case ':animated':
- allColonSelectorsMatch = element.animated();
- break;
- case ':unanimated':
- allColonSelectorsMatch = !element.animated();
- break;
- case ':parent':
- allColonSelectorsMatch = element.isNode() && element.children().nonempty();
- break;
- case ':child':
- case ':nonorphan':
- allColonSelectorsMatch = element.isNode() && element.parent().nonempty();
- break;
- case ':orphan':
- allColonSelectorsMatch = element.isNode() && element.parent().empty();
- break;
- case ':loop':
- allColonSelectorsMatch = element.isEdge() && element.data('source') === element.data('target');
- break;
- case ':simple':
- allColonSelectorsMatch = element.isEdge() && element.data('source') !== element.data('target');
- break;
- case ':active':
- allColonSelectorsMatch = element.active();
- break;
- case ':inactive':
- allColonSelectorsMatch = !element.active();
- break;
- case ':touch':
- allColonSelectorsMatch = is.touch();
- break;
- case ':backgrounding':
- allColonSelectorsMatch = element.backgrounding();
- break;
- case ':nonbackgrounding':
- allColonSelectorsMatch = !element.backgrounding();
- break;
- }
-
- if( !allColonSelectorsMatch ) break;
- }
- if( !allColonSelectorsMatch ) return false;
-
- // check id
- var allIdsMatch = true;
- for(var k = 0; k < query.ids.length; k++){
- var id = query.ids[k];
- var actualId = element._private.data.id;
-
- allIdsMatch = allIdsMatch && (id == actualId);
-
- if( !allIdsMatch ) break;
- }
- if( !allIdsMatch ) return false;
-
- // check classes
- var allClassesMatch = true;
- for(var k = 0; k < query.classes.length; k++){
- var cls = query.classes[k];
-
- allClassesMatch = allClassesMatch && element.hasClass(cls);
-
- if( !allClassesMatch ) break;
- }
- if( !allClassesMatch ) return false;
-
- // generic checking for data/metadata
- var operandsMatch = function(params){
- var allDataMatches = true;
- for(var k = 0; k < query[params.name].length; k++){
- var data = query[params.name][k];
- var operator = data.operator;
- var value = data.value;
- var field = data.field;
- var matches;
-
- if( operator != null && value != null ){
-
- var fieldVal = params.fieldValue(field);
- var fieldStr = !is.string(fieldVal) && !is.number(fieldVal) ? '' : '' + fieldVal;
- var valStr = '' + value;
-
- var caseInsensitive = false;
- if( operator.indexOf('@') >= 0 ){
- fieldStr = fieldStr.toLowerCase();
- valStr = valStr.toLowerCase();
-
- operator = operator.replace('@', '');
- caseInsensitive = true;
- }
-
- var notExpr = false;
- var handledNotExpr = false;
- if( operator.indexOf('!') >= 0 ){
- operator = operator.replace('!', '');
- notExpr = true;
- }
-
- // if we're doing a case insensitive comparison, then we're using a STRING comparison
- // even if we're comparing numbers
- if( caseInsensitive ){
- value = valStr.toLowerCase();
- fieldVal = fieldStr.toLowerCase();
- }
-
- switch(operator){
- case '*=':
- matches = fieldStr.search(valStr) >= 0;
- break;
- case '$=':
- matches = new RegExp(valStr + '$').exec(fieldStr) != null;
- break;
- case '^=':
- matches = new RegExp('^' + valStr).exec(fieldStr) != null;
- break;
- case '=':
- matches = fieldVal === value;
- break;
- case '!=':
- matches = fieldVal !== value;
- break;
- case '>':
- matches = !notExpr ? fieldVal > value : fieldVal <= value;
- handledNotExpr = true;
- break;
- case '>=':
- matches = !notExpr ? fieldVal >= value : fieldVal < value;
- handledNotExpr = true;
- break;
- case '<':
- matches = !notExpr ? fieldVal < value : fieldVal >= value;
- handledNotExpr = true;
- break;
- case '<=':
- matches = !notExpr ? fieldVal <= value : fieldVal > value;
- handledNotExpr = true;
- break;
- default:
- matches = false;
- break;
-
- }
- } else if( operator != null ){
- switch(operator){
- case '?':
- matches = params.fieldTruthy(field);
- break;
- case '!':
- matches = !params.fieldTruthy(field);
- break;
- case '^':
- matches = params.fieldUndefined(field);
- break;
- }
- } else {
- matches = !params.fieldUndefined(field);
- }
-
- if( notExpr && !handledNotExpr ){
- matches = !matches;
- handledNotExpr = true;
- }
-
- if( !matches ){
- allDataMatches = false;
- break;
- }
- } // for
-
- return allDataMatches;
- }; // operandsMatch
-
- // check data matches
- var allDataMatches = operandsMatch({
- name: 'data',
- fieldValue: function(field){
- return element._private.data[field];
- },
- fieldRef: function(field){
- return 'element._private.data.' + field;
- },
- fieldUndefined: function(field){
- return element._private.data[field] === undefined;
- },
- fieldTruthy: function(field){
- if( element._private.data[field] ){
- return true;
- }
- return false;
- }
- });
-
- if( !allDataMatches ){
- return false;
- }
-
- // check metadata matches
- var allMetaMatches = operandsMatch({
- name: 'meta',
- fieldValue: function(field){
- return element[field]();
- },
- fieldRef: function(field){
- return 'element.' + field + '()';
- },
- fieldUndefined: function(field){
- return element[field]() == null;
- },
- fieldTruthy: function(field){
- if( element[field]() ){
- return true;
- }
- return false;
- }
- });
-
- if( !allMetaMatches ){
- return false;
- }
-
- // check collection
- if( query.collection != null ){
- var matchesAny = query.collection._private.ids[ element.id() ] != null;
-
- if( !matchesAny ){
- return false;
- }
- }
-
- // check filter function
- if( query.filter != null && element.collection().filter( query.filter ).size() === 0 ){
- return false;
- }
-
-
- // check parent/child relations
- var confirmRelations = function( query, elements ){
- if( query != null ){
- var matches = false;
-
- if( !cy.hasCompoundNodes() ){
- return false;
- }
-
- elements = elements(); // make elements functional so we save cycles if query == null
-
- // query must match for at least one element (may be recursive)
- for(var i = 0; i < elements.length; i++){
- if( queryMatches( query, elements[i] ) ){
- matches = true;
- break;
- }
- }
-
- return matches;
- } else {
- return true;
- }
- };
-
- if (! confirmRelations(query.parent, function(){
- return element.parent();
- }) ){ return false; }
-
- if (! confirmRelations(query.ancestor, function(){
- return element.parents();
- }) ){ return false; }
-
- if (! confirmRelations(query.child, function(){
- return element.children();
- }) ){ return false; }
-
- if (! confirmRelations(query.descendant, function(){
- return element.descendants();
- }) ){ return false; }
-
- // we've reached the end, so we've matched everything for this query
- return true;
-}; // queryMatches
-
-// filter an existing collection
-selfn.filter = function(collection){
- var self = this;
- var cy = collection.cy();
-
- // don't bother trying if it's invalid
- if( self._private.invalid ){
- return cy.collection();
- }
-
- var selectorFunction = function(i, element){
- for(var j = 0; j < self.length; j++){
- var query = self[j];
-
- if( queryMatches(query, element) ){
- return true;
- }
- }
-
- return false;
- };
-
- if( self._private.selectorText == null ){
- selectorFunction = function(){ return true; };
- }
-
- var filteredCollection = collection.filter( selectorFunction );
-
- return filteredCollection;
-}; // filter
-
-// does selector match a single element?
-selfn.matches = function(ele){
- var self = this;
-
- // don't bother trying if it's invalid
- if( self._private.invalid ){
- return false;
- }
-
- for(var j = 0; j < self.length; j++){
- var query = self[j];
-
- if( queryMatches(query, ele) ){
- return true;
- }
- }
-
- return false;
-}; // filter
-
-// ith query to string
-selfn.toString = selfn.selector = function(){
-
- var str = '';
-
- var clean = function(obj, isValue){
- if( is.string(obj) ){
- return isValue ? '"' + obj + '"' : obj;
- }
- return '';
- };
-
- var queryToString = function(query){
- var str = '';
-
- if( query.subject === query ){
- str += '$';
- }
-
- var group = clean(query.group);
- str += group.substring(0, group.length - 1);
-
- for(var j = 0; j < query.data.length; j++){
- var data = query.data[j];
-
- if( data.value ){
- str += '[' + data.field + clean(data.operator) + clean(data.value, true) + ']';
- } else {
- str += '[' + clean(data.operator) + data.field + ']';
- }
- }
-
- for(var j = 0; j < query.meta.length; j++){
- var meta = query.meta[j];
- str += '[[' + meta.field + clean(meta.operator) + clean(meta.value, true) + ']]';
- }
-
- for(var j = 0; j < query.colonSelectors.length; j++){
- var sel = query.colonSelectors[i];
- str += sel;
- }
-
- for(var j = 0; j < query.ids.length; j++){
- var sel = '#' + query.ids[i];
- str += sel;
- }
-
- for(var j = 0; j < query.classes.length; j++){
- var sel = '.' + query.classes[j];
- str += sel;
- }
-
- if( query.parent != null ){
- str = queryToString( query.parent ) + ' > ' + str;
- }
-
- if( query.ancestor != null ){
- str = queryToString( query.ancestor ) + ' ' + str;
- }
-
- if( query.child != null ){
- str += ' > ' + queryToString( query.child );
- }
-
- if( query.descendant != null ){
- str += ' ' + queryToString( query.descendant );
- }
-
- return str;
- };
-
- for(var i = 0; i < this.length; i++){
- var query = this[i];
-
- str += queryToString( query );
-
- if( this.length > 1 && i < this.length - 1 ){
- str += ', ';
- }
- }
-
- return str;
-};
-
-module.exports = Selector;
-
-},{"./is":77,"./util":94}],82:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-var is = _dereq_('../is');
-
-var styfn = {};
-
-// (potentially expensive calculation)
-// apply the style to the element based on
-// - its bypass
-// - what selectors match it
-styfn.apply = function( eles ){
- var self = this;
-
- if( self._private.newStyle ){ // clear style caches
- this._private.contextStyles = {};
- this._private.propDiffs = {};
- }
-
- for( var ie = 0; ie < eles.length; ie++ ){
- var ele = eles[ie];
- var cxtMeta = self.getContextMeta( ele );
- var cxtStyle = self.getContextStyle( cxtMeta );
- var app = self.applyContextStyle( cxtMeta, cxtStyle, ele );
-
- self.updateTransitions( ele, app.diffProps );
- self.updateStyleHints( ele );
-
- } // for elements
-
- self._private.newStyle = false;
-};
-
-styfn.getPropertiesDiff = function( oldCxtKey, newCxtKey ){
- var self = this;
- var cache = self._private.propDiffs = self._private.propDiffs || {};
- var dualCxtKey = oldCxtKey + '-' + newCxtKey;
- var cachedVal = cache[dualCxtKey];
-
- if( cachedVal ){
- return cachedVal;
- }
-
- var diffProps = [];
- var addedProp = {};
-
- for( var i = 0; i < self.length; i++ ){
- var cxt = self[i];
- var oldHasCxt = oldCxtKey[i] === 't';
- var newHasCxt = newCxtKey[i] === 't';
- var cxtHasDiffed = oldHasCxt !== newHasCxt;
- var cxtHasMappedProps = cxt.mappedProperties.length > 0;
-
- if( cxtHasDiffed || cxtHasMappedProps ){
- var props;
-
- if( cxtHasDiffed && cxtHasMappedProps ){
- props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
- } else if( cxtHasDiffed ){
- props = cxt.properties; // need to check them all
- } else if( cxtHasMappedProps ){
- props = cxt.mappedProperties; // only need to check mapped
- }
-
- for( var j = 0; j < props.length; j++ ){
- var prop = props[j];
- var name = prop.name;
-
- // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
- // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
- // is cached)
- var laterCxtOverrides = false;
- for( var k = i + 1; k < self.length; k++ ){
- var laterCxt = self[k];
- var hasLaterCxt = newCxtKey[k] === 't';
-
- if( !hasLaterCxt ){ continue; } // can't override unless the context is active
-
- laterCxtOverrides = laterCxt.properties[ prop.name ] != null;
-
- if( laterCxtOverrides ){ break; } // exit early as long as one later context overrides
- }
-
- if( !addedProp[name] && !laterCxtOverrides ){
- addedProp[name] = true;
- diffProps.push( name );
- }
- } // for props
- } // if
-
- } // for contexts
-
- cache[ dualCxtKey ] = diffProps;
- return diffProps;
-};
-
-styfn.getContextMeta = function( ele ){
- var self = this;
- var cxtKey = '';
- var diffProps;
- var prevKey = ele._private.styleCxtKey || '';
-
- if( self._private.newStyle ){
- prevKey = ''; // since we need to apply all style if a fresh stylesheet
- }
-
- // get the cxt key
- for( var i = 0; i < self.length; i++ ){
- var context = self[i];
- var contextSelectorMatches = context.selector && context.selector.matches( ele ); // NB: context.selector may be null for 'core'
-
- if( contextSelectorMatches ){
- cxtKey += 't';
- } else {
- cxtKey += 'f';
- }
- } // for context
-
- diffProps = self.getPropertiesDiff( prevKey, cxtKey );
-
- ele._private.styleCxtKey = cxtKey;
-
- return {
- key: cxtKey,
- diffPropNames: diffProps
- };
-};
-
-// gets a computed ele style object based on matched contexts
-styfn.getContextStyle = function( cxtMeta ){
- var cxtKey = cxtMeta.key;
- var self = this;
- var cxtStyles = this._private.contextStyles = this._private.contextStyles || {};
-
- // if already computed style, returned cached copy
- if( cxtStyles[cxtKey] ){ return cxtStyles[cxtKey]; }
-
- var style = {
- _private: {
- key: cxtKey
- }
- };
-
- for( var i = 0; i < self.length; i++ ){
- var cxt = self[i];
- var hasCxt = cxtKey[i] === 't';
-
- if( !hasCxt ){ continue; }
-
- for( var j = 0; j < cxt.properties.length; j++ ){
- var prop = cxt.properties[j];
- var styProp = style[ prop.name ] = prop;
-
- styProp.context = cxt;
- }
- }
-
- cxtStyles[cxtKey] = style;
- return style;
-};
-
-styfn.applyContextStyle = function( cxtMeta, cxtStyle, ele ){
- var self = this;
- var diffProps = cxtMeta.diffPropNames;
- var retDiffProps = {};
-
- for( var i = 0; i < diffProps.length; i++ ){
- var diffPropName = diffProps[i];
- var cxtProp = cxtStyle[ diffPropName ];
- var eleProp = ele._private.style[ diffPropName ];
-
- // save cycles when the context prop doesn't need to be applied
- if( !cxtProp || eleProp === cxtProp ){ continue; }
-
- var retDiffProp = retDiffProps[ diffPropName ] = {
- prev: eleProp
- };
-
- self.applyParsedProperty( ele, cxtProp );
-
- retDiffProp.next = ele._private.style[ diffPropName ];
-
- if( retDiffProp.next && retDiffProp.next.bypass ){
- retDiffProp.next = retDiffProp.next.bypassed;
- }
- }
-
- return {
- diffProps: retDiffProps
- };
-};
-
-styfn.updateStyleHints = function(ele){
- var _p = ele._private;
- var self = this;
- var style = _p.style;
-
- if( ele.removed() ){ return; }
-
- // set whether has pie or not; for greater efficiency
- var hasPie = false;
- if( _p.group === 'nodes' && self._private.hasPie ){
- for( var i = 1; i <= self.pieBackgroundN; i++ ){ // 1..N
- var size = _p.style['pie-' + i + '-background-size'].value;
-
- if( size > 0 ){
- hasPie = true;
- break;
- }
- }
- }
-
- _p.hasPie = hasPie;
-
- var transform = style['text-transform'].strValue;
- var content = style['label'].strValue;
- var fStyle = style['font-style'].strValue;
- var size = style['font-size'].pfValue + 'px';
- var family = style['font-family'].strValue;
- // var variant = style['font-variant'].strValue;
- var weight = style['font-weight'].strValue;
- var valign = style['text-valign'].strValue;
- var halign = style['text-valign'].strValue;
- var oWidth = style['text-outline-width'].pfValue;
- var wrap = style['text-wrap'].strValue;
- var wrapW = style['text-max-width'].pfValue;
- _p.labelKey = fStyle +'$'+ size +'$'+ family +'$'+ weight +'$'+ content +'$'+ transform +'$'+ valign +'$'+ halign +'$'+ oWidth + '$' + wrap + '$' + wrapW;
- _p.fontKey = fStyle +'$'+ weight +'$'+ size +'$'+ family;
-
- var width = style['width'].pfValue;
- var height = style['height'].pfValue;
- var borderW = style['border-width'].pfValue;
- _p.boundingBoxKey = width +'$'+ height +'$'+ borderW;
-
- if( ele._private.group === 'edges' ){
- var cpss = style['control-point-step-size'].pfValue;
- var cpd = style['control-point-distances'] ? style['control-point-distances'].pfValue.join('_') : undefined;
- var cpw = style['control-point-weights'].value.join('_');
- var curve = style['curve-style'].strValue;
- var sd = style['segment-distances'] ? style['segment-distances'].pfValue.join('_') : undefined;
- var sw = style['segment-weights'].value.join('_');
-
- _p.boundingBoxKey += '$'+ cpss +'$'+ cpd +'$'+ cpw +'$'+ sd +'$'+ sw +'$'+ curve;
- }
-
- _p.styleKey = Date.now();
-};
-
-// apply a property to the style (for internal use)
-// returns whether application was successful
-//
-// now, this function flattens the property, and here's how:
-//
-// for parsedProp:{ bypass: true, deleteBypass: true }
-// no property is generated, instead the bypass property in the
-// element's style is replaced by what's pointed to by the `bypassed`
-// field in the bypass property (i.e. restoring the property the
-// bypass was overriding)
-//
-// for parsedProp:{ mapped: truthy }
-// the generated flattenedProp:{ mapping: prop }
-//
-// for parsedProp:{ bypass: true }
-// the generated flattenedProp:{ bypassed: parsedProp }
-styfn.applyParsedProperty = function( ele, parsedProp ){
- var self = this;
- var prop = parsedProp;
- var style = ele._private.style;
- var fieldVal, flatProp;
- var types = self.types;
- var type = self.properties[ prop.name ].type;
- var propIsBypass = prop.bypass;
- var origProp = style[ prop.name ];
- var origPropIsBypass = origProp && origProp.bypass;
- var _p = ele._private;
-
- // can't apply auto to width or height unless it's a parent node
- if( (parsedProp.name === 'height' || parsedProp.name === 'width') && ele.isNode() ){
- if( parsedProp.value === 'auto' && !ele.isParent() ){
- return false;
- } else if( parsedProp.value !== 'auto' && ele.isParent() ){
- prop = parsedProp = this.parse( parsedProp.name, 'auto', propIsBypass );
- }
- }
-
- // check if we need to delete the current bypass
- if( propIsBypass && prop.deleteBypass ){ // then this property is just here to indicate we need to delete
- var currentProp = style[ prop.name ];
-
- // can only delete if the current prop is a bypass and it points to the property it was overriding
- if( !currentProp ){
- return true; // property is already not defined
- } else if( currentProp.bypass && currentProp.bypassed ){ // then replace the bypass property with the original
-
- // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
- style[ prop.name ] = currentProp.bypassed;
- return true;
-
- } else {
- return false; // we're unsuccessful deleting the bypass
- }
- }
-
- var printMappingErr = function(){
- util.error('Do not assign mappings to elements without corresponding data (e.g. ele `'+ ele.id() +'` for property `'+ prop.name +'` with data field `'+ prop.field +'`); try a `['+ prop.field +']` selector to limit scope to elements with `'+ prop.field +'` defined');
- };
-
- // put the property in the style objects
- switch( prop.mapped ){ // flatten the property if mapped
- case types.mapData:
- case types.mapLayoutData:
- case types.mapScratch:
-
- var isLayout = prop.mapped === types.mapLayoutData;
- var isScratch = prop.mapped === types.mapScratch;
-
- // flatten the field (e.g. data.foo.bar)
- var fields = prop.field.split(".");
- var fieldVal;
-
- if( isScratch || isLayout ){
- fieldVal = _p.scratch;
- } else {
- fieldVal = _p.data;
- }
-
- for( var i = 0; i < fields.length && fieldVal; i++ ){
- var field = fields[i];
- fieldVal = fieldVal[ field ];
- }
-
- var percent;
- if( !is.number(fieldVal) ){ // then keep the mapping but assume 0% for now
- percent = 0;
- } else {
- percent = (fieldVal - prop.fieldMin) / (prop.fieldMax - prop.fieldMin);
- }
-
- // make sure to bound percent value
- if( percent < 0 ){
- percent = 0;
- } else if( percent > 1 ){
- percent = 1;
- }
-
- if( type.color ){
- var r1 = prop.valueMin[0];
- var r2 = prop.valueMax[0];
- var g1 = prop.valueMin[1];
- var g2 = prop.valueMax[1];
- var b1 = prop.valueMin[2];
- var b2 = prop.valueMax[2];
- var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
- var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
-
- var clr = [
- Math.round( r1 + (r2 - r1)*percent ),
- Math.round( g1 + (g2 - g1)*percent ),
- Math.round( b1 + (b2 - b1)*percent ),
- Math.round( a1 + (a2 - a1)*percent )
- ];
-
- flatProp = { // colours are simple, so just create the flat property instead of expensive string parsing
- bypass: prop.bypass, // we're a bypass if the mapping property is a bypass
- name: prop.name,
- value: clr,
- strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
- };
-
- } else if( type.number ){
- var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
- flatProp = this.parse( prop.name, calcValue, prop.bypass, true );
-
- } else {
- return false; // can only map to colours and numbers
- }
-
- if( !flatProp ){ // if we can't flatten the property, then use the origProp so we still keep the mapping itself
- flatProp = this.parse( prop.name, origProp.strValue, prop.bypass, true );
- }
-
- if( !flatProp ){ printMappingErr(); }
- flatProp.mapping = prop; // keep a reference to the mapping
- prop = flatProp; // the flattened (mapped) property is the one we want
-
- break;
-
- // direct mapping
- case types.data:
- case types.layoutData:
- case types.scratch:
- var isLayout = prop.mapped === types.layoutData;
- var isScratch = prop.mapped === types.scratch;
-
- // flatten the field (e.g. data.foo.bar)
- var fields = prop.field.split(".");
- var fieldVal;
-
- if( isScratch || isLayout ){
- fieldVal = _p.scratch;
- } else {
- fieldVal = _p.data;
- }
-
- if( fieldVal ){ for( var i = 0; i < fields.length; i++ ){
- var field = fields[i];
- fieldVal = fieldVal[ field ];
- } }
-
- flatProp = this.parse( prop.name, fieldVal, prop.bypass, true );
-
- if( !flatProp ){ // if we can't flatten the property, then use the origProp so we still keep the mapping itself
- var flatPropVal = origProp ? origProp.strValue : '';
-
- flatProp = this.parse( prop.name, flatPropVal, prop.bypass, true );
- }
-
- if( !flatProp ){ printMappingErr(); }
- flatProp.mapping = prop; // keep a reference to the mapping
- prop = flatProp; // the flattened (mapped) property is the one we want
-
- break;
-
- case types.fn:
- var fn = prop.value;
- var fnRetVal = fn( ele );
-
- flatProp = this.parse( prop.name, fnRetVal, prop.bypass, true );
- flatProp.mapping = prop; // keep a reference to the mapping
- prop = flatProp; // the flattened (mapped) property is the one we want
-
- break;
-
- case undefined:
- break; // just set the property
-
- default:
- return false; // not a valid mapping
- }
-
- // if the property is a bypass property, then link the resultant property to the original one
- if( propIsBypass ){
- if( origPropIsBypass ){ // then this bypass overrides the existing one
- prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
- } else { // then link the orig prop to the new bypass
- prop.bypassed = origProp;
- }
-
- style[ prop.name ] = prop; // and set
-
- } else { // prop is not bypass
- if( origPropIsBypass ){ // then keep the orig prop (since it's a bypass) and link to the new prop
- origProp.bypassed = prop;
- } else { // then just replace the old prop with the new one
- style[ prop.name ] = prop;
- }
- }
-
- return true;
-};
-
-// updates the visual style for all elements (useful for manual style modification after init)
-styfn.update = function(){
- var cy = this._private.cy;
- var eles = cy.elements();
-
- eles.updateStyle();
-};
-
-// just update the functional properties (i.e. mappings) in the elements'
-// styles (less expensive than recalculation)
-styfn.updateMappers = function( eles ){
- var self = this;
-
- for( var i = 0; i < eles.length; i++ ){ // for each ele
- var ele = eles[i];
- var style = ele._private.style;
-
- for( var j = 0; j < self.properties.length; j++ ){ // for each prop
- var prop = self.properties[j];
- var propInStyle = style[ prop.name ];
-
- if( propInStyle && propInStyle.mapping ){
- var mapping = propInStyle.mapping;
- this.applyParsedProperty( ele, mapping ); // reapply the mapping property
- }
- }
-
- this.updateStyleHints( ele );
- }
-};
-
-// diffProps : { name => { prev, next } }
-styfn.updateTransitions = function( ele, diffProps, isBypass ){
- var self = this;
- var _p = ele._private;
- var style = _p.style;
- var props = style['transition-property'].value;
- var duration = style['transition-duration'].pfValue;
- var delay = style['transition-delay'].pfValue;
- var css = {};
-
- if( props.length > 0 && duration > 0 ){
-
- // build up the style to animate towards
- var anyPrev = false;
- for( var i = 0; i < props.length; i++ ){
- var prop = props[i];
- var styProp = style[ prop ];
- var diffProp = diffProps[ prop ];
-
- if( !diffProp ){ continue; }
-
- var prevProp = diffProp.prev;
- var fromProp = prevProp;
- var toProp = diffProp.next != null ? diffProp.next : styProp;
- var diff = false;
- var initVal;
- var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
-
- if( !fromProp ){ continue; }
-
- // consider px values
- if( is.number( fromProp.pfValue ) && is.number( toProp.pfValue ) ){
- diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
- initVal = fromProp.pfValue + initDt * diff;
-
- // consider numerical values
- } else if( is.number( fromProp.value ) && is.number( toProp.value ) ){
- diff = toProp.value - fromProp.value; // nonzero is truthy
- initVal = fromProp.value + initDt * diff;
-
- // consider colour values
- } else if( is.array( fromProp.value ) && is.array( toProp.value ) ){
- diff = fromProp.value[0] !== toProp.value[0]
- || fromProp.value[1] !== toProp.value[1]
- || fromProp.value[2] !== toProp.value[2]
- ;
-
- initVal = fromProp.strValue;
- }
-
- // the previous value is good for an animation only if it's different
- if( diff ){
- css[ prop ] = toProp.strValue; // to val
- this.applyBypass( ele, prop, initVal ); // from val
- anyPrev = true;
- }
-
- } // end if props allow ani
-
- // can't transition if there's nothing previous to transition from
- if( !anyPrev ){ return; }
-
- _p.transitioning = true;
-
- ele.stop();
-
- if( delay > 0 ){
- ele.delay( delay );
- }
-
- ele.animate({
- css: css
- }, {
- duration: duration,
- easing: style['transition-timing-function'].value,
- queue: false,
- complete: function(){
- if( !isBypass ){
- self.removeBypasses( ele, props );
- }
-
- _p.transitioning = false;
- }
- });
-
- } else if( _p.transitioning ){
- ele.stop();
-
- this.removeBypasses( ele, props );
-
- _p.transitioning = false;
- }
-};
-
-module.exports = styfn;
-
-},{"../is":77,"../util":94}],83:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var util = _dereq_('../util');
-
-var styfn = {};
-
-// bypasses are applied to an existing style on an element, and just tacked on temporarily
-// returns true iff application was successful for at least 1 specified property
-styfn.applyBypass = function( eles, name, value, updateTransitions ){
- var self = this;
- var props = [];
- var isBypass = true;
-
- // put all the properties (can specify one or many) in an array after parsing them
- if( name === "*" || name === "**" ){ // apply to all property names
-
- if( value !== undefined ){
- for( var i = 0; i < self.properties.length; i++ ){
- var prop = self.properties[i];
- var name = prop.name;
-
- var parsedProp = this.parse(name, value, true);
-
- if( parsedProp ){
- props.push( parsedProp );
- }
- }
- }
-
- } else if( is.string(name) ){ // then parse the single property
- var parsedProp = this.parse(name, value, true);
-
- if( parsedProp ){
- props.push( parsedProp );
- }
- } else if( is.plainObject(name) ){ // then parse each property
- var specifiedProps = name;
- updateTransitions = value;
-
- for( var i = 0; i < self.properties.length; i++ ){
- var prop = self.properties[i];
- var name = prop.name;
- var value = specifiedProps[ name ];
-
- if( value === undefined ){ // try camel case name too
- value = specifiedProps[ util.dash2camel(name) ];
- }
-
- if( value !== undefined ){
- var parsedProp = this.parse(name, value, true);
-
- if( parsedProp ){
- props.push( parsedProp );
- }
- }
- }
- } else { // can't do anything without well defined properties
- return false;
- }
-
- // we've failed if there are no valid properties
- if( props.length === 0 ){ return false; }
-
- // now, apply the bypass properties on the elements
- var ret = false; // return true if at least one succesful bypass applied
- for( var i = 0; i < eles.length; i++ ){ // for each ele
- var ele = eles[i];
- var style = ele._private.style;
- var diffProps = {};
- var diffProp;
-
- for( var j = 0; j < props.length; j++ ){ // for each prop
- var prop = props[j];
-
- if( updateTransitions ){
- var prevProp = style[ prop.name ];
- diffProp = diffProps[ prop.name ] = { prev: prevProp };
- }
-
- ret = this.applyParsedProperty( ele, prop ) || ret;
-
- if( updateTransitions ){
- diffProp.next = style[ prop.name ];
- }
-
- } // for props
-
- if( ret ){
- this.updateStyleHints( ele );
- }
-
- if( updateTransitions ){
- this.updateTransitions( ele, diffProps, isBypass );
- }
- } // for eles
-
- return ret;
-};
-
-// only useful in specific cases like animation
-styfn.overrideBypass = function( eles, name, value ){
- name = util.camel2dash(name);
-
- for( var i = 0; i < eles.length; i++ ){
- var ele = eles[i];
- var prop = ele._private.style[ name ];
- var type = this.properties[ name ].type;
- var isColor = type.color;
- var isMulti = type.mutiple;
-
- if( !prop.bypass ){ // need a bypass if one doesn't exist
- this.applyBypass( ele, name, value );
- continue;
- }
-
- prop.value = value;
-
- if( prop.pfValue != null ){
- prop.pfValue = value;
- }
-
- if( isColor ){
- prop.strValue = 'rgb(' + value.join(',') + ')';
- } else if( isMulti ){
- prop.strValue = value.join(' ');
- } else {
- prop.strValue = '' + value;
- }
- }
-};
-
-styfn.removeAllBypasses = function( eles, updateTransitions ){
- return this.removeBypasses( eles, this.propertyNames, updateTransitions );
-};
-
-styfn.removeBypasses = function( eles, props, updateTransitions ){
- var isBypass = true;
-
- for( var j = 0; j < eles.length; j++ ){
- var ele = eles[j];
- var diffProps = {};
- var style = ele._private.style;
-
- for( var i = 0; i < props.length; i++ ){
- var name = props[i];
- var prop = this.properties[ name ];
- var value = ''; // empty => remove bypass
- var parsedProp = this.parse(name, value, true);
- var prevProp = style[ prop.name ];
- var diffProp = diffProps[ prop.name ] = { prev: prevProp };
-
- this.applyParsedProperty(ele, parsedProp);
-
- diffProp.next = style[ prop.name ];
- } // for props
-
- this.updateStyleHints( ele );
-
- if( updateTransitions ){
- this.updateTransitions( ele, diffProps, isBypass );
- }
- } // for eles
-};
-
-module.exports = styfn;
-
-},{"../is":77,"../util":94}],84:[function(_dereq_,module,exports){
-'use strict';
-
-var window = _dereq_('../window');
-
-var styfn = {};
-
-// gets what an em size corresponds to in pixels relative to a dom element
-styfn.getEmSizeInPixels = function(){
- var px = this.containerCss('font-size');
-
- if( px != null ){
- return parseFloat( px );
- } else {
- return 1; // for headless
- }
-};
-
-// gets css property from the core container
-styfn.containerCss = function( propName ){
- var cy = this._private.cy;
- var domElement = cy.container();
-
- if( window && domElement && window.getComputedStyle ){
- return window.getComputedStyle(domElement).getPropertyValue( propName );
- }
-};
-
-module.exports = styfn;
-
-},{"../window":100}],85:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-var is = _dereq_('../is');
-
-var styfn = {};
-
-// gets the rendered style for an element
-styfn.getRenderedStyle = function( ele ){
- return this.getRawStyle( ele, true );
-};
-
-// gets the raw style for an element
-styfn.getRawStyle = function( ele, isRenderedVal ){
- var self = this;
- var ele = ele[0]; // insure it's an element
-
- if( ele ){
- var rstyle = {};
-
- for( var i = 0; i < self.properties.length; i++ ){
- var prop = self.properties[i];
- var val = self.getStylePropertyValue( ele, prop.name, isRenderedVal );
-
- if( val ){
- rstyle[ prop.name ] = val;
- rstyle[ util.dash2camel(prop.name) ] = val;
- }
- }
-
- return rstyle;
- }
-};
-
-styfn.getStylePropertyValue = function( ele, propName, isRenderedVal ){
- var self = this;
- var ele = ele[0]; // insure it's an element
-
- if( ele ){
- var style = ele._private.style;
- var prop = self.properties[ propName ];
- var type = prop.type;
- var styleProp = style[ prop.name ];
- var zoom = ele.cy().zoom();
-
- if( styleProp ){
- var units = styleProp.units ? type.implicitUnits || 'px' : null;
- var val = units ? [].concat( styleProp.pfValue ).map(function( pfValue ){
- return ( pfValue * (isRenderedVal ? zoom : 1) ) + units;
- }).join(' ') : styleProp.strValue;
-
- return val;
- }
- }
-};
-
-// gets the value style for an element (useful for things like animations)
-styfn.getValueStyle = function( ele ){
- var self = this;
- var rstyle = {};
- var style;
- var isEle = is.element(ele);
-
- if( isEle ){
- style = ele._private.style;
- } else {
- style = ele; // just passed the style itself
- }
-
- if( style ){
- for( var i = 0; i < self.properties.length; i++ ){
- var prop = self.properties[i];
- var styleProp = style[ prop.name ] || style[ util.dash2camel(prop.name) ];
-
- if( styleProp !== undefined ){ // then make a prop of it
- if( is.plainObject( styleProp ) ){
- styleProp = this.parse( prop.name, styleProp.strValue );
- } else {
- styleProp = this.parse( prop.name, styleProp );
- }
- }
-
- if( styleProp ){
- rstyle[ prop.name ] = styleProp;
- rstyle[ util.dash2camel(prop.name) ] = styleProp;
- }
- }
- }
-
- return rstyle;
-};
-
-styfn.getPropsList = function( propsObj ){
- var self = this;
- var rstyle = [];
- var style = propsObj;
- var props = self.properties;
-
- if( style ){
- for( var name in style ){
- var val = style[name];
- var prop = props[name] || props[ util.camel2dash(name) ];
- var styleProp = this.parse( prop.name, val );
-
- rstyle.push( styleProp );
- }
- }
-
- return rstyle;
-};
-
-module.exports = styfn;
-
-},{"../is":77,"../util":94}],86:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var util = _dereq_('../util');
-var Selector = _dereq_('../selector');
-
-var Style = function( cy ){
-
- if( !(this instanceof Style) ){
- return new Style(cy);
- }
-
- if( !is.core(cy) ){
- util.error('A style must have a core reference');
- return;
- }
-
- this._private = {
- cy: cy,
- coreStyle: {},
- newStyle: true
- };
-
- this.length = 0;
-
- this.addDefaultStylesheet();
-};
-
-var styfn = Style.prototype;
-
-styfn.instanceString = function(){
- return 'style';
-};
-
-// remove all contexts
-styfn.clear = function(){
- for( var i = 0; i < this.length; i++ ){
- this[i] = undefined;
- }
- this.length = 0;
- this._private.newStyle = true;
-
- return this; // chaining
-};
-
-styfn.resetToDefault = function(){
- this.clear();
- this.addDefaultStylesheet();
-
- return this;
-};
-
-// builds a style object for the 'core' selector
-styfn.core = function(){
- return this._private.coreStyle;
-};
-
-// create a new context from the specified selector string and switch to that context
-styfn.selector = function( selectorStr ){
- // 'core' is a special case and does not need a selector
- var selector = selectorStr === 'core' ? null : new Selector( selectorStr );
-
- var i = this.length++; // new context means new index
- this[i] = {
- selector: selector,
- properties: [],
- mappedProperties: [],
- index: i
- };
-
- return this; // chaining
-};
-
-// add one or many css rules to the current context
-styfn.css = function(){
- var self = this;
- var args = arguments;
-
- switch( args.length ){
- case 1:
- var map = args[0];
-
- for( var i = 0; i < self.properties.length; i++ ){
- var prop = self.properties[i];
- var mapVal = map[ prop.name ];
-
- if( mapVal === undefined ){
- mapVal = map[ util.dash2camel(prop.name) ];
- }
-
- if( mapVal !== undefined ){
- this.cssRule( prop.name, mapVal );
- }
- }
-
- break;
-
- case 2:
- this.cssRule( args[0], args[1] );
- break;
-
- default:
- break; // do nothing if args are invalid
- }
-
- return this; // chaining
-};
-styfn.style = styfn.css;
-
-// add a single css rule to the current context
-styfn.cssRule = function( name, value ){
- // name-value pair
- var property = this.parse( name, value );
-
- // add property to current context if valid
- if( property ){
- var i = this.length - 1;
- this[i].properties.push( property );
- this[i].properties[ property.name ] = property; // allow access by name as well
-
- if( property.name.match(/pie-(\d+)-background-size/) && property.value ){
- this._private.hasPie = true;
- }
-
- if( property.mapped ){
- this[i].mappedProperties.push( property );
- }
-
- // add to core style if necessary
- var currentSelectorIsCore = !this[i].selector;
- if( currentSelectorIsCore ){
- this._private.coreStyle[ property.name ] = property;
- }
- }
-
- return this; // chaining
-};
-
-// static function
-Style.fromJson = function( cy, json ){
- var style = new Style( cy );
-
- style.fromJson( json );
-
- return style;
-};
-
-Style.fromString = function( cy, string ){
- return new Style( cy ).fromString( string );
-};
-
-[
- _dereq_('./apply'),
- _dereq_('./bypass'),
- _dereq_('./container'),
- _dereq_('./get-for-ele'),
- _dereq_('./json'),
- _dereq_('./string-sheet'),
- _dereq_('./properties'),
- _dereq_('./parse')
-].forEach(function( props ){
- util.extend( styfn, props );
-});
-
-
-Style.types = styfn.types;
-Style.properties = styfn.properties;
-
-module.exports = Style;
-
-},{"../is":77,"../selector":81,"../util":94,"./apply":82,"./bypass":83,"./container":84,"./get-for-ele":85,"./json":87,"./parse":88,"./properties":89,"./string-sheet":90}],87:[function(_dereq_,module,exports){
-'use strict';
-
-var styfn = {};
-
-styfn.applyFromJson = function( json ){
- var style = this;
-
- for( var i = 0; i < json.length; i++ ){
- var context = json[i];
- var selector = context.selector;
- var props = context.style || context.css;
-
- style.selector( selector ); // apply selector
-
- for( var name in props ){
- var value = props[name];
-
- style.css( name, value ); // apply property
- }
- }
-
- return style;
-};
-
-// accessible cy.style() function
-styfn.fromJson = function( json ){
- var style = this;
-
- style.resetToDefault();
- style.applyFromJson( json );
-
- return style;
-};
-
-// get json from cy.style() api
-styfn.json = function(){
- var json = [];
-
- for( var i = this.defaultLength; i < this.length; i++ ){
- var cxt = this[i];
- var selector = cxt.selector;
- var props = cxt.properties;
- var css = {};
-
- for( var j = 0; j < props.length; j++ ){
- var prop = props[j];
- css[ prop.name ] = prop.strValue;
- }
-
- json.push({
- selector: !selector ? 'core' : selector.toString(),
- style: css
- });
- }
-
- return json;
-};
-
-module.exports = styfn;
-
-},{}],88:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-var is = _dereq_('../is');
-
-var styfn = {};
-
-// a caching layer for property parsing
-styfn.parse = function( name, value, propIsBypass, propIsFlat ){
- var argHash = [ name, value, propIsBypass, propIsFlat ].join('$');
- var propCache = this.propCache = this.propCache || {};
- var ret;
- var impl = parseImpl.bind( this );
-
- if( !(ret = propCache[argHash]) ){
- ret = propCache[argHash] = impl( name, value, propIsBypass, propIsFlat );
- }
-
- // always need a copy since props are mutated later in their lifecycles
- ret = util.copy( ret );
-
- if( ret ){
- ret.value = util.copy( ret.value ); // because it could be an array, e.g. colour
- }
-
- return ret;
-};
-
-// parse a property; return null on invalid; return parsed property otherwise
-// fields :
-// - name : the name of the property
-// - value : the parsed, native-typed value of the property
-// - strValue : a string value that represents the property value in valid css
-// - bypass : true iff the property is a bypass property
-var parseImpl = function( name, value, propIsBypass, propIsFlat ){
- var self = this;
-
- name = util.camel2dash( name ); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
-
- var property = self.properties[ name ];
- var passedValue = value;
- var types = self.types;
-
- if( !property ){ return null; } // return null on property of unknown name
- if( value === undefined || value === null ){ return null; } // can't assign null
-
- // the property may be an alias
- if( property.alias ){
- property = property.pointsTo;
- name = property.name;
- }
-
- var valueIsString = is.string(value);
- if( valueIsString ){ // trim the value to make parsing easier
- value = value.trim();
- }
-
- var type = property.type;
- if( !type ){ return null; } // no type, no luck
-
- // check if bypass is null or empty string (i.e. indication to delete bypass property)
- if( propIsBypass && (value === '' || value === null) ){
- return {
- name: name,
- value: value,
- bypass: true,
- deleteBypass: true
- };
- }
-
- // check if value is a function used as a mapper
- if( is.fn(value) ){
- return {
- name: name,
- value: value,
- strValue: 'fn',
- mapped: types.fn,
- bypass: propIsBypass
- };
- }
-
- // check if value is mapped
- var data, mapData, layoutData, mapLayoutData, scratch, mapScratch;
- if( !valueIsString || propIsFlat ){
- // then don't bother to do the expensive regex checks
-
- } else if(
- ( data = new RegExp( types.data.regex ).exec( value ) ) ||
- ( layoutData = new RegExp( types.layoutData.regex ).exec( value ) ) ||
- ( scratch = new RegExp( types.scratch.regex ).exec( value ) )
- ){
- if( propIsBypass ){ return false; } // mappers not allowed in bypass
-
- var mapped;
- if( data ){
- mapped = types.data;
- } else if( layoutData ){
- mapped = types.layoutData;
- } else {
- mapped = types.scratch;
- }
-
- data = data || layoutData || scratch;
-
- return {
- name: name,
- value: data,
- strValue: '' + value,
- mapped: mapped,
- field: data[1],
- bypass: propIsBypass
- };
-
- } else if(
- ( mapData = new RegExp( types.mapData.regex ).exec( value ) ) ||
- ( mapLayoutData = new RegExp( types.mapLayoutData.regex ).exec( value ) ) ||
- ( mapScratch = new RegExp( types.mapScratch.regex ).exec( value ) )
- ){
- if( propIsBypass ){ return false; } // mappers not allowed in bypass
- if( type.multiple ){ return false; } // impossible to map to num
-
- var mapped;
- if( mapData ){
- mapped = types.mapData;
- } else if( mapLayoutData ){
- mapped = types.mapLayoutData;
- } else {
- mapped = types.mapScratch;
- }
-
- mapData = mapData || mapLayoutData || mapScratch;
-
- // we can map only if the type is a colour or a number
- if( !(type.color || type.number) ){ return false; }
-
- var valueMin = this.parse( name, mapData[4] ); // parse to validate
- if( !valueMin || valueMin.mapped ){ return false; } // can't be invalid or mapped
-
- var valueMax = this.parse( name, mapData[5] ); // parse to validate
- if( !valueMax || valueMax.mapped ){ return false; } // can't be invalid or mapped
-
- // check if valueMin and valueMax are the same
- if( valueMin.value === valueMax.value ){
- return false; // can't make much of a mapper without a range
-
- } else if( type.color ){
- var c1 = valueMin.value;
- var c2 = valueMax.value;
-
- var same = c1[0] === c2[0] // red
- && c1[1] === c2[1] // green
- && c1[2] === c2[2] // blue
- && ( // optional alpha
- c1[3] === c2[3] // same alpha outright
- || (
- (c1[3] == null || c1[3] === 1) // full opacity for colour 1?
- &&
- (c2[3] == null || c2[3] === 1) // full opacity for colour 2?
- )
- )
- ;
-
- if( same ){ return false; } // can't make a mapper without a range
- }
-
- return {
- name: name,
- value: mapData,
- strValue: '' + value,
- mapped: mapped,
- field: mapData[1],
- fieldMin: parseFloat( mapData[2] ), // min & max are numeric
- fieldMax: parseFloat( mapData[3] ),
- valueMin: valueMin.value,
- valueMax: valueMax.value,
- bypass: propIsBypass
- };
- }
-
- if( type.multiple && propIsFlat !== 'multiple' ){
- var vals;
-
- if( valueIsString ){
- vals = value.split(/\s+/);
- } else if( is.array(value) ){
- vals = value;
- } else {
- vals = [ value ];
- }
-
- if( type.evenMultiple && vals.length % 2 !== 0 ){ return null; }
-
- var valArr = vals.map(function( v ){
- var p = self.parse( name, v, propIsBypass, 'multiple' );
-
- if( p.pfValue != null ){
- return p.pfValue;
- } else {
- return p.value;
- }
- });
-
- return {
- name: name,
- value: valArr,
- pfValue: valArr,
- strValue: valArr.join(' '),
- bypass: propIsBypass,
- units: type.number && !type.unitless ? type.implicitUnits || 'px' : undefined
- };
- }
-
- // several types also allow enums
- var checkEnums = function(){
- for( var i = 0; i < type.enums.length; i++ ){
- var en = type.enums[i];
-
- if( en === value ){
- return {
- name: name,
- value: value,
- strValue: '' + value,
- bypass: propIsBypass
- };
- }
- }
-
- return null;
- };
-
- // check the type and return the appropriate object
- if( type.number ){
- var units;
- var implicitUnits = 'px'; // not set => px
-
- if( type.units ){ // use specified units if set
- units = type.units;
- }
-
- if( type.implicitUnits ){
- implicitUnits = type.implicitUnits;
- }
-
- if( !type.unitless ){
- if( valueIsString ){
- var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
- if( units ){ unitsRegex = units; } // only allow explicit units if so set
- var match = value.match( '^(' + util.regex.number + ')(' + unitsRegex + ')?' + '$' );
-
- if( match ){
- value = match[1];
- units = match[2] || implicitUnits;
- }
-
- } else if( !units || type.implicitUnits ) {
- units = implicitUnits; // implicitly px if unspecified
- }
- }
-
- value = parseFloat( value );
-
- // if not a number and enums not allowed, then the value is invalid
- if( isNaN(value) && type.enums === undefined ){
- return null;
- }
-
- // check if this number type also accepts special keywords in place of numbers
- // (i.e. `left`, `auto`, etc)
- if( isNaN(value) && type.enums !== undefined ){
- value = passedValue;
-
- return checkEnums();
- }
-
- // check if value must be an integer
- if( type.integer && !is.integer(value) ){
- return null;
- }
-
- // check value is within range
- if( (type.min !== undefined && value < type.min)
- || (type.max !== undefined && value > type.max)
- ){
- return null;
- }
-
- var ret = {
- name: name,
- value: value,
- strValue: '' + value + (units ? units : ''),
- units: units,
- bypass: propIsBypass
- };
-
- // normalise value in pixels
- if( type.unitless || (units !== 'px' && units !== 'em') ){
- ret.pfValue = value;
- } else {
- ret.pfValue = ( units === 'px' || !units ? (value) : (this.getEmSizeInPixels() * value) );
- }
-
- // normalise value in ms
- if( units === 'ms' || units === 's' ){
- ret.pfValue = units === 'ms' ? value : 1000 * value;
- }
-
- // normalise value in rad
- if( units === 'deg' || units === 'rad' ){
- ret.pfValue = units === 'rad' ? value : value * Math.PI/180;
- }
-
- return ret;
-
- } else if( type.propList ) {
-
- var props = [];
- var propsStr = '' + value;
-
- if( propsStr === 'none' ){
- // leave empty
-
- } else { // go over each prop
-
- var propsSplit = propsStr.split(',');
- for( var i = 0; i < propsSplit.length; i++ ){
- var propName = propsSplit[i].trim();
-
- if( self.properties[propName] ){
- props.push( propName );
- }
- }
-
- if( props.length === 0 ){ return null; }
- }
-
- return {
- name: name,
- value: props,
- strValue: props.length === 0 ? 'none' : props.join(', '),
- bypass: propIsBypass
- };
-
- } else if( type.color ){
- var tuple = util.color2tuple( value );
-
- if( !tuple ){ return null; }
-
- return {
- name: name,
- value: tuple,
- strValue: '' + value,
- bypass: propIsBypass,
- roundValue: true
- };
-
- } else if( type.regex || type.regexes ){
-
- // first check enums
- if( type.enums ){
- var enumProp = checkEnums();
-
- if( enumProp ){ return enumProp; }
- }
-
- var regexes = type.regexes ? type.regexes : [ type.regex ];
-
- for( var i = 0; i < regexes.length; i++ ){
- var regex = new RegExp( regexes[i] ); // make a regex from the type string
- var m = regex.exec( value );
-
- if( m ){ // regex matches
- return {
- name: name,
- value: m,
- strValue: '' + value,
- bypass: propIsBypass
- };
-
- }
- }
-
- return null; // didn't match any
-
- } else if( type.string ){
- // just return
- return {
- name: name,
- value: value,
- strValue: '' + value,
- bypass: propIsBypass
- };
-
- } else if( type.enums ){ // check enums last because it's a combo type in others
- return checkEnums();
-
- } else {
- return null; // not a type we can handle
- }
-
-};
-
-module.exports = styfn;
-
-},{"../is":77,"../util":94}],89:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-
-var styfn = {};
-
-(function(){
- var number = util.regex.number;
- var rgba = util.regex.rgbaNoBackRefs;
- var hsla = util.regex.hslaNoBackRefs;
- var hex3 = util.regex.hex3;
- var hex6 = util.regex.hex6;
- var data = function( prefix ){ return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$'; };
- var mapData = function( prefix ){
- var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3 + '|' + hex6;
- return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
- };
-
- // each visual style property has a type and needs to be validated according to it
- styfn.types = {
- time: { number: true, min: 0, units: 's|ms', implicitUnits: 'ms' },
- percent: { number: true, min: 0, max: 100, units: '%', implicitUnits: '%' },
- zeroOneNumber: { number: true, min: 0, max: 1, unitless: true },
- nOneOneNumber: { number: true, min: -1, max: 1, unitless: true },
- nonNegativeInt: { number: true, min: 0, integer: true, unitless: true },
- position: { enums: ['parent', 'origin'] },
- nodeSize: { number: true, min: 0, enums: ['auto', 'label'] },
- number: { number: true, unitless: true },
- numbers: { number: true, unitless: true, multiple: true },
- size: { number: true, min: 0 },
- bidirectionalSize: { number: true }, // allows negative
- bidirectionalSizes: { number: true, multiple: true }, // allows negative
- bgSize: { number: true, min: 0, allowPercent: true },
- bgWH: { number: true, min: 0, allowPercent: true, enums: ['auto'] },
- bgPos: { number: true, allowPercent: true },
- bgRepeat: { enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'] },
- bgFit: { enums: ['none', 'contain', 'cover'] },
- bgClip: { enums: ['none', 'node'] },
- color: { color: true },
- bool: { enums: ['yes', 'no'] },
- lineStyle: { enums: ['solid', 'dotted', 'dashed'] },
- borderStyle: { enums: ['solid', 'dotted', 'dashed', 'double'] },
- curveStyle: { enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments'] },
- fontFamily: { regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$' },
- fontVariant: { enums: ['small-caps', 'normal'] },
- fontStyle: { enums: ['italic', 'normal', 'oblique'] },
- fontWeight: { enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900] },
- textDecoration: { enums: ['none', 'underline', 'overline', 'line-through'] },
- textTransform: { enums: ['none', 'uppercase', 'lowercase'] },
- textWrap: { enums: ['none', 'wrap'] },
- textBackgroundShape: { enums: ['rectangle', 'roundrectangle']},
- nodeShape: { enums: ['rectangle', 'roundrectangle', 'ellipse', 'triangle', 'square', 'pentagon', 'hexagon', 'heptagon', 'octagon', 'star', 'diamond', 'vee', 'rhomboid', 'polygon'] },
- compoundIncludeLabels: { enums: ['include', 'exclude'] },
- arrowShape: { enums: ['tee', 'triangle', 'triangle-tee', 'triangle-backcurve', 'half-triangle-overshot', 'vee', 'square', 'circle', 'diamond', 'none'] },
- arrowFill: { enums: ['filled', 'hollow'] },
- display: { enums: ['element', 'none'] },
- visibility: { enums: ['hidden', 'visible'] },
- valign: { enums: ['top', 'center', 'bottom'] },
- halign: { enums: ['left', 'center', 'right'] },
- text: { string: true },
- data: { mapping: true, regex: data('data') },
- layoutData: { mapping: true, regex: data('layoutData') },
- scratch: { mapping: true, regex: data('scratch') },
- mapData: { mapping: true, regex: mapData('mapData') },
- mapLayoutData: { mapping: true, regex: mapData('mapLayoutData') },
- mapScratch: { mapping: true, regex: mapData('mapScratch') },
- fn: { mapping: true, fn: true },
- url: { regex: '^url\\s*\\(\\s*([^\\s]+)\\s*\\s*\\)|none|(.+)$' },
- propList: { propList: true },
- angle: { number: true, units: 'deg|rad', implicitUnits: 'rad' },
- textRotation: { enums: ['none', 'autorotate'] },
- polygonPointList: { number: true, multiple: true, evenMultiple: true, min: -1, max: 1, unitless: true },
- easing: {
- regexes: [
- '^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$',
- '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'
- ],
- enums: [
- 'linear',
- 'ease', 'ease-in', 'ease-out', 'ease-in-out',
- 'ease-in-sine', 'ease-out-sine', 'ease-in-out-sine',
- 'ease-in-quad', 'ease-out-quad', 'ease-in-out-quad',
- 'ease-in-cubic', 'ease-out-cubic', 'ease-in-out-cubic',
- 'ease-in-quart', 'ease-out-quart', 'ease-in-out-quart',
- 'ease-in-quint', 'ease-out-quint', 'ease-in-out-quint',
- 'ease-in-expo', 'ease-out-expo', 'ease-in-out-expo',
- 'ease-in-circ', 'ease-out-circ', 'ease-in-out-circ'
- ]
- }
- };
-
- // define visual style properties
- var t = styfn.types;
- var props = styfn.properties = [
- // labels
- { name: 'text-valign', type: t.valign },
- { name: 'text-halign', type: t.halign },
- { name: 'color', type: t.color },
- { name: 'label', type: t.text },
- { name: 'text-outline-color', type: t.color },
- { name: 'text-outline-width', type: t.size },
- { name: 'text-outline-opacity', type: t.zeroOneNumber },
- { name: 'text-opacity', type: t.zeroOneNumber },
- { name: 'text-background-color', type: t.color },
- { name: 'text-background-opacity', type: t.zeroOneNumber },
- { name: 'text-border-opacity', type: t.zeroOneNumber },
- { name: 'text-border-color', type: t.color },
- { name: 'text-border-width', type: t.size },
- { name: 'text-border-style', type: t.borderStyle },
- { name: 'text-background-shape', type: t.textBackgroundShape},
- // { name: 'text-decoration', type: t.textDecoration }, // not supported in canvas
- { name: 'text-transform', type: t.textTransform },
- { name: 'text-wrap', type: t.textWrap },
- { name: 'text-max-width', type: t.size },
- { name: 'text-events', type: t.bool },
-
- // { name: 'text-rotation', type: t.angle }, // TODO disabled b/c rotation breaks bounding boxes
- { name: 'font-family', type: t.fontFamily },
- { name: 'font-style', type: t.fontStyle },
- // { name: 'font-variant', type: t.fontVariant }, // not useful
- { name: 'font-weight', type: t.fontWeight },
- { name: 'font-size', type: t.size },
- { name: 'min-zoomed-font-size', type: t.size },
- { name: 'edge-text-rotation', type: t.textRotation },
-
- // behaviour
- { name: 'events', type: t.bool },
-
- // visibility
- { name: 'display', type: t.display },
- { name: 'visibility', type: t.visibility },
- { name: 'opacity', type: t.zeroOneNumber },
- { name: 'z-index', type: t.nonNegativeInt },
-
- // overlays
- { name: 'overlay-padding', type: t.size },
- { name: 'overlay-color', type: t.color },
- { name: 'overlay-opacity', type: t.zeroOneNumber },
-
- // shadows
- { name: 'shadow-blur', type: t.size },
- { name: 'shadow-color', type: t.color },
- { name: 'shadow-opacity', type: t.zeroOneNumber },
- { name: 'shadow-offset-x', type: t.bidirectionalSize },
- { name: 'shadow-offset-y', type: t.bidirectionalSize },
-
- // label shadows
- { name: 'text-shadow-blur', type: t.size },
- { name: 'text-shadow-color', type: t.color },
- { name: 'text-shadow-opacity', type: t.zeroOneNumber },
- { name: 'text-shadow-offset-x', type: t.bidirectionalSize },
- { name: 'text-shadow-offset-y', type: t.bidirectionalSize },
-
- // transition anis
- { name: 'transition-property', type: t.propList },
- { name: 'transition-duration', type: t.time },
- { name: 'transition-delay', type: t.time },
- { name: 'transition-timing-function', type: t.easing },
-
- // node body
- { name: 'height', type: t.nodeSize },
- { name: 'width', type: t.nodeSize },
- { name: 'shape', type: t.nodeShape },
- { name: 'shape-polygon-points', type: t.polygonPointList },
- { name: 'background-color', type: t.color },
- { name: 'background-opacity', type: t.zeroOneNumber },
- { name: 'background-blacken', type: t.nOneOneNumber },
- { name: 'padding-left', type: t.size },
- { name: 'padding-right', type: t.size },
- { name: 'padding-top', type: t.size },
- { name: 'padding-bottom', type: t.size },
-
- // node border
- { name: 'border-color', type: t.color },
- { name: 'border-opacity', type: t.zeroOneNumber },
- { name: 'border-width', type: t.size },
- { name: 'border-style', type: t.borderStyle },
-
- // node background images
- { name: 'background-image', type: t.url },
- { name: 'background-image-opacity', type: t.zeroOneNumber },
- { name: 'background-position-x', type: t.bgPos },
- { name: 'background-position-y', type: t.bgPos },
- { name: 'background-repeat', type: t.bgRepeat },
- { name: 'background-fit', type: t.bgFit },
- { name: 'background-clip', type: t.bgClip },
- { name: 'background-width', type: t.bgWH },
- { name: 'background-height', type: t.bgWH },
-
- // compound props
- { name: 'position', type: t.position },
- { name: 'compound-sizing-wrt-labels', type: t.compoundIncludeLabels },
-
- // edge line
- { name: 'line-style', type: t.lineStyle },
- { name: 'line-color', type: t.color },
- { name: 'curve-style', type: t.curveStyle },
- { name: 'haystack-radius', type: t.zeroOneNumber },
- { name: 'control-point-step-size', type: t.size },
- { name: 'control-point-distances', type: t.bidirectionalSizes },
- { name: 'control-point-weights', type: t.numbers },
- { name: 'segment-distances', type: t.bidirectionalSizes },
- { name: 'segment-weights', type: t.numbers },
-
- // these are just for the core
- { name: 'selection-box-color', type: t.color },
- { name: 'selection-box-opacity', type: t.zeroOneNumber },
- { name: 'selection-box-border-color', type: t.color },
- { name: 'selection-box-border-width', type: t.size },
- { name: 'active-bg-color', type: t.color },
- { name: 'active-bg-opacity', type: t.zeroOneNumber },
- { name: 'active-bg-size', type: t.size },
- { name: 'outside-texture-bg-color', type: t.color },
- { name: 'outside-texture-bg-opacity', type: t.zeroOneNumber }
- ];
-
- // define aliases
- var aliases = styfn.aliases = [
- { name: 'content', pointsTo: 'label' },
- { name: 'control-point-distance', pointsTo: 'control-point-distances' },
- { name: 'control-point-weight', pointsTo: 'control-point-weights' }
- ];
-
- // pie backgrounds for nodes
- styfn.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
- props.push({ name: 'pie-size', type: t.bgSize });
- for( var i = 1; i <= styfn.pieBackgroundN; i++ ){
- props.push({ name: 'pie-'+i+'-background-color', type: t.color });
- props.push({ name: 'pie-'+i+'-background-size', type: t.percent });
- props.push({ name: 'pie-'+i+'-background-opacity', type: t.zeroOneNumber });
- }
-
- // edge arrows
- var arrowPrefixes = styfn.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
- [
- { name: 'arrow-shape', type: t.arrowShape },
- { name: 'arrow-color', type: t.color },
- { name: 'arrow-fill', type: t.arrowFill }
- ].forEach(function( prop ){
- arrowPrefixes.forEach(function( prefix ){
- var name = prefix + '-' + prop.name;
- var type = prop.type;
-
- props.push({ name: name, type: type });
- });
- }, {});
-
- // list of property names
- styfn.propertyNames = props.map(function(p){ return p.name; });
-
- // allow access of properties by name ( e.g. style.properties.height )
- for( var i = 0; i < props.length; i++ ){
- var prop = props[i];
-
- props[ prop.name ] = prop; // allow lookup by name
- }
-
- // map aliases
- for( var i = 0; i < aliases.length; i++ ){
- var alias = aliases[i];
- var pointsToProp = props[ alias.pointsTo ];
- var aliasProp = {
- name: alias.name,
- alias: true,
- pointsTo: pointsToProp
- };
-
- // add alias prop for parsing
- props.push( aliasProp );
-
- props[ alias.name ] = aliasProp; // allow lookup by name
- }
-})();
-
-// adds the default stylesheet to the current style
-styfn.addDefaultStylesheet = function(){
- // fill the style with the default stylesheet
- this
- .selector('node, edge') // common properties
- .css( util.extend( {
- 'events': 'yes',
- 'text-events': 'no',
- 'text-valign': 'top',
- 'text-halign': 'center',
- 'color': '#000',
- 'text-outline-color': '#000',
- 'text-outline-width': 0,
- 'text-outline-opacity': 1,
- 'text-opacity': 1,
- 'text-decoration': 'none',
- 'text-transform': 'none',
- 'text-wrap': 'none',
- 'text-max-width': 9999,
- 'text-background-color': '#000',
- 'text-background-opacity': 0,
- 'text-border-opacity': 0,
- 'text-border-width': 0,
- 'text-border-style': 'solid',
- 'text-border-color':'#000',
- 'text-background-shape':'rectangle',
- 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
- 'font-style': 'normal',
- // 'font-variant': fontVariant,
- 'font-weight': 'normal',
- 'font-size': 16,
- 'min-zoomed-font-size': 0,
- 'edge-text-rotation': 'none',
- 'visibility': 'visible',
- 'display': 'element',
- 'opacity': 1,
- 'z-index': 0,
- 'label': '',
- 'overlay-opacity': 0,
- 'overlay-color': '#000',
- 'overlay-padding': 10,
- 'shadow-opacity': 0,
- 'shadow-color': '#000',
- 'shadow-blur': 10,
- 'shadow-offset-x': 0,
- 'shadow-offset-y': 0,
- 'text-shadow-opacity': 0,
- 'text-shadow-color': '#000',
- 'text-shadow-blur': 5,
- 'text-shadow-offset-x': 0,
- 'text-shadow-offset-y': 0,
- 'transition-property': 'none',
- 'transition-duration': 0,
- 'transition-delay': 0,
- 'transition-timing-function': 'linear',
-
- // node props
- 'background-blacken': 0,
- 'background-color': '#888',
- 'background-opacity': 1,
- 'background-image': 'none',
- 'background-image-opacity': 1,
- 'background-position-x': '50%',
- 'background-position-y': '50%',
- 'background-repeat': 'no-repeat',
- 'background-fit': 'none',
- 'background-clip': 'node',
- 'background-width': 'auto',
- 'background-height': 'auto',
- 'border-color': '#000',
- 'border-opacity': 1,
- 'border-width': 0,
- 'border-style': 'solid',
- 'height': 30,
- 'width': 30,
- 'shape': 'ellipse',
- 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
-
- // compound props
- 'padding-top': 0,
- 'padding-bottom': 0,
- 'padding-left': 0,
- 'padding-right': 0,
- 'position': 'origin',
- 'compound-sizing-wrt-labels': 'include'
- }, {
- // node pie bg
- 'pie-size': '100%'
- }, [
- { name: 'pie-{{i}}-background-color', value: 'black' },
- { name: 'pie-{{i}}-background-size', value: '0%' },
- { name: 'pie-{{i}}-background-opacity', value: 1 }
- ].reduce(function( css, prop ){
- for( var i = 1; i <= styfn.pieBackgroundN; i++ ){
- var name = prop.name.replace('{{i}}', i);
- var val = prop.value;
-
- css[ name ] = val;
- }
-
- return css;
- }, {}), {
- // edge props
- 'line-style': 'solid',
- 'line-color': '#ddd',
- 'control-point-step-size': 40,
- 'control-point-weights': 0.5,
- 'segment-weights': 0.5,
- 'segment-distances': 20,
- 'curve-style': 'bezier',
- 'haystack-radius': 0.8
- }, [
- { name: 'arrow-shape', value: 'none' },
- { name: 'arrow-color', value: '#ddd' },
- { name: 'arrow-fill', value: 'filled' }
- ].reduce(function( css, prop ){
- styfn.arrowPrefixes.forEach(function( prefix ){
- var name = prefix + '-' + prop.name;
- var val = prop.value;
-
- css[ name ] = val;
- });
-
- return css;
- }, {}) ) )
- .selector('$node > node') // compound (parent) node properties
- .css({
- 'width': 'auto',
- 'height': 'auto',
- 'shape': 'rectangle',
- 'padding-top': 10,
- 'padding-right': 10,
- 'padding-left': 10,
- 'padding-bottom': 10
- })
- .selector('edge') // just edge properties
- .css({
- 'width': 1
- })
- .selector(':active')
- .css({
- 'overlay-color': 'black',
- 'overlay-padding': 10,
- 'overlay-opacity': 0.25
- })
- .selector('core') // just core properties
- .css({
- 'selection-box-color': '#ddd',
- 'selection-box-opacity': 0.65,
- 'selection-box-border-color': '#aaa',
- 'selection-box-border-width': 1,
- 'active-bg-color': 'black',
- 'active-bg-opacity': 0.15,
- 'active-bg-size': 30,
- 'outside-texture-bg-color': '#000',
- 'outside-texture-bg-opacity': 0.125
- })
- ;
-
- this.defaultLength = this.length;
-};
-
-module.exports = styfn;
-
-},{"../util":94}],90:[function(_dereq_,module,exports){
-'use strict';
-
-var util = _dereq_('../util');
-var Selector = _dereq_('../selector');
-
-var styfn = {};
-
-styfn.applyFromString = function( string ){
- var self = this;
- var style = this;
- var remaining = '' + string;
- var selAndBlockStr;
- var blockRem;
- var propAndValStr;
-
- // remove comments from the style string
- remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
-
- function removeSelAndBlockFromRemaining(){
- // remove the parsed selector and block from the remaining text to parse
- if( remaining.length > selAndBlockStr.length ){
- remaining = remaining.substr( selAndBlockStr.length );
- } else {
- remaining = '';
- }
- }
-
- function removePropAndValFromRem(){
- // remove the parsed property and value from the remaining block text to parse
- if( blockRem.length > propAndValStr.length ){
- blockRem = blockRem.substr( propAndValStr.length );
- } else {
- blockRem = '';
- }
- }
-
- while(true){
- var nothingLeftToParse = remaining.match(/^\s*$/);
- if( nothingLeftToParse ){ break; }
-
- var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
-
- if( !selAndBlock ){
- util.error('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
- break;
- }
-
- selAndBlockStr = selAndBlock[0];
-
- // parse the selector
- var selectorStr = selAndBlock[1];
- if( selectorStr !== 'core' ){
- var selector = new Selector( selectorStr );
- if( selector._private.invalid ){
- util.error('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr);
-
- // skip this selector and block
- removeSelAndBlockFromRemaining();
- continue;
- }
- }
-
- // parse the block of properties and values
- var blockStr = selAndBlock[2];
- var invalidBlock = false;
- blockRem = blockStr;
- var props = [];
-
- while(true){
- var nothingLeftToParse = blockRem.match(/^\s*$/);
- if( nothingLeftToParse ){ break; }
-
- var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
-
- if( !propAndVal ){
- util.error('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
- invalidBlock = true;
- break;
- }
-
- propAndValStr = propAndVal[0];
- var propStr = propAndVal[1];
- var valStr = propAndVal[2];
-
- var prop = self.properties[ propStr ];
- if( !prop ){
- util.error('Skipping property: Invalid property name in: ' + propAndValStr);
-
- // skip this property in the block
- removePropAndValFromRem();
- continue;
- }
-
- var parsedProp = style.parse( propStr, valStr );
-
- if( !parsedProp ){
- util.error('Skipping property: Invalid property definition in: ' + propAndValStr);
-
- // skip this property in the block
- removePropAndValFromRem();
- continue;
- }
-
- props.push({
- name: propStr,
- val: valStr
- });
- removePropAndValFromRem();
- }
-
- if( invalidBlock ){
- removeSelAndBlockFromRemaining();
- break;
- }
-
- // put the parsed block in the style
- style.selector( selectorStr );
- for( var i = 0; i < props.length; i++ ){
- var prop = props[i];
- style.css( prop.name, prop.val );
- }
-
- removeSelAndBlockFromRemaining();
- }
-
- return style;
-};
-
-styfn.fromString = function( string ){
- var style = this;
-
- style.resetToDefault();
- style.applyFromString( string );
-
- return style;
-};
-
-module.exports = styfn;
-
-},{"../selector":81,"../util":94}],91:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('./is');
-var util = _dereq_('./util');
-var Style = _dereq_('./style');
-
-// a dummy stylesheet object that doesn't need a reference to the core
-// (useful for init)
-var Stylesheet = function(){
- if( !(this instanceof Stylesheet) ){
- return new Stylesheet();
- }
-
- this.length = 0;
-};
-
-var sheetfn = Stylesheet.prototype;
-
-sheetfn.instanceString = function(){
- return 'stylesheet';
-};
-
-// just store the selector to be parsed later
-sheetfn.selector = function( selector ){
- var i = this.length++;
-
- this[i] = {
- selector: selector,
- properties: []
- };
-
- return this; // chaining
-};
-
-// just store the property to be parsed later
-sheetfn.css = function( name, value ){
- var i = this.length - 1;
-
- if( is.string(name) ){
- this[i].properties.push({
- name: name,
- value: value
- });
- } else if( is.plainObject(name) ){
- var map = name;
-
- for( var j = 0; j < Style.properties.length; j++ ){
- var prop = Style.properties[j];
- var mapVal = map[ prop.name ];
-
- if( mapVal === undefined ){ // also try camel case name
- mapVal = map[ util.dash2camel(prop.name) ];
- }
-
- if( mapVal !== undefined ){
- var name = prop.name;
- var value = mapVal;
-
- this[i].properties.push({
- name: name,
- value: value
- });
- }
- }
- }
-
- return this; // chaining
-};
-
-sheetfn.style = sheetfn.css;
-
-// generate a real style object from the dummy stylesheet
-sheetfn.generateStyle = function( cy ){
- var style = new Style(cy);
-
- for( var i = 0; i < this.length; i++ ){
- var context = this[i];
- var selector = context.selector;
- var props = context.properties;
-
- style.selector(selector); // apply selector
-
- for( var j = 0; j < props.length; j++ ){
- var prop = props[j];
-
- style.css( prop.name, prop.value ); // apply property
- }
- }
-
- return style;
-};
-
-module.exports = Stylesheet;
-
-},{"./is":77,"./style":86,"./util":94}],92:[function(_dereq_,module,exports){
-// cross-env thread/worker
-// NB : uses (heavyweight) processes on nodejs so best not to create too many threads
-
-'use strict';
-
-var window = _dereq_('./window');
-var util = _dereq_('./util');
-var Promise = _dereq_('./promise');
-var Event = _dereq_('./event');
-var define = _dereq_('./define');
-var is = _dereq_('./is');
-
-var Thread = function( opts ){
- if( !(this instanceof Thread) ){
- return new Thread( opts );
- }
-
- var _p = this._private = {
- requires: [],
- files: [],
- queue: null,
- pass: [],
- disabled: false
- };
-
- if( is.plainObject(opts) ){
- if( opts.disabled != null ){
- _p.disabled = !!opts.disabled;
- }
- }
-
-};
-
-var thdfn = Thread.prototype; // short alias
-
-var stringifyFieldVal = function( val ){
- var valStr = is.fn( val ) ? val.toString() : 'JSON.parse("' + JSON.stringify(val) + '")';
-
- return valStr;
-};
-
-// allows for requires with prototypes and subobjs etc
-var fnAsRequire = function( fn ){
- var req;
- var fnName;
-
- if( is.object(fn) && fn.fn ){ // manual fn
- req = fnAs( fn.fn, fn.name );
- fnName = fn.name;
- fn = fn.fn;
- } else if( is.fn(fn) ){ // auto fn
- req = fn.toString();
- fnName = fn.name;
- } else if( is.string(fn) ){ // stringified fn
- req = fn;
- } else if( is.object(fn) ){ // plain object
- if( fn.proto ){
- req = '';
- } else {
- req = fn.name + ' = {};';
- }
-
- fnName = fn.name;
- fn = fn.obj;
- }
-
- req += '\n';
-
- var protoreq = function( val, subname ){
- if( val.prototype ){
- var protoNonempty = false;
- for( var prop in val.prototype ){ protoNonempty = true; break; } // jshint ignore:line
-
- if( protoNonempty ){
- req += fnAsRequire( {
- name: subname,
- obj: val,
- proto: true
- }, val );
- }
- }
- };
-
- // pull in prototype
- if( fn.prototype && fnName != null ){
-
- for( var name in fn.prototype ){
- var protoStr = '';
-
- var val = fn.prototype[ name ];
- var valStr = stringifyFieldVal( val );
- var subname = fnName + '.prototype.' + name;
-
- protoStr += subname + ' = ' + valStr + ';\n';
-
- if( protoStr ){
- req += protoStr;
- }
-
- protoreq( val, subname ); // subobject with prototype
- }
-
- }
-
- // pull in properties for obj/fns
- if( !is.string(fn) ){ for( var name in fn ){
- var propsStr = '';
-
- if( fn.hasOwnProperty(name) ){
- var val = fn[ name ];
- var valStr = stringifyFieldVal( val );
- var subname = fnName + '["' + name + '"]';
-
- propsStr += subname + ' = ' + valStr + ';\n';
- }
-
- if( propsStr ){
- req += propsStr;
- }
-
- protoreq( val, subname ); // subobject with prototype
- } }
-
- return req;
-};
-
-var isPathStr = function( str ){
- return is.string(str) && str.match(/\.js$/);
-};
-
-util.extend(thdfn, {
-
- instanceString: function(){ return 'thread'; },
-
- require: function( fn, as ){
- var requires = this._private.requires;
-
- if( isPathStr(fn) ){
- this._private.files.push( fn );
-
- return this;
- }
-
- if( as ){
- if( is.fn(fn) ){
- fn = { name: as, fn: fn };
- } else {
- fn = { name: as, obj: fn };
- }
- } else {
- if( is.fn(fn) ){
- if( !fn.name ){
- throw 'The function name could not be automatically determined. Use thread.require( someFunction, "someFunction" )';
- }
-
- fn = { name: fn.name, fn: fn };
- }
- }
-
- requires.push( fn );
-
- return this; // chaining
- },
-
- pass: function( data ){
- this._private.pass.push( data );
-
- return this; // chaining
- },
-
- run: function( fn, pass ){ // fn used like main()
- var self = this;
- var _p = this._private;
- pass = pass || _p.pass.shift();
-
- if( _p.stopped ){
- throw 'Attempted to run a stopped thread! Start a new thread or do not stop the existing thread and reuse it.';
- }
-
- if( _p.running ){
- return ( _p.queue = _p.queue.then(function(){ // inductive step
- return self.run( fn, pass );
- }) );
- }
-
- var useWW = window != null && !_p.disabled;
- var useNode = !window && typeof module !== 'undefined' && !_p.disabled;
-
- self.trigger('run');
-
- var runP = new Promise(function( resolve, reject ){
-
- _p.running = true;
-
- var threadTechAlreadyExists = _p.ran;
-
- var fnImplStr = is.string( fn ) ? fn : fn.toString();
-
- // worker code to exec
- var fnStr = '\n' + ( _p.requires.map(function( r ){
- return fnAsRequire( r );
- }) ).concat( _p.files.map(function( f ){
- if( useWW ){
- var wwifyFile = function( file ){
- if( file.match(/^\.\//) || file.match(/^\.\./) ){
- return window.location.origin + window.location.pathname + file;
- } else if( file.match(/^\//) ){
- return window.location.origin + '/' + file;
- }
- return file;
- };
-
- return 'importScripts("' + wwifyFile(f) + '");';
- } else if( useNode ) {
- return 'eval( require("fs").readFileSync("' + f + '", { encoding: "utf8" }) );';
- } else {
- throw 'External file `' + f + '` can not be required without any threading technology.';
- }
- }) ).concat([
- '( function(){',
- 'var ret = (' + fnImplStr + ')(' + JSON.stringify(pass) + ');',
- 'if( ret !== undefined ){ resolve(ret); }', // assume if ran fn returns defined value (incl. null), that we want to resolve to it
- '} )()\n'
- ]).join('\n');
-
- // because we've now consumed the requires, empty the list so we don't dupe on next run()
- _p.requires = [];
- _p.files = [];
-
- if( useWW ){
- var fnBlob, fnUrl;
-
- // add normalised thread api functions
- if( !threadTechAlreadyExists ){
- var fnPre = fnStr + '';
-
- fnStr = [
- 'function _ref_(o){ return eval(o); };',
- 'function broadcast(m){ return message(m); };', // alias
- 'function message(m){ postMessage(m); };',
- 'function listen(fn){',
- ' self.addEventListener("message", function(m){ ',
- ' if( typeof m === "object" && (m.data.$$eval || m.data === "$$start") ){',
- ' } else { ',
- ' fn( m.data );',
- ' }',
- ' });',
- '};',
- 'self.addEventListener("message", function(m){ if( m.data.$$eval ){ eval( m.data.$$eval ); } });',
- 'function resolve(v){ postMessage({ $$resolve: v }); };',
- 'function reject(v){ postMessage({ $$reject: v }); };'
- ].join('\n');
-
- fnStr += fnPre;
-
- fnBlob = new Blob([ fnStr ], {
- type: 'application/javascript'
- });
- fnUrl = window.URL.createObjectURL( fnBlob );
- }
- // create webworker and let it exec the serialised code
- var ww = _p.webworker = _p.webworker || new Worker( fnUrl );
-
- if( threadTechAlreadyExists ){ // then just exec new run() code
- ww.postMessage({
- $$eval: fnStr
- });
- }
-
- // worker messages => events
- var cb;
- ww.addEventListener('message', cb = function( m ){
- var isObject = is.object(m) && is.object( m.data );
-
- if( isObject && ('$$resolve' in m.data) ){
- ww.removeEventListener('message', cb); // done listening b/c resolve()
-
- resolve( m.data.$$resolve );
- } else if( isObject && ('$$reject' in m.data) ){
- ww.removeEventListener('message', cb); // done listening b/c reject()
-
- reject( m.data.$$reject );
- } else {
- self.trigger( new Event(m, { type: 'message', message: m.data }) );
- }
- }, false);
-
- if( !threadTechAlreadyExists ){
- ww.postMessage('$$start'); // start up the worker
- }
-
- } else if( useNode ){
- // create a new process
-
- if( !_p.child ){
- _p.child = ( _dereq_('child_process').fork( _dereq_('path').join(__dirname, 'thread-node-fork') ) );
- }
-
- var child = _p.child;
-
- // child process messages => events
- var cb;
- child.on('message', cb = function( m ){
- if( is.object(m) && ('$$resolve' in m) ){
- child.removeListener('message', cb); // done listening b/c resolve()
-
- resolve( m.$$resolve );
- } else if( is.object(m) && ('$$reject' in m) ){
- child.removeListener('message', cb); // done listening b/c reject()
-
- reject( m.$$reject );
- } else {
- self.trigger( new Event({}, { type: 'message', message: m }) );
- }
- });
-
- // ask the child process to eval the worker code
- child.send({
- $$eval: fnStr
- });
-
- } else { // use a fallback mechanism using a timeout
-
- var promiseResolve = resolve;
- var promiseReject = reject;
-
- var timer = _p.timer = _p.timer || {
-
- listeners: [],
-
- exec: function(){
- // as a string so it can't be mangled by minifiers and processors
- fnStr = [
- 'function _ref_(o){ return eval(o); };',
- 'function broadcast(m){ return message(m); };',
- 'function message(m){ self.trigger( new Event({}, { type: "message", message: m }) ); };',
- 'function listen(fn){ timer.listeners.push( fn ); };',
- 'function resolve(v){ promiseResolve(v); };',
- 'function reject(v){ promiseReject(v); };'
- ].join('\n') + fnStr;
-
- // the .run() code
- eval( fnStr ); // jshint ignore:line
- },
-
- message: function( m ){
- var ls = timer.listeners;
-
- for( var i = 0; i < ls.length; i++ ){
- var fn = ls[i];
-
- fn( m );
- }
- }
-
- };
-
- timer.exec();
- }
-
- }).then(function( v ){
- _p.running = false;
- _p.ran = true;
-
- self.trigger('ran');
-
- return v;
- });
-
- if( _p.queue == null ){
- _p.queue = runP; // i.e. first step of inductive promise chain (for queue)
- }
-
- return runP;
- },
-
- // send the thread a message
- message: function( m ){
- var _p = this._private;
-
- if( _p.webworker ){
- _p.webworker.postMessage( m );
- }
-
- if( _p.child ){
- _p.child.send( m );
- }
-
- if( _p.timer ){
- _p.timer.message( m );
- }
-
- return this; // chaining
- },
-
- stop: function(){
- var _p = this._private;
-
- if( _p.webworker ){
- _p.webworker.terminate();
- }
-
- if( _p.child ){
- _p.child.kill();
- }
-
- if( _p.timer ){
- // nothing we can do if we've run a timeout
- }
-
- _p.stopped = true;
-
- return this.trigger('stop'); // chaining
- },
-
- stopped: function(){
- return this._private.stopped;
- }
-
-});
-
-// turns a stringified function into a (re)named function
-var fnAs = function( fn, name ){
- var fnStr = fn.toString();
- fnStr = fnStr.replace(/function\s*?\S*?\s*?\(/, 'function ' + name + '(');
-
- return fnStr;
-};
-
-var defineFnal = function( opts ){
- opts = opts || {};
-
- return function fnalImpl( fn, arg1 ){
- var fnStr = fnAs( fn, '_$_$_' + opts.name );
-
- this.require( fnStr );
-
- return this.run( [
- 'function( data ){',
- ' var origResolve = resolve;',
- ' var res = [];',
- ' ',
- ' resolve = function( val ){',
- ' res.push( val );',
- ' };',
- ' ',
- ' var ret = data.' + opts.name + '( _$_$_' + opts.name + ( arguments.length > 1 ? ', ' + JSON.stringify(arg1) : '' ) + ' );',
- ' ',
- ' resolve = origResolve;',
- ' resolve( res.length > 0 ? res : ret );',
- '}'
- ].join('\n') );
- };
-};
-
-util.extend(thdfn, {
- reduce: defineFnal({ name: 'reduce' }),
-
- reduceRight: defineFnal({ name: 'reduceRight' }),
-
- map: defineFnal({ name: 'map' })
-});
-
-// aliases
-var fn = thdfn;
-fn.promise = fn.run;
-fn.terminate = fn.halt = fn.stop;
-fn.include = fn.require;
-
-// pull in event apis
-util.extend(thdfn, {
- on: define.on(),
- one: define.on({ unbindSelfOnTrigger: true }),
- off: define.off(),
- trigger: define.trigger()
-});
-
-define.eventAliasesOn( thdfn );
-
-module.exports = Thread;
-
-},{"./define":41,"./event":42,"./is":77,"./promise":80,"./util":94,"./window":100,"child_process":undefined,"path":undefined}],93:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-
-module.exports = {
- // get [r, g, b] from #abc or #aabbcc
- hex2tuple: function( hex ){
- if( !(hex.length === 4 || hex.length === 7) || hex[0] !== "#" ){ return; }
-
- var shortHex = hex.length === 4;
- var r, g, b;
- var base = 16;
-
- if( shortHex ){
- r = parseInt( hex[1] + hex[1], base );
- g = parseInt( hex[2] + hex[2], base );
- b = parseInt( hex[3] + hex[3], base );
- } else {
- r = parseInt( hex[1] + hex[2], base );
- g = parseInt( hex[3] + hex[4], base );
- b = parseInt( hex[5] + hex[6], base );
- }
-
- return [r, g, b];
- },
-
- // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
- hsl2tuple: function( hsl ){
- var ret;
- var h, s, l, a, r, g, b;
- function hue2rgb(p, q, t){
- if(t < 0) t += 1;
- if(t > 1) t -= 1;
- if(t < 1/6) return p + (q - p) * 6 * t;
- if(t < 1/2) return q;
- if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
- }
-
- var m = new RegExp("^" + this.regex.hsla + "$").exec(hsl);
- if( m ){
-
- // get hue
- h = parseInt( m[1] );
- if( h < 0 ){
- h = ( 360 - (-1*h % 360) ) % 360;
- } else if( h > 360 ){
- h = h % 360;
- }
- h /= 360; // normalise on [0, 1]
-
- s = parseFloat( m[2] );
- if( s < 0 || s > 100 ){ return; } // saturation is [0, 100]
- s = s/100; // normalise on [0, 1]
-
- l = parseFloat( m[3] );
- if( l < 0 || l > 100 ){ return; } // lightness is [0, 100]
- l = l/100; // normalise on [0, 1]
-
- a = m[4];
- if( a !== undefined ){
- a = parseFloat( a );
-
- if( a < 0 || a > 1 ){ return; } // alpha is [0, 1]
- }
-
- // now, convert to rgb
- // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
- if( s === 0 ){
- r = g = b = Math.round(l * 255); // achromatic
- } else {
- var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- var p = 2 * l - q;
- r = Math.round( 255 * hue2rgb(p, q, h + 1/3) );
- g = Math.round( 255 * hue2rgb(p, q, h) );
- b = Math.round( 255 * hue2rgb(p, q, h - 1/3) );
- }
-
- ret = [r, g, b, a];
- }
-
- return ret;
- },
-
- // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
- rgb2tuple: function( rgb ){
- var ret;
-
- var m = new RegExp("^" + this.regex.rgba + "$").exec(rgb);
- if( m ){
- ret = [];
-
- var isPct = [];
- for( var i = 1; i <= 3; i++ ){
- var channel = m[i];
-
- if( channel[ channel.length - 1 ] === "%" ){
- isPct[i] = true;
- }
- channel = parseFloat( channel );
-
- if( isPct[i] ){
- channel = channel/100 * 255; // normalise to [0, 255]
- }
-
- if( channel < 0 || channel > 255 ){ return; } // invalid channel value
-
- ret.push( Math.floor(channel) );
- }
-
- var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
- var allArePct = isPct[1] && isPct[2] && isPct[3];
- if( atLeastOneIsPct && !allArePct ){ return; } // must all be percent values if one is
-
- var alpha = m[4];
- if( alpha !== undefined ){
- alpha = parseFloat( alpha );
-
- if( alpha < 0 || alpha > 1 ){ return; } // invalid alpha value
-
- ret.push( alpha );
- }
- }
-
- return ret;
- },
-
- colorname2tuple: function( color ){
- return this.colors[ color.toLowerCase() ];
- },
-
- color2tuple: function( color ){
- return ( is.array(color) ? color : null )
- || this.colorname2tuple(color)
- || this.hex2tuple(color)
- || this.rgb2tuple(color)
- || this.hsl2tuple(color);
- },
-
- colors: {
- // special colour names
- transparent: [0, 0, 0, 0], // NB alpha === 0
-
- // regular colours
- aliceblue: [240, 248, 255],
- antiquewhite: [250, 235, 215],
- aqua: [0, 255, 255],
- aquamarine: [127, 255, 212],
- azure: [240, 255, 255],
- beige: [245, 245, 220],
- bisque: [255, 228, 196],
- black: [0, 0, 0],
- blanchedalmond: [255, 235, 205],
- blue: [0, 0, 255],
- blueviolet: [138, 43, 226],
- brown: [165, 42, 42],
- burlywood: [222, 184, 135],
- cadetblue: [95, 158, 160],
- chartreuse: [127, 255, 0],
- chocolate: [210, 105, 30],
- coral: [255, 127, 80],
- cornflowerblue: [100, 149, 237],
- cornsilk: [255, 248, 220],
- crimson: [220, 20, 60],
- cyan: [0, 255, 255],
- darkblue: [0, 0, 139],
- darkcyan: [0, 139, 139],
- darkgoldenrod: [184, 134, 11],
- darkgray: [169, 169, 169],
- darkgreen: [0, 100, 0],
- darkgrey: [169, 169, 169],
- darkkhaki: [189, 183, 107],
- darkmagenta: [139, 0, 139],
- darkolivegreen: [85, 107, 47],
- darkorange: [255, 140, 0],
- darkorchid: [153, 50, 204],
- darkred: [139, 0, 0],
- darksalmon: [233, 150, 122],
- darkseagreen: [143, 188, 143],
- darkslateblue: [72, 61, 139],
- darkslategray: [47, 79, 79],
- darkslategrey: [47, 79, 79],
- darkturquoise: [0, 206, 209],
- darkviolet: [148, 0, 211],
- deeppink: [255, 20, 147],
- deepskyblue: [0, 191, 255],
- dimgray: [105, 105, 105],
- dimgrey: [105, 105, 105],
- dodgerblue: [30, 144, 255],
- firebrick: [178, 34, 34],
- floralwhite: [255, 250, 240],
- forestgreen: [34, 139, 34],
- fuchsia: [255, 0, 255],
- gainsboro: [220, 220, 220],
- ghostwhite: [248, 248, 255],
- gold: [255, 215, 0],
- goldenrod: [218, 165, 32],
- gray: [128, 128, 128],
- grey: [128, 128, 128],
- green: [0, 128, 0],
- greenyellow: [173, 255, 47],
- honeydew: [240, 255, 240],
- hotpink: [255, 105, 180],
- indianred: [205, 92, 92],
- indigo: [75, 0, 130],
- ivory: [255, 255, 240],
- khaki: [240, 230, 140],
- lavender: [230, 230, 250],
- lavenderblush: [255, 240, 245],
- lawngreen: [124, 252, 0],
- lemonchiffon: [255, 250, 205],
- lightblue: [173, 216, 230],
- lightcoral: [240, 128, 128],
- lightcyan: [224, 255, 255],
- lightgoldenrodyellow: [250, 250, 210],
- lightgray: [211, 211, 211],
- lightgreen: [144, 238, 144],
- lightgrey: [211, 211, 211],
- lightpink: [255, 182, 193],
- lightsalmon: [255, 160, 122],
- lightseagreen: [32, 178, 170],
- lightskyblue: [135, 206, 250],
- lightslategray: [119, 136, 153],
- lightslategrey: [119, 136, 153],
- lightsteelblue: [176, 196, 222],
- lightyellow: [255, 255, 224],
- lime: [0, 255, 0],
- limegreen: [50, 205, 50],
- linen: [250, 240, 230],
- magenta: [255, 0, 255],
- maroon: [128, 0, 0],
- mediumaquamarine: [102, 205, 170],
- mediumblue: [0, 0, 205],
- mediumorchid: [186, 85, 211],
- mediumpurple: [147, 112, 219],
- mediumseagreen: [60, 179, 113],
- mediumslateblue: [123, 104, 238],
- mediumspringgreen: [0, 250, 154],
- mediumturquoise: [72, 209, 204],
- mediumvioletred: [199, 21, 133],
- midnightblue: [25, 25, 112],
- mintcream: [245, 255, 250],
- mistyrose: [255, 228, 225],
- moccasin: [255, 228, 181],
- navajowhite: [255, 222, 173],
- navy: [0, 0, 128],
- oldlace: [253, 245, 230],
- olive: [128, 128, 0],
- olivedrab: [107, 142, 35],
- orange: [255, 165, 0],
- orangered: [255, 69, 0],
- orchid: [218, 112, 214],
- palegoldenrod: [238, 232, 170],
- palegreen: [152, 251, 152],
- paleturquoise: [175, 238, 238],
- palevioletred: [219, 112, 147],
- papayawhip: [255, 239, 213],
- peachpuff: [255, 218, 185],
- peru: [205, 133, 63],
- pink: [255, 192, 203],
- plum: [221, 160, 221],
- powderblue: [176, 224, 230],
- purple: [128, 0, 128],
- red: [255, 0, 0],
- rosybrown: [188, 143, 143],
- royalblue: [65, 105, 225],
- saddlebrown: [139, 69, 19],
- salmon: [250, 128, 114],
- sandybrown: [244, 164, 96],
- seagreen: [46, 139, 87],
- seashell: [255, 245, 238],
- sienna: [160, 82, 45],
- silver: [192, 192, 192],
- skyblue: [135, 206, 235],
- slateblue: [106, 90, 205],
- slategray: [112, 128, 144],
- slategrey: [112, 128, 144],
- snow: [255, 250, 250],
- springgreen: [0, 255, 127],
- steelblue: [70, 130, 180],
- tan: [210, 180, 140],
- teal: [0, 128, 128],
- thistle: [216, 191, 216],
- tomato: [255, 99, 71],
- turquoise: [64, 224, 208],
- violet: [238, 130, 238],
- wheat: [245, 222, 179],
- white: [255, 255, 255],
- whitesmoke: [245, 245, 245],
- yellow: [255, 255, 0],
- yellowgreen: [154, 205, 50]
- }
-};
-
-},{"../is":77}],94:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-var math = _dereq_('../math');
-
-var util = {
-
- falsify: function(){ return false; },
-
- zeroify: function(){ return 0; },
-
- noop: function(){},
-
- /* jshint ignore:start */
- error: function( msg ){
- if( console.error ){
- console.error.apply( console, arguments );
-
- if( console.trace ){ console.trace(); }
- } else {
- console.log.apply( console, arguments );
-
- if( console.trace ){ console.trace(); }
- }
- },
- /* jshint ignore:end */
-
- clone: function( obj ){
- return this.extend( {}, obj );
- },
-
- // gets a shallow copy of the argument
- copy: function( obj ){
- if( obj == null ){
- return obj;
- } if( is.array(obj) ){
- return obj.slice();
- } else if( is.plainObject(obj) ){
- return this.clone( obj );
- } else {
- return obj;
- }
- }
-
-};
-
-util.makeBoundingBox = math.makeBoundingBox.bind( math );
-
-util._staticEmptyObject = {};
-
-util.staticEmptyObject = function(){
- return util._staticEmptyObject;
-};
-
-util.extend = Object.assign != null ? Object.assign : function( tgt ){
- var args = arguments;
-
- for( var i = 1; i < args.length; i++ ){
- var obj = args[i];
-
- for( var k in obj ){
- tgt[k] = obj[k];
- }
- }
-
- return tgt;
-};
-
-[
- _dereq_('./colors'),
- _dereq_('./maps'),
- { memoize: _dereq_('./memoize') },
- _dereq_('./regex'),
- _dereq_('./strings'),
- _dereq_('./timing')
-].forEach(function( req ){
- util.extend( util, req );
-});
-
-module.exports = util;
-
-},{"../is":77,"../math":79,"./colors":93,"./maps":95,"./memoize":96,"./regex":97,"./strings":98,"./timing":99}],95:[function(_dereq_,module,exports){
-'use strict';
-
-var is = _dereq_('../is');
-
-module.exports = {
- // has anything been set in the map
- mapEmpty: function( map ){
- var empty = true;
-
- if( map != null ){
- for(var i in map){ // jshint ignore:line
- empty = false;
- break;
- }
- }
-
- return empty;
- },
-
- // pushes to the array at the end of a map (map may not be built)
- pushMap: function( options ){
- var array = this.getMap(options);
-
- if( array == null ){ // if empty, put initial array
- this.setMap( this.extend({}, options, {
- value: [ options.value ]
- }) );
- } else {
- array.push( options.value );
- }
- },
-
- // sets the value in a map (map may not be built)
- setMap: function( options ){
- var obj = options.map;
- var key;
- var keys = options.keys;
- var l = keys.length;
-
- for(var i = 0; i < l; i++){
- var key = keys[i];
-
- if( is.plainObject( key ) ){
- this.error('Tried to set map with object key');
- }
-
- if( i < keys.length - 1 ){
-
- // extend the map if necessary
- if( obj[key] == null ){
- obj[key] = {};
- }
-
- obj = obj[key];
- } else {
- // set the value
- obj[key] = options.value;
- }
- }
- },
-
- // gets the value in a map even if it's not built in places
- getMap: function( options ){
- var obj = options.map;
- var keys = options.keys;
- var l = keys.length;
-
- for(var i = 0; i < l; i++){
- var key = keys[i];
-
- if( is.plainObject( key ) ){
- this.error('Tried to get map with object key');
- }
-
- obj = obj[key];
-
- if( obj == null ){
- return obj;
- }
- }
-
- return obj;
- },
-
- // deletes the entry in the map
- deleteMap: function( options ){
- var obj = options.map;
- var keys = options.keys;
- var l = keys.length;
- var keepChildren = options.keepChildren;
-
- for(var i = 0; i < l; i++){
- var key = keys[i];
-
- if( is.plainObject( key ) ){
- this.error('Tried to delete map with object key');
- }
-
- var lastKey = i === options.keys.length - 1;
- if( lastKey ){
-
- if( keepChildren ){ // then only delete child fields not in keepChildren
- for( var child in obj ){
- if( !keepChildren[child] ){
- obj[child] = undefined;
- }
- }
- } else {
- obj[key] = undefined;
- }
-
- } else {
- obj = obj[key];
- }
- }
- }
-};
-
-},{"../is":77}],96:[function(_dereq_,module,exports){
-'use strict';
-
-module.exports = function memoize( fn, keyFn ){
- var self = this;
- var cache = {};
-
- if( !keyFn ){
- keyFn = function(){
- if( arguments.length === 1 ){
- return arguments[0];
- }
-
- var args = [];
-
- for( var i = 0; i < arguments.length; i++ ){
- args.push( arguments[i] );
- }
-
- return args.join('$');
- };
- }
-
- return function memoizedFn(){
- var args = arguments;
- var ret;
- var k = keyFn.apply( self, args );
-
- if( !(ret = cache[k]) ){
- ret = cache[k] = fn.apply( self, args );
- }
-
- return ret;
- };
-};
-
-},{}],97:[function(_dereq_,module,exports){
-'use strict';
-
-var number = "(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))";
-
-var rgba = "rgb[a]?\\(("+ number +"[%]?)\\s*,\\s*("+ number +"[%]?)\\s*,\\s*("+ number +"[%]?)(?:\\s*,\\s*("+ number +"))?\\)";
-var rgbaNoBackRefs = "rgb[a]?\\((?:"+ number +"[%]?)\\s*,\\s*(?:"+ number +"[%]?)\\s*,\\s*(?:"+ number +"[%]?)(?:\\s*,\\s*(?:"+ number +"))?\\)";
-
-var hsla = "hsl[a]?\\(("+ number +")\\s*,\\s*("+ number +"[%])\\s*,\\s*("+ number +"[%])(?:\\s*,\\s*("+ number +"))?\\)";
-var hslaNoBackRefs = "hsl[a]?\\((?:"+ number +")\\s*,\\s*(?:"+ number +"[%])\\s*,\\s*(?:"+ number +"[%])(?:\\s*,\\s*(?:"+ number +"))?\\)";
-
-var hex3 = "\\#[0-9a-fA-F]{3}";
-var hex6 = "\\#[0-9a-fA-F]{6}";
-
-module.exports = {
- regex: {
- number: number,
- rgba: rgba,
- rgbaNoBackRefs: rgbaNoBackRefs,
- hsla: hsla,
- hslaNoBackRefs: hslaNoBackRefs,
- hex3: hex3,
- hex6: hex6
- }
-};
-
-},{}],98:[function(_dereq_,module,exports){
-'use strict';
-
-var memoize = _dereq_('./memoize');
-var is = _dereq_('../is');
-
-module.exports = {
-
- camel2dash: memoize( function( str ){
- return str.replace(/([A-Z])/g, function( v ){
- return '-' + v.toLowerCase();
- });
- } ),
-
- dash2camel: memoize( function( str ){
- return str.replace(/(-\w)/g, function( v ){
- return v[1].toUpperCase();
- });
- } ),
-
- capitalize: function(str){
- if( is.emptyString(str) ){
- return str;
- }
-
- return str.charAt(0).toUpperCase() + str.substring(1);
- }
-
-};
-
-},{"../is":77,"./memoize":96}],99:[function(_dereq_,module,exports){
-'use strict';
-
-var window = _dereq_('../window');
-var is = _dereq_('../is');
-var performance = window ? window.performance : null;
-
-var util = {};
-
-var raf = !window ? null : ( window.requestAnimationFrame || window.mozRequestAnimationFrame ||
- window.webkitRequestAnimationFrame || window.msRequestAnimationFrame );
-
-raf = raf || function( fn ){
- if( fn ){
- setTimeout(function(){
- fn( pnow() );
- }, 1000/60);
- }
-};
-
-util.requestAnimationFrame = function(fn){
- raf( fn );
-};
-
-var pnow = performance && performance.now ? function(){ return performance.now(); } : function(){ return Date.now(); };
-
-util.performanceNow = pnow;
-
-// ported lodash throttle function
-util.throttle = function(func, wait, options) {
- var leading = true,
- trailing = true;
-
- if (options === false) {
- leading = false;
- } else if (is.plainObject(options)) {
- leading = 'leading' in options ? options.leading : leading;
- trailing = 'trailing' in options ? options.trailing : trailing;
- }
- options = options || {};
- options.leading = leading;
- options.maxWait = wait;
- options.trailing = trailing;
-
- return util.debounce(func, wait, options);
-};
-
-util.now = function(){
- return Date.now();
-};
-
-util.debounce = function(func, wait, options) { // ported lodash debounce function
- var util = this;
- var args,
- maxTimeoutId,
- result,
- stamp,
- thisArg,
- timeoutId,
- trailingCall,
- lastCalled = 0,
- maxWait = false,
- trailing = true;
-
- if (!is.fn(func)) {
- return;
- }
- wait = Math.max(0, wait) || 0;
- if (options === true) {
- var leading = true;
- trailing = false;
- } else if (is.plainObject(options)) {
- leading = options.leading;
- maxWait = 'maxWait' in options && (Math.max(wait, options.maxWait) || 0);
- trailing = 'trailing' in options ? options.trailing : trailing;
- }
- var delayed = function() {
- var remaining = wait - (util.now() - stamp);
- if (remaining <= 0) {
- if (maxTimeoutId) {
- clearTimeout(maxTimeoutId);
- }
- var isCalled = trailingCall;
- maxTimeoutId = timeoutId = trailingCall = undefined;
- if (isCalled) {
- lastCalled = util.now();
- result = func.apply(thisArg, args);
- if (!timeoutId && !maxTimeoutId) {
- args = thisArg = null;
- }
- }
- } else {
- timeoutId = setTimeout(delayed, remaining);
- }
- };
-
- var maxDelayed = function() {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- maxTimeoutId = timeoutId = trailingCall = undefined;
- if (trailing || (maxWait !== wait)) {
- lastCalled = util.now();
- result = func.apply(thisArg, args);
- if (!timeoutId && !maxTimeoutId) {
- args = thisArg = null;
- }
- }
- };
-
- return function() {
- args = arguments;
- stamp = util.now();
- thisArg = this;
- trailingCall = trailing && (timeoutId || !leading);
-
- if (maxWait === false) {
- var leadingCall = leading && !timeoutId;
- } else {
- if (!maxTimeoutId && !leading) {
- lastCalled = stamp;
- }
- var remaining = maxWait - (stamp - lastCalled),
- isCalled = remaining <= 0;
-
- if (isCalled) {
- if (maxTimeoutId) {
- maxTimeoutId = clearTimeout(maxTimeoutId);
- }
- lastCalled = stamp;
- result = func.apply(thisArg, args);
- }
- else if (!maxTimeoutId) {
- maxTimeoutId = setTimeout(maxDelayed, remaining);
- }
- }
- if (isCalled && timeoutId) {
- timeoutId = clearTimeout(timeoutId);
- }
- else if (!timeoutId && wait !== maxWait) {
- timeoutId = setTimeout(delayed, wait);
- }
- if (leadingCall) {
- isCalled = true;
- result = func.apply(thisArg, args);
- }
- if (isCalled && !timeoutId && !maxTimeoutId) {
- args = thisArg = null;
- }
- return result;
- };
-};
-
-module.exports = util;
-
-},{"../is":77,"../window":100}],100:[function(_dereq_,module,exports){
-module.exports = ( typeof window === 'undefined' ? null : window );
-
-},{}]},{},[76])(76)
-});
-
-
-//# sourceMappingURL=cytoscape.js.map
diff --git a/benchmark/suite/index.js b/benchmark/suite/index.js
index fb0041df83..c0f92d3425 100644
--- a/benchmark/suite/index.js
+++ b/benchmark/suite/index.js
@@ -6,17 +6,17 @@ global.newCytoscape = newCytoscape;
global.oldCytoscape = oldCytoscape;
function Suite( name, suiteOpts ){
+ suiteOpts = suiteOpts || {};
+
var suite = new Benchmark.Suite( name, suiteOpts );
var suiteAdd = suite.add;
- suite.add = function( fn, opts ){
- var opts = global.opts = opts || {};
-
- global.setup = opts.setup || function( cytoscape ){
- return oldCytoscape();
+ suite.add = function( fn ){
+ global.setup = suiteOpts.setup || function( cytoscape ){
+ return cytoscape();
};
- global.teardown = opts.teardown || function( cy ){
+ global.teardown = suiteOpts.teardown || function( cy ){
if( cy.destroy ){ cy.destroy(); }
};
diff --git a/gulpfile.js b/gulpfile.js
index c0df913d3d..606fe35f35 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -20,7 +20,7 @@ var assign = function( a, b ){
return a;
};
-var benchmarkVersion = '2.5.3'; // old version to test against for benchmarks
+var benchmarkVersion = require('./benchmark/old-version.json'); // old version to test against for benchmarks
var benchmarkVersionUrl = 'https://raw.githubusercontent.com/cytoscape/cytoscape.js/v' + benchmarkVersion + '/dist/cytoscape.js';
var weaverVersion = 'master';