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';