完成世界书、骰子、apiconfig页面处理
This commit is contained in:
329
frontend/node_modules/cytoscape/src/collection/algorithms/affinity-propagation.mjs
generated
vendored
Normal file
329
frontend/node_modules/cytoscape/src/collection/algorithms/affinity-propagation.mjs
generated
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
// Implemented by Zoe Xi @zoexi for GSOC 2016
|
||||
// https://github.com/cytoscape/cytoscape.js-affinity-propagation
|
||||
|
||||
// Implemented from the reference library: https://github.com/juhis/affinity-propagation
|
||||
// Additional reference: http://www.psi.toronto.edu/affinitypropagation/faq.html
|
||||
|
||||
import * as util from '../../util/index.mjs';
|
||||
import * as math from '../../math.mjs';
|
||||
import * as is from '../../is.mjs';
|
||||
import clusteringDistance from './clustering-distances.mjs';
|
||||
|
||||
let defaults = util.defaults({
|
||||
distance: 'euclidean', // distance metric to compare attributes between two nodes
|
||||
preference: 'median', // suitability of a data point to serve as an exemplar
|
||||
damping: 0.8, // damping factor between [0.5, 1)
|
||||
maxIterations: 1000, // max number of iterations to run
|
||||
minIterations: 100, // min number of iterations to run in order for clustering to stop
|
||||
attributes: [ // functions to quantify the similarity between any two points
|
||||
// e.g. node => node.data('weight')
|
||||
]
|
||||
});
|
||||
|
||||
let setOptions = function( options ) {
|
||||
let dmp = options.damping;
|
||||
let pref = options.preference;
|
||||
|
||||
if( !(0.5 <= dmp && dmp < 1) ){
|
||||
util.error(`Damping must range on [0.5, 1). Got: ${dmp}`);
|
||||
}
|
||||
|
||||
let validPrefs = ['median', 'mean', 'min', 'max'];
|
||||
if( !( validPrefs.some(v => v === pref) || is.number(pref) ) ){
|
||||
util.error(`Preference must be one of [${validPrefs.map( p => `'${p}'` ).join(', ')}] or a number. Got: ${pref}`);
|
||||
}
|
||||
|
||||
return defaults( options );
|
||||
};
|
||||
|
||||
if( process.env.NODE_ENV !== 'production' ){ /* eslint-disable no-console, no-unused-vars */
|
||||
var printMatrix = function( M ) { // used for debugging purposes only
|
||||
let str = '';
|
||||
let log = s => str = str + s + '\n';
|
||||
let n = Math.sqrt(M.length);
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
let row = '';
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
row += M[i*n+j] + ' ';
|
||||
}
|
||||
log(row);
|
||||
}
|
||||
|
||||
console.log(str);
|
||||
};
|
||||
} /* eslint-enable */
|
||||
|
||||
let getSimilarity = function( type, n1, n2, attributes ) {
|
||||
let attr = (n, i) => attributes[i](n);
|
||||
|
||||
// nb negative because similarity should have an inverse relationship to distance
|
||||
return -clusteringDistance( type, attributes.length, i => attr(n1, i), i => attr(n2, i), n1, n2 );
|
||||
};
|
||||
|
||||
let getPreference = function( S, preference ) { // larger preference = greater # of clusters
|
||||
let p = null;
|
||||
|
||||
if( preference === 'median' ){
|
||||
p = math.median( S );
|
||||
} else if( preference === 'mean' ){
|
||||
p = math.mean( S );
|
||||
} else if ( preference === 'min' ){
|
||||
p = math.min( S );
|
||||
} else if ( preference === 'max' ){
|
||||
p = math.max( S );
|
||||
} else { // Custom preference number, as set by user
|
||||
p = preference;
|
||||
}
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
let findExemplars = function( n, R, A ) {
|
||||
let indices = [];
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
if ( R[i * n + i] + A[i * n + i] > 0 ) {
|
||||
indices.push(i);
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
};
|
||||
|
||||
let assignClusters = function( n, S, exemplars ) {
|
||||
let clusters = [];
|
||||
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
let index = -1;
|
||||
let max = -Infinity;
|
||||
|
||||
for ( let ei = 0; ei < exemplars.length; ei++ ) {
|
||||
let e = exemplars[ei];
|
||||
if ( S[i * n + e] > max ) {
|
||||
index = e;
|
||||
max = S[i * n + e];
|
||||
}
|
||||
}
|
||||
|
||||
if( index > 0 ){
|
||||
clusters.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
for (let ei = 0; ei < exemplars.length; ei++) {
|
||||
clusters[ exemplars[ei] ] = exemplars[ei];
|
||||
}
|
||||
|
||||
return clusters;
|
||||
};
|
||||
|
||||
let assign = function( n, S, exemplars ) {
|
||||
|
||||
let clusters = assignClusters( n, S, exemplars );
|
||||
|
||||
for ( let ei = 0; ei < exemplars.length; ei++ ) {
|
||||
|
||||
let ii = [];
|
||||
for ( let c = 0; c < clusters.length; c++ ) {
|
||||
if (clusters[c] === exemplars[ei]) {
|
||||
ii.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
let maxI = -1;
|
||||
let maxSum = -Infinity;
|
||||
for ( let i = 0; i < ii.length; i++ ) {
|
||||
let sum = 0;
|
||||
for ( let j = 0; j < ii.length; j++ ) {
|
||||
sum += S[ii[j] * n + ii[i]];
|
||||
}
|
||||
if ( sum > maxSum ) {
|
||||
maxI = i;
|
||||
maxSum = sum;
|
||||
}
|
||||
}
|
||||
|
||||
exemplars[ei] = ii[maxI];
|
||||
}
|
||||
|
||||
clusters = assignClusters( n, S, exemplars );
|
||||
|
||||
return clusters;
|
||||
};
|
||||
|
||||
let affinityPropagation = function( options ) {
|
||||
let cy = this.cy();
|
||||
let nodes = this.nodes();
|
||||
let opts = setOptions( options );
|
||||
|
||||
// Map each node to its position in node array
|
||||
let id2position = {};
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
id2position[ nodes[i].id() ] = i;
|
||||
}
|
||||
|
||||
// Begin affinity propagation algorithm
|
||||
|
||||
let n; // number of data points
|
||||
let n2; // size of matrices
|
||||
let S; // similarity matrix (1D array)
|
||||
let p; // preference/suitability of a data point to serve as an exemplar
|
||||
let R; // responsibility matrix (1D array)
|
||||
let A; // availability matrix (1D array)
|
||||
|
||||
n = nodes.length;
|
||||
n2 = n * n;
|
||||
|
||||
// Initialize and build S similarity matrix
|
||||
S = new Array(n2);
|
||||
for ( let i = 0; i < n2; i++ ) {
|
||||
S[i] = -Infinity; // for cases where two data points shouldn't be linked together
|
||||
}
|
||||
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
if ( i !== j ) {
|
||||
S[i * n + j] = getSimilarity( opts.distance, nodes[i], nodes[j], opts.attributes );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Place preferences on the diagonal of S
|
||||
p = getPreference( S, opts.preference );
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
S[i * n + i] = p;
|
||||
}
|
||||
|
||||
// Initialize R responsibility matrix
|
||||
R = new Array(n2);
|
||||
for ( let i = 0; i < n2; i++ ) {
|
||||
R[i] = 0.0;
|
||||
}
|
||||
|
||||
// Initialize A availability matrix
|
||||
A = new Array(n2);
|
||||
for ( let i = 0; i < n2; i++ ) {
|
||||
A[i] = 0.0;
|
||||
}
|
||||
|
||||
let old = new Array(n);
|
||||
let Rp = new Array(n);
|
||||
let se = new Array(n);
|
||||
|
||||
for ( let i = 0; i < n; i ++ ) {
|
||||
old[i] = 0.0;
|
||||
Rp[i] = 0.0;
|
||||
se[i] = 0;
|
||||
}
|
||||
|
||||
let e = new Array(n * opts.minIterations);
|
||||
for ( let i = 0; i < e.length; i++ ) {
|
||||
e[i] = 0;
|
||||
}
|
||||
|
||||
let iter;
|
||||
for ( iter = 0; iter < opts.maxIterations; iter++ ) { // main algorithmic loop
|
||||
|
||||
// Update R responsibility matrix
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
|
||||
let max = -Infinity,
|
||||
max2 = -Infinity,
|
||||
maxI = -1,
|
||||
AS = 0.0;
|
||||
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
|
||||
old[j] = R[i * n + j];
|
||||
|
||||
AS = A[i * n + j] + S[i * n + j];
|
||||
if ( AS >= max ) {
|
||||
max2 = max;
|
||||
max = AS;
|
||||
maxI = j;
|
||||
}
|
||||
else if ( AS > max2 ) {
|
||||
max2 = AS;
|
||||
}
|
||||
}
|
||||
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
R[i * n + j] = (1 - opts.damping) * (S[i * n + j] - max) + opts.damping * old[j];
|
||||
}
|
||||
|
||||
R[i * n + maxI] = (1 - opts.damping) * (S[i * n + maxI] - max2) + opts.damping * old[maxI];
|
||||
}
|
||||
|
||||
// Update A availability matrix
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
let sum = 0;
|
||||
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
old[j] = A[j * n + i];
|
||||
Rp[j] = Math.max(0, R[j * n + i]);
|
||||
sum += Rp[j];
|
||||
}
|
||||
|
||||
sum -= Rp[i];
|
||||
Rp[i] = R[i * n + i];
|
||||
sum += Rp[i];
|
||||
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
A[j * n + i] = (1 - opts.damping) * Math.min(0, sum - Rp[j]) + opts.damping * old[j];
|
||||
}
|
||||
A[i * n + i] = (1 - opts.damping) * (sum - Rp[i]) + opts.damping * old[i];
|
||||
}
|
||||
|
||||
// Check for convergence
|
||||
let K = 0;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
let E = A[i * n + i] + R[i * n + i] > 0 ? 1 : 0;
|
||||
e[(iter % opts.minIterations) * n + i] = E;
|
||||
K += E;
|
||||
}
|
||||
|
||||
if ( K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1) ) {
|
||||
|
||||
let sum = 0;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
se[i] = 0;
|
||||
for ( let j = 0; j < opts.minIterations; j++ ) {
|
||||
se[i] += e[j * n + i];
|
||||
}
|
||||
if ( se[i] === 0 || se[i] === opts.minIterations ) {
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( sum === n ) { // then we have convergence
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Identify exemplars (cluster centers)
|
||||
let exemplarsIndices = findExemplars( n, R, A );
|
||||
|
||||
// Assign nodes to clusters
|
||||
let clusterIndices = assign( n, S, exemplarsIndices, nodes, id2position );
|
||||
|
||||
let clusters = {};
|
||||
for ( let c = 0; c < exemplarsIndices.length; c++ ) {
|
||||
clusters[ exemplarsIndices[c] ] = [];
|
||||
}
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let pos = id2position[ nodes[i].id() ];
|
||||
let clusterIndex = clusterIndices[pos];
|
||||
|
||||
if( clusterIndex != null ){ // the node may have not been assigned a cluster if no valid attributes were specified
|
||||
clusters[ clusterIndex ].push( nodes[i] );
|
||||
}
|
||||
}
|
||||
let retClusters = new Array(exemplarsIndices.length);
|
||||
for ( let c = 0; c < exemplarsIndices.length; c++ ) {
|
||||
retClusters[c] = cy.collection( clusters[ exemplarsIndices[c] ] );
|
||||
}
|
||||
|
||||
return retClusters;
|
||||
};
|
||||
|
||||
export default { affinityPropagation, ap: affinityPropagation };
|
||||
174
frontend/node_modules/cytoscape/src/collection/algorithms/betweenness-centrality.mjs
generated
vendored
Normal file
174
frontend/node_modules/cytoscape/src/collection/algorithms/betweenness-centrality.mjs
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
import Heap from '../../heap.mjs';
|
||||
import * as util from '../../util/index.mjs';
|
||||
|
||||
const defaults = util.defaults({
|
||||
weight: null,
|
||||
directed: false
|
||||
});
|
||||
|
||||
let 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 ){
|
||||
let { directed, weight } = defaults(options);
|
||||
let weighted = weight != null;
|
||||
let cy = this.cy();
|
||||
|
||||
// starting
|
||||
let V = this.nodes();
|
||||
let A = {};
|
||||
let _C = {};
|
||||
let max = 0;
|
||||
let C = {
|
||||
set: function( key, val ){
|
||||
_C[ key ] = val;
|
||||
|
||||
if( val > max ){ max = val; }
|
||||
},
|
||||
|
||||
get: function( key ){ return _C[ key ]; }
|
||||
};
|
||||
|
||||
// A contains the neighborhoods of every node
|
||||
for( let i = 0; i < V.length; i++ ){
|
||||
let v = V[ i ];
|
||||
let vid = v.id();
|
||||
|
||||
if( directed ){
|
||||
A[ vid ] = v.outgoers().nodes(); // get outgoers of every node
|
||||
} else {
|
||||
A[ vid ] = v.openNeighborhood().nodes(); // get neighbors of every node
|
||||
}
|
||||
|
||||
C.set( vid, 0 );
|
||||
}
|
||||
|
||||
for( let s = 0; s < V.length; s++ ){
|
||||
let sid = V[s].id();
|
||||
let S = []; // stack
|
||||
let P = {};
|
||||
let g = {};
|
||||
let d = {};
|
||||
let Q = new Heap(function( a, b ){
|
||||
return d[a] - d[b];
|
||||
}); // queue
|
||||
|
||||
// init dictionaries
|
||||
for( let i = 0; i < V.length; i++ ){
|
||||
let vid = V[ i ].id();
|
||||
|
||||
P[ vid ] = [];
|
||||
g[ vid ] = 0;
|
||||
d[ vid ] = Infinity;
|
||||
}
|
||||
|
||||
g[ sid ] = 1; // sigma
|
||||
d[ sid ] = 0; // distance to s
|
||||
|
||||
Q.push( sid );
|
||||
|
||||
while( !Q.empty() ){
|
||||
let v = Q.pop();
|
||||
|
||||
S.push( v );
|
||||
|
||||
if( weighted ){
|
||||
for( let j = 0; j < A[v].length; j++ ){
|
||||
let w = A[v][j];
|
||||
let vEle = cy.getElementById( v );
|
||||
|
||||
let edge;
|
||||
if( vEle.edgesTo( w ).length > 0 ){
|
||||
edge = vEle.edgesTo( w )[0];
|
||||
} else {
|
||||
edge = w.edgesTo( vEle )[0];
|
||||
}
|
||||
|
||||
let edgeWeight = weight( edge );
|
||||
|
||||
w = w.id();
|
||||
|
||||
if( d[w] > d[v] + edgeWeight ){
|
||||
d[w] = d[v] + edgeWeight;
|
||||
|
||||
if( Q.nodes.indexOf( w ) < 0 ){ //if w is not in Q
|
||||
Q.push( w );
|
||||
} else { // update position if w is in Q
|
||||
Q.updateItem( w );
|
||||
}
|
||||
|
||||
g[w] = 0;
|
||||
P[w] = [];
|
||||
}
|
||||
|
||||
if( d[w] == d[v] + edgeWeight ){
|
||||
g[w] = g[w] + g[v];
|
||||
P[w].push( v );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for( let j = 0; j < A[v].length; j++ ){
|
||||
let w = A[v][j].id();
|
||||
|
||||
if( d[w] == Infinity ){
|
||||
Q.push( w );
|
||||
|
||||
d[w] = d[v] + 1;
|
||||
}
|
||||
|
||||
if( d[w] == d[v] + 1 ){
|
||||
g[w] = g[w] + g[v];
|
||||
P[w].push( v );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let e = {};
|
||||
for( let i = 0; i < V.length; i++ ){
|
||||
e[ V[ i ].id() ] = 0;
|
||||
}
|
||||
|
||||
while( S.length > 0 ){
|
||||
let w = S.pop();
|
||||
|
||||
for( let j = 0; j < P[w].length; j++ ){
|
||||
let v = P[w][j];
|
||||
|
||||
e[v] = e[v] + (g[v] / g[w]) * (1 + e[w]);
|
||||
}
|
||||
|
||||
if( w != V[s].id() ){
|
||||
C.set( w, C.get( w ) + e[w] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ret = {
|
||||
betweenness: function( node ){
|
||||
let id = cy.collection(node).id();
|
||||
|
||||
return C.get( id );
|
||||
},
|
||||
|
||||
betweennessNormalized: function( node ){
|
||||
if ( max == 0 ){ return 0; }
|
||||
|
||||
let id = cy.collection(node).id();
|
||||
|
||||
return C.get( id ) / max;
|
||||
}
|
||||
};
|
||||
|
||||
// alias
|
||||
ret.betweennessNormalised = ret.betweennessNormalized;
|
||||
|
||||
return ret;
|
||||
} // betweennessCentrality
|
||||
|
||||
}); // elesfn
|
||||
|
||||
// nice, short mathematical alias
|
||||
elesfn.bc = elesfn.betweennessCentrality;
|
||||
|
||||
export default elesfn;
|
||||
101
frontend/node_modules/cytoscape/src/collection/algorithms/closeness-centrality.mjs
generated
vendored
Normal file
101
frontend/node_modules/cytoscape/src/collection/algorithms/closeness-centrality.mjs
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
import * as is from '../../is.mjs';
|
||||
import * as util from '../../util/index.mjs';
|
||||
|
||||
const defaults = util.defaults({
|
||||
harmonic: true,
|
||||
weight: () => 1,
|
||||
directed: false,
|
||||
root: null
|
||||
});
|
||||
|
||||
const elesfn = ({
|
||||
|
||||
closenessCentralityNormalized: function( options ){
|
||||
let { harmonic, weight, directed } = defaults(options);
|
||||
|
||||
let cy = this.cy();
|
||||
let closenesses = {};
|
||||
let maxCloseness = 0;
|
||||
let nodes = this.nodes();
|
||||
let fw = this.floydWarshall({ weight, directed });
|
||||
|
||||
// Compute closeness for every node and find the maximum closeness
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let currCloseness = 0;
|
||||
let node_i = nodes[i];
|
||||
|
||||
for( let j = 0; j < nodes.length; j++ ){
|
||||
if( i !== j ){
|
||||
let d = fw.distance( node_i, nodes[j] );
|
||||
|
||||
if( harmonic ){
|
||||
currCloseness += 1 / d;
|
||||
} else {
|
||||
currCloseness += d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !harmonic ){
|
||||
currCloseness = 1 / currCloseness;
|
||||
}
|
||||
|
||||
if( maxCloseness < currCloseness ){
|
||||
maxCloseness = currCloseness;
|
||||
}
|
||||
|
||||
closenesses[ node_i.id() ] = currCloseness;
|
||||
}
|
||||
|
||||
return {
|
||||
closeness: function( node ){
|
||||
if( maxCloseness == 0 ){ return 0; }
|
||||
|
||||
if( is.string( node ) ){
|
||||
// from is a selector string
|
||||
node = (cy.filter( node )[0]).id();
|
||||
} else {
|
||||
// from is a node
|
||||
node = node.id();
|
||||
}
|
||||
|
||||
return closenesses[ node ] / maxCloseness;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Implemented from pseudocode from wikipedia
|
||||
closenessCentrality: function( options ){
|
||||
let { root, weight, directed, harmonic } = defaults(options);
|
||||
|
||||
root = this.filter(root)[0];
|
||||
|
||||
// we need distance from this node to every other node
|
||||
let dijkstra = this.dijkstra({ root, weight, directed });
|
||||
let totalDistance = 0;
|
||||
let nodes = this.nodes();
|
||||
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let n = nodes[i];
|
||||
|
||||
if( !n.same(root) ){
|
||||
let d = dijkstra.distanceTo(n);
|
||||
|
||||
if( harmonic ){
|
||||
totalDistance += 1 / d;
|
||||
} else {
|
||||
totalDistance += d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return harmonic ? totalDistance : 1 / totalDistance;
|
||||
} // closenessCentrality
|
||||
|
||||
}); // elesfn
|
||||
|
||||
// nice, short mathematical alias
|
||||
elesfn.cc = elesfn.closenessCentrality;
|
||||
elesfn.ccn = elesfn.closenessCentralityNormalised = elesfn.closenessCentralityNormalized;
|
||||
|
||||
export default elesfn;
|
||||
133
frontend/node_modules/cytoscape/src/collection/algorithms/dijkstra.mjs
generated
vendored
Normal file
133
frontend/node_modules/cytoscape/src/collection/algorithms/dijkstra.mjs
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
import * as is from '../../is.mjs';
|
||||
import Heap from '../../heap.mjs';
|
||||
import { defaults } from '../../util/index.mjs';
|
||||
|
||||
const dijkstraDefaults = defaults({
|
||||
root: null,
|
||||
weight: edge => 1,
|
||||
directed: false
|
||||
});
|
||||
|
||||
let elesfn = ({
|
||||
|
||||
dijkstra: function( options ){
|
||||
if( !is.plainObject(options) ){
|
||||
let args = arguments;
|
||||
|
||||
options = { root: args[0], weight: args[1], directed: args[2] };
|
||||
}
|
||||
|
||||
let { root, weight, directed } = dijkstraDefaults(options);
|
||||
|
||||
let eles = this;
|
||||
let weightFn = weight;
|
||||
let source = is.string( root ) ? this.filter( root )[0] : root[0];
|
||||
let dist = {};
|
||||
let prev = {};
|
||||
let knownDist = {};
|
||||
|
||||
let { nodes, edges } = this.byGroup();
|
||||
edges.unmergeBy( ele => ele.isLoop() );
|
||||
|
||||
let getDist = node => dist[ node.id() ];
|
||||
|
||||
let setDist = ( node, d ) => {
|
||||
dist[ node.id() ] = d;
|
||||
|
||||
Q.updateItem( node );
|
||||
};
|
||||
|
||||
let Q = new Heap( (a, b) => getDist(a) - getDist(b) );
|
||||
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let node = nodes[ i ];
|
||||
|
||||
dist[ node.id() ] = node.same( source ) ? 0 : Infinity;
|
||||
Q.push( node );
|
||||
}
|
||||
|
||||
let distBetween = ( u, v ) => {
|
||||
let uvs = ( directed ? u.edgesTo(v) : u.edgesWith(v) ).intersect( edges );
|
||||
let smallestDistance = Infinity;
|
||||
let smallestEdge;
|
||||
|
||||
for( let i = 0; i < uvs.length; i++ ){
|
||||
let edge = uvs[ i ];
|
||||
let weight = weightFn( edge );
|
||||
|
||||
if( weight < smallestDistance || !smallestEdge ){
|
||||
smallestDistance = weight;
|
||||
smallestEdge = edge;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
edge: smallestEdge,
|
||||
dist: smallestDistance
|
||||
};
|
||||
};
|
||||
|
||||
while( Q.size() > 0 ){
|
||||
let u = Q.pop();
|
||||
let smalletsDist = getDist( u );
|
||||
let uid = u.id();
|
||||
|
||||
knownDist[ uid ] = smalletsDist;
|
||||
|
||||
if( smalletsDist === Infinity ){
|
||||
continue;
|
||||
}
|
||||
|
||||
let neighbors = u.neighborhood().intersect( nodes );
|
||||
for( let i = 0; i < neighbors.length; i++ ){
|
||||
let v = neighbors[ i ];
|
||||
let vid = v.id();
|
||||
let vDist = distBetween( u, v );
|
||||
|
||||
let alt = smalletsDist + vDist.dist;
|
||||
|
||||
if( alt < getDist( v ) ){
|
||||
setDist( v, alt );
|
||||
|
||||
prev[ vid ] = {
|
||||
node: u,
|
||||
edge: vDist.edge
|
||||
};
|
||||
}
|
||||
} // for
|
||||
} // while
|
||||
|
||||
return {
|
||||
distanceTo: function( node ){
|
||||
let target = is.string( node ) ? nodes.filter( node )[0] : node[0];
|
||||
|
||||
return knownDist[ target.id() ];
|
||||
},
|
||||
|
||||
pathTo: function( node ){
|
||||
let target = is.string( node ) ? nodes.filter( node )[0] : node[0];
|
||||
let S = [];
|
||||
let u = target;
|
||||
let uid = u.id();
|
||||
|
||||
if( target.length > 0 ){
|
||||
S.unshift( target );
|
||||
|
||||
while( prev[ uid ] ){
|
||||
let p = prev[ uid ];
|
||||
|
||||
S.unshift( p.edge );
|
||||
S.unshift( p.node );
|
||||
|
||||
u = p.node;
|
||||
uid = u.id();
|
||||
}
|
||||
}
|
||||
|
||||
return eles.spawn( S );
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default elesfn;
|
||||
118
frontend/node_modules/cytoscape/src/collection/algorithms/hopcroft-tarjan-biconnected.mjs
generated
vendored
Normal file
118
frontend/node_modules/cytoscape/src/collection/algorithms/hopcroft-tarjan-biconnected.mjs
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
let hopcroftTarjanBiconnected = function() {
|
||||
let eles = this;
|
||||
let nodes = {};
|
||||
let id = 0;
|
||||
let edgeCount = 0;
|
||||
let components = [];
|
||||
let stack = [];
|
||||
let visitedEdges = {};
|
||||
|
||||
const buildComponent = (x, y) => {
|
||||
let i = stack.length-1;
|
||||
let cutset = [];
|
||||
let component = eles.spawn();
|
||||
|
||||
while (stack[i].x != x || stack[i].y != y) {
|
||||
cutset.push(stack.pop().edge);
|
||||
i--;
|
||||
}
|
||||
cutset.push(stack.pop().edge);
|
||||
|
||||
cutset.forEach(edge => {
|
||||
let connectedNodes = edge.connectedNodes()
|
||||
.intersection(eles);
|
||||
component.merge(edge);
|
||||
connectedNodes.forEach(node => {
|
||||
const nodeId = node.id();
|
||||
const connectedEdges = node.connectedEdges()
|
||||
.intersection(eles);
|
||||
component.merge(node);
|
||||
if (!nodes[nodeId].cutVertex) {
|
||||
component.merge(connectedEdges);
|
||||
} else {
|
||||
component.merge(connectedEdges.filter(edge => edge.isLoop()));
|
||||
}
|
||||
});
|
||||
});
|
||||
components.push(component);
|
||||
};
|
||||
|
||||
const biconnectedSearch = (root, currentNode, parent) => {
|
||||
if (root === parent) edgeCount += 1;
|
||||
nodes[currentNode] = {
|
||||
id : id,
|
||||
low : id++,
|
||||
cutVertex : false
|
||||
};
|
||||
let edges = eles.getElementById(currentNode)
|
||||
.connectedEdges()
|
||||
.intersection(eles);
|
||||
|
||||
if (edges.size() === 0) {
|
||||
components.push(eles.spawn(eles.getElementById(currentNode)));
|
||||
} else {
|
||||
let sourceId, targetId, otherNodeId, edgeId;
|
||||
|
||||
edges.forEach(edge => {
|
||||
sourceId = edge.source().id();
|
||||
targetId = edge.target().id();
|
||||
otherNodeId = (sourceId === currentNode) ? targetId : sourceId;
|
||||
|
||||
if (otherNodeId !== parent) {
|
||||
edgeId = edge.id();
|
||||
|
||||
if (!visitedEdges[edgeId]) {
|
||||
visitedEdges[edgeId] = true;
|
||||
stack.push({
|
||||
x : currentNode,
|
||||
y : otherNodeId,
|
||||
edge
|
||||
});
|
||||
}
|
||||
|
||||
if (!(otherNodeId in nodes)) {
|
||||
biconnectedSearch(root, otherNodeId, currentNode);
|
||||
nodes[currentNode].low = Math.min(nodes[currentNode].low,
|
||||
nodes[otherNodeId].low);
|
||||
|
||||
if (nodes[currentNode].id <= nodes[otherNodeId].low) {
|
||||
nodes[currentNode].cutVertex = true;
|
||||
buildComponent(currentNode, otherNodeId);
|
||||
}
|
||||
} else {
|
||||
nodes[currentNode].low = Math.min(nodes[currentNode].low,
|
||||
nodes[otherNodeId].id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
eles.forEach(ele => {
|
||||
if (ele.isNode()) {
|
||||
let nodeId = ele.id();
|
||||
|
||||
if (!(nodeId in nodes)) {
|
||||
edgeCount = 0;
|
||||
biconnectedSearch(nodeId, nodeId);
|
||||
nodes[nodeId].cutVertex = (edgeCount > 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let cutVertices = Object.keys(nodes)
|
||||
.filter(id => nodes[id].cutVertex)
|
||||
.map(id => eles.getElementById(id));
|
||||
|
||||
return {
|
||||
cut: eles.spawn(cutVertices),
|
||||
components
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
hopcroftTarjanBiconnected,
|
||||
htbc: hopcroftTarjanBiconnected,
|
||||
htb: hopcroftTarjanBiconnected,
|
||||
hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
|
||||
};
|
||||
34
frontend/node_modules/cytoscape/src/collection/cache-traversal-call.mjs
generated
vendored
Normal file
34
frontend/node_modules/cytoscape/src/collection/cache-traversal-call.mjs
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
|
||||
let cache = function( fn, name ){
|
||||
return function traversalCache( arg1, arg2, arg3, arg4 ){
|
||||
let selectorOrEles = arg1;
|
||||
let eles = this;
|
||||
let key;
|
||||
|
||||
if( selectorOrEles == null ){
|
||||
key = '';
|
||||
} else if( is.elementOrCollection( selectorOrEles ) && selectorOrEles.length === 1 ){
|
||||
key = selectorOrEles.id();
|
||||
}
|
||||
|
||||
if( eles.length === 1 && key ){
|
||||
let _p = eles[0]._private;
|
||||
let tch = _p.traversalCache = _p.traversalCache || {};
|
||||
let ch = tch[ name ] = tch[ name ] || [];
|
||||
let hash = util.hashString( key );
|
||||
let cacheHit = ch[ hash ];
|
||||
|
||||
if( cacheHit ){
|
||||
return cacheHit;
|
||||
} else {
|
||||
return ( ch[ hash ] = fn.call( eles, arg1, arg2, arg3, arg4 ) );
|
||||
}
|
||||
} else {
|
||||
return fn.call( eles, arg1, arg2, arg3, arg4 );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default cache;
|
||||
86
frontend/node_modules/cytoscape/src/collection/data.mjs
generated
vendored
Normal file
86
frontend/node_modules/cytoscape/src/collection/data.mjs
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
import define from '../define/index.mjs';
|
||||
|
||||
let 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(){
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return ele._private.data.id;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// aliases
|
||||
fn.attr = fn.data;
|
||||
fn.removeAttr = fn.removeData;
|
||||
|
||||
export default elesfn;
|
||||
56
frontend/node_modules/cytoscape/src/collection/dimensions/edge-points.mjs
generated
vendored
Normal file
56
frontend/node_modules/cytoscape/src/collection/dimensions/edge-points.mjs
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import * as math from '../../math.mjs';
|
||||
|
||||
const ifEdge = (ele, getValue) => {
|
||||
if( ele.isEdge() && ele.takesUpSpace() ){
|
||||
return getValue( ele );
|
||||
}
|
||||
};
|
||||
|
||||
const ifEdgeRenderedPosition = (ele, getPoint) => {
|
||||
if( ele.isEdge() && ele.takesUpSpace() ){
|
||||
let cy = ele.cy();
|
||||
|
||||
return math.modelToRenderedPosition( getPoint( ele ), cy.zoom(), cy.pan() );
|
||||
}
|
||||
};
|
||||
|
||||
const ifEdgeRenderedPositions = (ele, getPoints) => {
|
||||
if( ele.isEdge() && ele.takesUpSpace() ){
|
||||
let cy = ele.cy();
|
||||
let pan = cy.pan();
|
||||
let zoom = cy.zoom();
|
||||
|
||||
return getPoints( ele ).map( p => math.modelToRenderedPosition( p, zoom, pan ) );
|
||||
}
|
||||
};
|
||||
|
||||
const controlPoints = ele => ele.renderer().getControlPoints( ele );
|
||||
const segmentPoints = ele => ele.renderer().getSegmentPoints( ele );
|
||||
const sourceEndpoint = ele => ele.renderer().getSourceEndpoint( ele );
|
||||
const targetEndpoint = ele => ele.renderer().getTargetEndpoint( ele );
|
||||
const midpoint = ele => ele.renderer().getEdgeMidpoint( ele );
|
||||
|
||||
const pts = {
|
||||
controlPoints: { get: controlPoints, mult: true },
|
||||
segmentPoints: { get: segmentPoints, mult: true },
|
||||
sourceEndpoint: { get: sourceEndpoint },
|
||||
targetEndpoint: { get: targetEndpoint },
|
||||
midpoint: { get: midpoint }
|
||||
};
|
||||
|
||||
const renderedName = name => 'rendered' + name[0].toUpperCase() + name.substr(1);
|
||||
|
||||
export default Object.keys( pts ).reduce( ( obj, name ) => {
|
||||
let spec = pts[ name ];
|
||||
let rName = renderedName( name );
|
||||
|
||||
obj[ name ] = function(){ return ifEdge( this, spec.get ); };
|
||||
|
||||
if( spec.mult ){
|
||||
obj[ rName ] = function(){ return ifEdgeRenderedPositions( this, spec.get ); };
|
||||
} else {
|
||||
obj[ rName ] = function(){ return ifEdgeRenderedPosition( this, spec.get ); };
|
||||
}
|
||||
|
||||
return obj;
|
||||
}, {} );
|
||||
130
frontend/node_modules/cytoscape/src/collection/element.mjs
generated
vendored
Normal file
130
frontend/node_modules/cytoscape/src/collection/element.mjs
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import Set from '../set.mjs';
|
||||
|
||||
// represents a node or an edge
|
||||
let Element = function( cy, params, restore = true ){
|
||||
if( cy === undefined || params === undefined || !is.core( cy ) ){
|
||||
util.error( 'An element must have a core reference and parameters set' );
|
||||
return;
|
||||
}
|
||||
|
||||
let group = params.group;
|
||||
|
||||
// try to automatically infer the group if unspecified
|
||||
if( group == null ){
|
||||
if( params.data && 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()
|
||||
let _p = this._private = {
|
||||
cy: cy,
|
||||
single: true, // indicates this is an element
|
||||
data: params.data || {}, // data object
|
||||
position: params.position || { x: 0, y: 0 }, // (x, y) position pair
|
||||
autoWidth: undefined, // width and height of nodes calculated by the renderer when set to special 'auto' value
|
||||
autoHeight: undefined,
|
||||
autoPadding: undefined,
|
||||
compoundBoundsClean: false, // whether the compound dimensions need to be recalculated the next time dimensions are read
|
||||
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
|
||||
styleKeys: {}, // per-group keys of style property values
|
||||
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
|
||||
pannable: params.pannable === undefined ? (group === 'edges' ? true : false) : ( params.pannable ? true : false ), // whether the element has passthrough panning enabled
|
||||
active: false, // whether the element is active from user interaction
|
||||
classes: new Set(), // 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
|
||||
parent: params.parent && params.parent.isNode() ? params.parent : null, // parent ref
|
||||
traversalCache: {}, // cache of output of traversal functions
|
||||
backgrounding: false, // whether background images are loading
|
||||
bbCache: null, // cache of the current bounding box
|
||||
bbCacheShift: { x: 0, y: 0 }, // shift applied to cached bb to be applied on next get
|
||||
bodyBounds: null, // bounds cache of element body, w/o overlay
|
||||
overlayBounds: null, // bounds cache of element body, including overlay
|
||||
labelBounds: { // bounds cache of labels
|
||||
all: null,
|
||||
source: null,
|
||||
target: null,
|
||||
main: null
|
||||
},
|
||||
arrowBounds: { // bounds cache of edge arrows
|
||||
source: null,
|
||||
target: null,
|
||||
'mid-source': null,
|
||||
'mid-target': null
|
||||
}
|
||||
};
|
||||
|
||||
if( _p.position.x == null ){ _p.position.x = 0; }
|
||||
if( _p.position.y == null ){ _p.position.y = 0; }
|
||||
|
||||
// renderedPosition overrides if specified
|
||||
if( params.renderedPosition ){
|
||||
let rpos = params.renderedPosition;
|
||||
let pan = cy.pan();
|
||||
let zoom = cy.zoom();
|
||||
|
||||
_p.position = {
|
||||
x: (rpos.x - pan.x) / zoom,
|
||||
y: (rpos.y - pan.y) / zoom
|
||||
};
|
||||
}
|
||||
|
||||
let classes = [];
|
||||
if( is.array( params.classes ) ){
|
||||
classes = params.classes;
|
||||
} else if( is.string( params.classes ) ){
|
||||
classes = params.classes.split( /\s+/ );
|
||||
}
|
||||
for( let i = 0, l = classes.length; i < l; i++ ){
|
||||
let cls = classes[ i ];
|
||||
if( !cls || cls === '' ){ continue; }
|
||||
|
||||
_p.classes.add(cls);
|
||||
}
|
||||
|
||||
this.createEmitter();
|
||||
|
||||
if( restore === undefined || restore ){
|
||||
this.restore();
|
||||
}
|
||||
|
||||
let bypass = params.style || params.css;
|
||||
if( bypass ){
|
||||
util.warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
|
||||
|
||||
this.style(bypass);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default Element;
|
||||
196
frontend/node_modules/cytoscape/src/collection/layout.mjs
generated
vendored
Normal file
196
frontend/node_modules/cytoscape/src/collection/layout.mjs
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import Promise from '../promise.mjs';
|
||||
import * as math from '../math.mjs';
|
||||
|
||||
const getLayoutDimensionOptions = util.defaults({
|
||||
nodeDimensionsIncludeLabels: false
|
||||
});
|
||||
|
||||
let elesfn = ({
|
||||
// Calculates and returns node dimensions { x, y } based on options given
|
||||
layoutDimensions: function( options ){
|
||||
options = getLayoutDimensionOptions( options );
|
||||
|
||||
let dims;
|
||||
|
||||
if( !this.takesUpSpace() ){
|
||||
dims = { w: 0, h: 0 };
|
||||
} else if( options.nodeDimensionsIncludeLabels ){
|
||||
let bbDim = this.boundingBox();
|
||||
|
||||
dims = {
|
||||
w: bbDim.w,
|
||||
h: bbDim.h
|
||||
};
|
||||
} else {
|
||||
dims = {
|
||||
w: this.outerWidth(),
|
||||
h: this.outerHeight()
|
||||
};
|
||||
}
|
||||
|
||||
// sanitise the dimensions for external layouts (avoid division by zero)
|
||||
if( dims.w === 0 || dims.h === 0 ){
|
||||
dims.w = dims.h = 1;
|
||||
}
|
||||
|
||||
return dims;
|
||||
},
|
||||
|
||||
// using standard layout options, apply position function (w/ or w/o animation)
|
||||
layoutPositions: function( layout, options, fn ){
|
||||
let nodes = this.nodes().filter(n => !n.isParent());
|
||||
let cy = this.cy();
|
||||
let layoutEles = options.eles; // nodes & edges
|
||||
let getMemoizeKey = node => node.id();
|
||||
let fnMem = util.memoize( fn, getMemoizeKey ); // memoized version of position function
|
||||
|
||||
layout.emit( { type: 'layoutstart', layout: layout } );
|
||||
|
||||
layout.animations = [];
|
||||
|
||||
let calculateSpacing = function( spacing, nodesBb, pos ){
|
||||
let center = {
|
||||
x: nodesBb.x1 + nodesBb.w / 2,
|
||||
y: nodesBb.y1 + nodesBb.h / 2
|
||||
};
|
||||
|
||||
let spacingVector = { // scale from center of bounding box (not necessarily 0,0)
|
||||
x: (pos.x - center.x) * spacing,
|
||||
y: (pos.y - center.y) * spacing
|
||||
};
|
||||
|
||||
return {
|
||||
x: center.x + spacingVector.x,
|
||||
y: center.y + spacingVector.y
|
||||
};
|
||||
};
|
||||
|
||||
let useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
|
||||
|
||||
let spacingBb = function(){
|
||||
if( !useSpacingFactor ){ return null; }
|
||||
|
||||
let bb = math.makeBoundingBox();
|
||||
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let node = nodes[i];
|
||||
let pos = fnMem( node, i );
|
||||
|
||||
math.expandBoundingBoxByPoint( bb, pos.x, pos.y );
|
||||
}
|
||||
|
||||
return bb;
|
||||
};
|
||||
|
||||
let bb = spacingBb();
|
||||
|
||||
let getFinalPos = util.memoize( function( node, i ){
|
||||
let newPos = fnMem( node, i );
|
||||
|
||||
if( useSpacingFactor ){
|
||||
let spacing = Math.abs( options.spacingFactor );
|
||||
|
||||
newPos = calculateSpacing( spacing, bb, newPos );
|
||||
}
|
||||
|
||||
if( options.transform != null ){
|
||||
newPos = options.transform( node, newPos );
|
||||
}
|
||||
|
||||
return newPos;
|
||||
}, getMemoizeKey );
|
||||
|
||||
if( options.animate ){
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let node = nodes[ i ];
|
||||
let newPos = getFinalPos( node, i );
|
||||
let animateNode = options.animateFilter == null || options.animateFilter( node, i );
|
||||
|
||||
if( animateNode ){
|
||||
let ani = node.animation( {
|
||||
position: newPos,
|
||||
duration: options.animationDuration,
|
||||
easing: options.animationEasing
|
||||
} );
|
||||
|
||||
layout.animations.push( ani );
|
||||
} else {
|
||||
node.position( newPos );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( options.fit ){
|
||||
let fitAni = cy.animation({
|
||||
fit: {
|
||||
boundingBox: layoutEles.boundingBoxAt( getFinalPos ),
|
||||
padding: options.padding
|
||||
},
|
||||
duration: options.animationDuration,
|
||||
easing: options.animationEasing
|
||||
});
|
||||
|
||||
layout.animations.push( fitAni );
|
||||
} else if( options.zoom !== undefined && options.pan !== undefined ){
|
||||
let zoomPanAni = cy.animation({
|
||||
zoom: options.zoom,
|
||||
pan: options.pan,
|
||||
duration: options.animationDuration,
|
||||
easing: options.animationEasing
|
||||
});
|
||||
|
||||
layout.animations.push( zoomPanAni );
|
||||
}
|
||||
|
||||
layout.animations.forEach(ani => ani.play());
|
||||
|
||||
layout.one( 'layoutready', options.ready );
|
||||
layout.emit( { type: 'layoutready', layout: layout } );
|
||||
|
||||
Promise.all( layout.animations.map(function( ani ){
|
||||
return ani.promise();
|
||||
}) ).then(function(){
|
||||
layout.one( 'layoutstop', options.stop );
|
||||
layout.emit( { type: 'layoutstop', layout: layout } );
|
||||
});
|
||||
} else {
|
||||
|
||||
nodes.positions( getFinalPos );
|
||||
|
||||
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.emit( { type: 'layoutready', layout: layout } );
|
||||
|
||||
layout.one( 'layoutstop', options.stop );
|
||||
layout.emit( { type: 'layoutstop', layout: layout } );
|
||||
}
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
layout: function( options ){
|
||||
let cy = this.cy();
|
||||
|
||||
return cy.makeLayout( util.extend( {}, options, {
|
||||
eles: this
|
||||
} ) );
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// aliases:
|
||||
elesfn.createLayout = elesfn.makeLayout = elesfn.layout;
|
||||
|
||||
export default elesfn;
|
||||
457
frontend/node_modules/cytoscape/src/collection/style.mjs
generated
vendored
Normal file
457
frontend/node_modules/cytoscape/src/collection/style.mjs
generated
vendored
Normal file
@@ -0,0 +1,457 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
|
||||
function styleCache( key, fn, ele ){
|
||||
var _p = ele._private;
|
||||
var cache = _p.styleCache = _p.styleCache || [];
|
||||
var val;
|
||||
|
||||
if( (val = cache[key]) != null ){
|
||||
return val;
|
||||
} else {
|
||||
val = cache[key] = fn( ele );
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
function cacheStyleFunction( key, fn ){
|
||||
key = util.hashString( key );
|
||||
|
||||
return function cachedStyleFunction( ele ){
|
||||
return styleCache( key, fn, ele );
|
||||
};
|
||||
}
|
||||
|
||||
function cachePrototypeStyleFunction( key, fn ){
|
||||
key = util.hashString( key );
|
||||
|
||||
let selfFn = ele => fn.call( ele );
|
||||
|
||||
return function cachedPrototypeStyleFunction(){
|
||||
var ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return styleCache( key, selfFn, ele );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let elesfn = ({
|
||||
|
||||
recalculateRenderedStyle: function( useCache ){
|
||||
let cy = this.cy();
|
||||
let renderer = cy.renderer();
|
||||
let styleEnabled = cy.styleEnabled();
|
||||
|
||||
if( renderer && styleEnabled ){
|
||||
renderer.recalculateRenderedStyle( this, useCache );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
dirtyStyleCache: function(){
|
||||
let cy = this.cy();
|
||||
let dirty = ele => ele._private.styleCache = null;
|
||||
|
||||
if( cy.hasCompoundNodes() ){
|
||||
let eles;
|
||||
|
||||
eles = this.spawnSelf()
|
||||
.merge( this.descendants() )
|
||||
.merge( this.parents() )
|
||||
;
|
||||
|
||||
eles.merge( eles.connectedEdges() );
|
||||
|
||||
eles.forEach( dirty );
|
||||
} else {
|
||||
this.forEach( ele => {
|
||||
dirty( ele );
|
||||
|
||||
ele.connectedEdges().forEach( dirty );
|
||||
} );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// fully updates (recalculates) the style for the elements
|
||||
updateStyle: function( notifyRenderer ){
|
||||
let cy = this._private.cy;
|
||||
|
||||
if( !cy.styleEnabled() ){ return this; }
|
||||
|
||||
if( cy.batching() ){
|
||||
let bEles = cy._private.batchStyleEles;
|
||||
|
||||
bEles.merge( this );
|
||||
|
||||
return this; // chaining and exit early when batching
|
||||
}
|
||||
|
||||
let hasCompounds = cy.hasCompoundNodes();
|
||||
let updatedEles = this;
|
||||
|
||||
notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
|
||||
|
||||
if( hasCompounds ){ // then add everything up and down for compound selector checks
|
||||
updatedEles = this.spawnSelf().merge( this.descendants() ).merge( this.parents() );
|
||||
}
|
||||
|
||||
// let changedEles = style.apply( updatedEles );
|
||||
let changedEles = updatedEles;
|
||||
|
||||
if( notifyRenderer ){
|
||||
changedEles.emitAndNotify( 'style' ); // let renderer know we changed style
|
||||
} else {
|
||||
changedEles.emit( 'style' ); // just fire the event
|
||||
}
|
||||
|
||||
updatedEles.forEach(ele => ele._private.styleDirty = true);
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
// private: clears dirty flag and recalculates style
|
||||
cleanStyle: function(){
|
||||
let cy = this.cy();
|
||||
|
||||
if( !cy.styleEnabled() ){ return; }
|
||||
|
||||
for( let i = 0; i < this.length; i++ ){
|
||||
let ele = this[i];
|
||||
|
||||
if( ele._private.styleDirty ){
|
||||
// n.b. this flag should be set before apply() to avoid potential infinite recursion
|
||||
ele._private.styleDirty = false;
|
||||
|
||||
cy.style().apply(ele);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// get the internal parsed style object for the specified property
|
||||
parsedStyle: function( property, includeNonDefault = true ){
|
||||
let ele = this[0];
|
||||
let cy = ele.cy();
|
||||
|
||||
if( !cy.styleEnabled() ){ return; }
|
||||
|
||||
if( ele ){
|
||||
// this.cleanStyle();
|
||||
|
||||
// Inline the important part of cleanStyle(), for raw performance
|
||||
if( ele._private.styleDirty ){
|
||||
// n.b. this flag should be set before apply() to avoid potential infinite recursion
|
||||
ele._private.styleDirty = false;
|
||||
cy.style().apply(ele);
|
||||
}
|
||||
|
||||
let overriddenStyle = ele._private.style[ property ];
|
||||
|
||||
if( overriddenStyle != null ){
|
||||
return overriddenStyle;
|
||||
} else if( includeNonDefault ){
|
||||
return cy.style().getDefaultProperty( property );
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
numericStyle: function( property ){
|
||||
let ele = this[0];
|
||||
|
||||
if( !ele.cy().styleEnabled() ){ return; }
|
||||
|
||||
if( ele ){
|
||||
let pstyle = ele.pstyle( property );
|
||||
|
||||
return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
|
||||
}
|
||||
},
|
||||
|
||||
numericStyleUnits: function( property ){
|
||||
let ele = this[0];
|
||||
|
||||
if( !ele.cy().styleEnabled() ){ return; }
|
||||
|
||||
if( ele ){
|
||||
return ele.pstyle( property ).units;
|
||||
}
|
||||
},
|
||||
|
||||
// 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)
|
||||
renderedStyle: function( property ){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return this; }
|
||||
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return cy.style().getRenderedStyle( ele, property );
|
||||
}
|
||||
},
|
||||
|
||||
// read the calculated css style of the element or override the style (via a bypass)
|
||||
style: function( name, value ){
|
||||
let cy = this.cy();
|
||||
|
||||
if( !cy.styleEnabled() ){ return this; }
|
||||
|
||||
let updateTransitions = false;
|
||||
let style = cy.style();
|
||||
|
||||
if( is.plainObject( name ) ){ // then extend the bypass
|
||||
let props = name;
|
||||
style.applyBypass( this, props, updateTransitions );
|
||||
|
||||
this.emitAndNotify( 'style' ); // let the renderer know we've updated style
|
||||
|
||||
} else if( is.string( name ) ){
|
||||
|
||||
if( value === undefined ){ // then get the property from the style
|
||||
let 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 );
|
||||
|
||||
this.emitAndNotify( 'style' ); // let the renderer know we've updated style
|
||||
}
|
||||
|
||||
} else if( name === undefined ){
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return style.getRawStyle( ele );
|
||||
} else { // empty collection => can't get any value
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
removeStyle: function( names ){
|
||||
let cy = this.cy();
|
||||
|
||||
if( !cy.styleEnabled() ){ return this; }
|
||||
|
||||
let updateTransitions = false;
|
||||
let style = cy.style();
|
||||
let eles = this;
|
||||
|
||||
if( names === undefined ){
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
style.removeAllBypasses( ele, updateTransitions );
|
||||
}
|
||||
} else {
|
||||
names = names.split( /\s+/ );
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
style.removeBypasses( ele, names, updateTransitions );
|
||||
}
|
||||
}
|
||||
|
||||
this.emitAndNotify( '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
|
||||
},
|
||||
|
||||
effectiveOpacity: function(){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return 1; }
|
||||
|
||||
let hasCompoundNodes = cy.hasCompoundNodes();
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
let _p = ele._private;
|
||||
let parentOpacity = ele.pstyle( 'opacity' ).value;
|
||||
|
||||
if( !hasCompoundNodes ){ return parentOpacity; }
|
||||
|
||||
let parents = !_p.data.parent ? null : ele.parents();
|
||||
|
||||
if( parents ){
|
||||
for( let i = 0; i < parents.length; i++ ){
|
||||
let parent = parents[ i ];
|
||||
let opacity = parent.pstyle( 'opacity' ).value;
|
||||
|
||||
parentOpacity = opacity * parentOpacity;
|
||||
}
|
||||
}
|
||||
|
||||
return parentOpacity;
|
||||
}
|
||||
},
|
||||
|
||||
transparent: function(){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return false; }
|
||||
|
||||
let ele = this[0];
|
||||
let hasCompoundNodes = ele.cy().hasCompoundNodes();
|
||||
|
||||
if( ele ){
|
||||
if( !hasCompoundNodes ){
|
||||
return ele.pstyle( 'opacity' ).value === 0;
|
||||
} else {
|
||||
return ele.effectiveOpacity() === 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
backgrounding: function(){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return false; }
|
||||
|
||||
let ele = this[0];
|
||||
|
||||
return ele._private.backgrounding ? true : false;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function checkCompound( ele, parentOk ){
|
||||
let _p = ele._private;
|
||||
let parents = _p.data.parent ? ele.parents() : null;
|
||||
|
||||
if( parents ){ for( let i = 0; i < parents.length; i++ ){
|
||||
let parent = parents[ i ];
|
||||
|
||||
if( !parentOk( parent ) ){ return false; }
|
||||
} }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function defineDerivedStateFunction( specs ){
|
||||
let ok = specs.ok;
|
||||
let edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
|
||||
let parentOk = specs.parentOk || specs.ok;
|
||||
|
||||
return function(){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return true; }
|
||||
|
||||
let ele = this[0];
|
||||
let hasCompoundNodes = cy.hasCompoundNodes();
|
||||
|
||||
if( ele ){
|
||||
let _p = ele._private;
|
||||
|
||||
if( !ok( ele ) ){ return false; }
|
||||
|
||||
if( ele.isNode() ){
|
||||
return !hasCompoundNodes || checkCompound( ele, parentOk );
|
||||
} else {
|
||||
let src = _p.source;
|
||||
let tgt = _p.target;
|
||||
|
||||
return ( edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) ) &&
|
||||
( src === tgt || ( edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)) ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let eleTakesUpSpace = cacheStyleFunction( 'eleTakesUpSpace', function( ele ){
|
||||
return (
|
||||
ele.pstyle( 'display' ).value === 'element'
|
||||
&& ele.width() !== 0
|
||||
&& ( ele.isNode() ? ele.height() !== 0 : true )
|
||||
);
|
||||
} );
|
||||
|
||||
elesfn.takesUpSpace = cachePrototypeStyleFunction( 'takesUpSpace', defineDerivedStateFunction({
|
||||
ok: eleTakesUpSpace
|
||||
}) );
|
||||
|
||||
let eleInteractive = cacheStyleFunction( 'eleInteractive', function( ele ){
|
||||
return (
|
||||
ele.pstyle('events').value === 'yes'
|
||||
&& ele.pstyle('visibility').value === 'visible'
|
||||
&& eleTakesUpSpace( ele )
|
||||
);
|
||||
} );
|
||||
|
||||
let parentInteractive = cacheStyleFunction( 'parentInteractive', function( parent ){
|
||||
return (
|
||||
parent.pstyle('visibility').value === 'visible'
|
||||
&& eleTakesUpSpace( parent )
|
||||
);
|
||||
} );
|
||||
|
||||
elesfn.interactive = cachePrototypeStyleFunction( 'interactive', defineDerivedStateFunction({
|
||||
ok: eleInteractive,
|
||||
parentOk: parentInteractive,
|
||||
edgeOkViaNode: eleTakesUpSpace
|
||||
}) );
|
||||
|
||||
elesfn.noninteractive = function(){
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return !ele.interactive();
|
||||
}
|
||||
};
|
||||
|
||||
let eleVisible = cacheStyleFunction( 'eleVisible', function( ele ){
|
||||
return (
|
||||
ele.pstyle( 'visibility' ).value === 'visible'
|
||||
&& ele.pstyle( 'opacity' ).pfValue !== 0
|
||||
&& eleTakesUpSpace( ele )
|
||||
);
|
||||
} );
|
||||
|
||||
let edgeVisibleViaNode = eleTakesUpSpace;
|
||||
|
||||
elesfn.visible = cachePrototypeStyleFunction( 'visible', defineDerivedStateFunction({
|
||||
ok: eleVisible,
|
||||
edgeOkViaNode: edgeVisibleViaNode
|
||||
}) );
|
||||
|
||||
elesfn.hidden = function(){
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return !ele.visible();
|
||||
}
|
||||
};
|
||||
|
||||
elesfn.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function(){
|
||||
if( !this.cy().styleEnabled() ){ return false; }
|
||||
|
||||
return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
|
||||
});
|
||||
|
||||
elesfn.bypass = elesfn.css = elesfn.style;
|
||||
elesfn.renderedCss = elesfn.renderedStyle;
|
||||
elesfn.removeBypass = elesfn.removeCss = elesfn.removeStyle;
|
||||
elesfn.pstyle = elesfn.parsedStyle;
|
||||
|
||||
export default elesfn;
|
||||
82
frontend/node_modules/cytoscape/src/core/add-remove.mjs
generated
vendored
Normal file
82
frontend/node_modules/cytoscape/src/core/add-remove.mjs
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
import Collection from '../collection/index.mjs';
|
||||
import Element from '../collection/element.mjs';
|
||||
|
||||
let corefn = {
|
||||
add: function( opts ){
|
||||
|
||||
let elements;
|
||||
let cy = this;
|
||||
|
||||
// add the elements
|
||||
if( is.elementOrCollection( opts ) ){
|
||||
let eles = opts;
|
||||
|
||||
if( eles._private.cy === cy ){ // same instance => just restore
|
||||
elements = eles.restore();
|
||||
|
||||
} else { // otherwise, copy from json
|
||||
let jsons = [];
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
jsons.push( ele.json() );
|
||||
}
|
||||
|
||||
elements = new Collection( cy, jsons );
|
||||
}
|
||||
}
|
||||
|
||||
// specify an array of options
|
||||
else if( is.array( opts ) ){
|
||||
let 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 )) ){
|
||||
let elesByGroup = opts;
|
||||
let jsons = [];
|
||||
|
||||
let grs = [ 'nodes', 'edges' ];
|
||||
for( let i = 0, il = grs.length; i < il; i++ ){
|
||||
let group = grs[ i ];
|
||||
let elesArray = elesByGroup[ group ];
|
||||
|
||||
if( is.array( elesArray ) ){
|
||||
|
||||
for( let j = 0, jl = elesArray.length; j < jl; j++ ){
|
||||
let json = util.extend( { group: group }, elesArray[ j ] );
|
||||
|
||||
jsons.push( json );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elements = new Collection( cy, jsons );
|
||||
}
|
||||
|
||||
// specify options for one element
|
||||
else {
|
||||
let json = opts;
|
||||
elements = (new Element( cy, json )).collection();
|
||||
}
|
||||
|
||||
return elements;
|
||||
},
|
||||
|
||||
remove: function( collection ){
|
||||
if( is.elementOrCollection( collection ) ){
|
||||
// already have right ref
|
||||
} else if( is.string( collection ) ){
|
||||
let selector = collection;
|
||||
collection = this.$( selector );
|
||||
}
|
||||
|
||||
return collection.remove();
|
||||
}
|
||||
};
|
||||
|
||||
export default corefn;
|
||||
82
frontend/node_modules/cytoscape/src/core/animation/ease.mjs
generated
vendored
Normal file
82
frontend/node_modules/cytoscape/src/core/animation/ease.mjs
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import * as is from '../../is.mjs';
|
||||
|
||||
function getEasedValue( type, start, end, percent, easingFn ){
|
||||
if( percent === 1 ){
|
||||
return end;
|
||||
}
|
||||
|
||||
if( start === end ){
|
||||
return end;
|
||||
}
|
||||
|
||||
let val = easingFn( start, end, percent );
|
||||
|
||||
if( type == null ){
|
||||
return val;
|
||||
}
|
||||
|
||||
if( type.roundValue || type.color ){
|
||||
val = Math.round( val );
|
||||
}
|
||||
|
||||
if( type.min !== undefined ){
|
||||
val = Math.max( val, type.min );
|
||||
}
|
||||
|
||||
if( type.max !== undefined ){
|
||||
val = Math.min( val, type.max );
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
function getValue( prop, spec ){
|
||||
if( prop.pfValue != null || prop.value != null ){
|
||||
if( prop.pfValue != null && (spec == null || spec.type.units !== '%') ){
|
||||
return prop.pfValue;
|
||||
} else {
|
||||
return prop.value;
|
||||
}
|
||||
} else {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
function ease( startProp, endProp, percent, easingFn, propSpec ){
|
||||
let type = propSpec != null ? propSpec.type : null;
|
||||
|
||||
if( percent < 0 ){
|
||||
percent = 0;
|
||||
} else if( percent > 1 ){
|
||||
percent = 1;
|
||||
}
|
||||
|
||||
let start = getValue( startProp, propSpec );
|
||||
let end = getValue( endProp, propSpec );
|
||||
|
||||
if( is.number( start ) && is.number( end ) ){
|
||||
return getEasedValue( type, start, end, percent, easingFn );
|
||||
|
||||
} else if( is.array( start ) && is.array( end ) ){
|
||||
let easedArr = [];
|
||||
|
||||
for( let i = 0; i < end.length; i++ ){
|
||||
let si = start[ i ];
|
||||
let ei = end[ i ];
|
||||
|
||||
if( si != null && ei != null ){
|
||||
let val = getEasedValue( type, si, ei, percent, easingFn );
|
||||
|
||||
easedArr.push( val );
|
||||
} else {
|
||||
easedArr.push( ei );
|
||||
}
|
||||
}
|
||||
|
||||
return easedArr;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export default ease;
|
||||
76
frontend/node_modules/cytoscape/src/core/animation/easings.mjs
generated
vendored
Normal file
76
frontend/node_modules/cytoscape/src/core/animation/easings.mjs
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import generateCubicBezier from './cubic-bezier.mjs';
|
||||
import generateSpringRK4 from './spring.mjs';
|
||||
|
||||
let cubicBezier = function( t1, p1, t2, p2 ){
|
||||
let bezier = generateCubicBezier( t1, p1, t2, p2 );
|
||||
|
||||
return function( start, end, percent ){
|
||||
return start + ( end - start ) * bezier( percent );
|
||||
};
|
||||
};
|
||||
|
||||
let 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 ){
|
||||
if( duration === 0 ){ // can't get a spring w/ duration 0
|
||||
return easings.linear; // duration 0 => jump to end so impl doesn't matter
|
||||
}
|
||||
|
||||
let spring = generateSpringRK4( tension, friction, duration );
|
||||
|
||||
return function( start, end, percent ){
|
||||
return start + (end - start) * spring( percent );
|
||||
};
|
||||
},
|
||||
|
||||
'cubic-bezier': cubicBezier
|
||||
};
|
||||
|
||||
export default easings;
|
||||
522
frontend/node_modules/cytoscape/src/core/index.mjs
generated
vendored
Normal file
522
frontend/node_modules/cytoscape/src/core/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,522 @@
|
||||
import window from '../window.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
import Collection from '../collection/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import Promise from '../promise.mjs';
|
||||
|
||||
import addRemove from './add-remove.mjs';
|
||||
import animation from './animation/index.mjs';
|
||||
import events from './events.mjs';
|
||||
import exportFormat from './export.mjs';
|
||||
import layout from './layout.mjs';
|
||||
import notification from './notification.mjs';
|
||||
import renderer from './renderer.mjs';
|
||||
import search from './search.mjs';
|
||||
import style from './style.mjs';
|
||||
import viewport from './viewport.mjs';
|
||||
import data from './data.mjs';
|
||||
|
||||
let Core = function( opts ){
|
||||
let cy = this;
|
||||
|
||||
opts = util.extend( {}, opts );
|
||||
|
||||
let 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];
|
||||
}
|
||||
|
||||
let 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
|
||||
}
|
||||
|
||||
let readies = reg.readies = reg.readies || [];
|
||||
|
||||
if( container ){ container._cyreg = reg; } // make sure container assoc'd reg points to this cy
|
||||
reg.cy = cy;
|
||||
|
||||
let head = window !== undefined && container !== undefined && !opts.headless;
|
||||
let options = opts;
|
||||
options.layout = util.extend( { name: head ? 'grid' : 'null' }, options.layout );
|
||||
options.renderer = util.extend( { name: head ? 'canvas' : 'null' }, options.renderer );
|
||||
|
||||
let defVal = function( def, val, altVal ){
|
||||
if( val !== undefined ){
|
||||
return val;
|
||||
} else if( altVal !== undefined ){
|
||||
return altVal;
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
};
|
||||
|
||||
let _p = this._private = {
|
||||
container: container, // html dom ele container
|
||||
ready: false, // whether ready has been triggered
|
||||
options: options, // cached options
|
||||
elements: new Collection( this ), // elements in the graph
|
||||
listeners: [], // list of listeners
|
||||
aniEles: new Collection( this ), // elements being animated
|
||||
data: options.data || {}, // data for the core
|
||||
scratch: {}, // scratch object for core
|
||||
layout: null,
|
||||
renderer: null,
|
||||
destroyed: false, // whether destroy was called
|
||||
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,
|
||||
multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
|
||||
};
|
||||
|
||||
this.createEmitter();
|
||||
|
||||
// set selection type
|
||||
this.selectionType( options.selectionType );
|
||||
|
||||
// init zoom bounds
|
||||
this.zoomRange({ min: options.minZoom, max: options.maxZoom });
|
||||
|
||||
let loadExtData = function( extData, next ){
|
||||
let anyIsPromise = extData.some( is.promise );
|
||||
|
||||
if( anyIsPromise ){
|
||||
return Promise.all( extData ).then( next ); // load all data asynchronously, then exec rest of init
|
||||
} else {
|
||||
next( extData ); // exec synchronously for convenience
|
||||
}
|
||||
};
|
||||
|
||||
// start with the default stylesheet so we have something before loading an external stylesheet
|
||||
if( _p.styleEnabled ){
|
||||
cy.setStyle([]);
|
||||
}
|
||||
|
||||
// create the renderer
|
||||
let rendererOptions = util.assign({}, options, options.renderer); // allow rendering hints in top level options
|
||||
cy.initRenderer( rendererOptions );
|
||||
|
||||
let setElesAndLayout = function( elements, onload, ondone ){
|
||||
cy.notifications( false );
|
||||
|
||||
// remove old elements
|
||||
let oldEles = cy.mutableElements();
|
||||
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.emit( e ); // we missed this event by turning notifications off, so pass it on
|
||||
|
||||
cy.one( 'load', onload );
|
||||
cy.emitAndNotify( 'load' );
|
||||
} ).one( 'layoutstop', function(){
|
||||
cy.one( 'done', ondone );
|
||||
cy.emit( 'done' );
|
||||
} );
|
||||
|
||||
let layoutOpts = util.extend( {}, cy._private.options.layout );
|
||||
layoutOpts.eles = cy.elements();
|
||||
|
||||
cy.layout( layoutOpts ).run();
|
||||
};
|
||||
|
||||
loadExtData([ options.style, options.elements ], function( thens ){
|
||||
let initStyle = thens[0];
|
||||
let initEles = thens[1];
|
||||
|
||||
// init style
|
||||
if( _p.styleEnabled ){
|
||||
cy.style().append( initStyle );
|
||||
}
|
||||
|
||||
// initial load
|
||||
setElesAndLayout( 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( let i = 0; i < readies.length; i++ ){
|
||||
let 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.emit( 'ready' );
|
||||
}, options.done );
|
||||
|
||||
} );
|
||||
};
|
||||
|
||||
let corefn = Core.prototype; // short alias
|
||||
|
||||
util.extend( corefn, {
|
||||
instanceString: function(){
|
||||
return 'core';
|
||||
},
|
||||
|
||||
isReady: function(){
|
||||
return this._private.ready;
|
||||
},
|
||||
|
||||
destroyed: function(){
|
||||
return this._private.destroyed;
|
||||
},
|
||||
|
||||
ready: function( fn ){
|
||||
if( this.isReady() ){
|
||||
this.emitter().emit( 'ready', [], fn ); // just calls fn as though triggered via ready event
|
||||
} else {
|
||||
this.on( 'ready', fn );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
let cy = this;
|
||||
if( cy.destroyed() ) return;
|
||||
|
||||
cy.stopAnimationLoop();
|
||||
|
||||
cy.destroyRenderer();
|
||||
|
||||
this.emit( 'destroy' );
|
||||
|
||||
cy._private.destroyed = true;
|
||||
|
||||
return cy;
|
||||
},
|
||||
|
||||
hasElementWithId: function( id ){
|
||||
return this._private.elements.hasElementWithId( id );
|
||||
},
|
||||
|
||||
getElementById: function( id ){
|
||||
return this._private.elements.getElementById( id );
|
||||
},
|
||||
|
||||
hasCompoundNodes: function(){
|
||||
return this._private.hasCompoundNodes;
|
||||
},
|
||||
|
||||
headless: function(){
|
||||
return this._private.renderer.isHeadless();
|
||||
},
|
||||
|
||||
styleEnabled: function(){
|
||||
return this._private.styleEnabled;
|
||||
},
|
||||
|
||||
addToPool: function( eles ){
|
||||
this._private.elements.merge( eles );
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
removeFromPool: function( eles ){
|
||||
this._private.elements.unmerge( eles );
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
container: function(){
|
||||
return this._private.container || null;
|
||||
},
|
||||
|
||||
window: function() {
|
||||
let container = this._private.container;
|
||||
if (container == null) return window;
|
||||
|
||||
let ownerDocument = this._private.container.ownerDocument;
|
||||
|
||||
if (ownerDocument === undefined || ownerDocument == null) {
|
||||
return window;
|
||||
}
|
||||
|
||||
return ownerDocument.defaultView || window;
|
||||
},
|
||||
|
||||
mount: function( container ){
|
||||
if( container == null ){ return; }
|
||||
|
||||
let cy = this;
|
||||
let _p = cy._private;
|
||||
let options = _p.options;
|
||||
|
||||
if( !is.htmlElement( container ) && is.htmlElement( container[0] ) ){
|
||||
container = container[0];
|
||||
}
|
||||
|
||||
cy.stopAnimationLoop();
|
||||
|
||||
cy.destroyRenderer();
|
||||
|
||||
_p.container = container;
|
||||
_p.styleEnabled = true;
|
||||
|
||||
cy.invalidateSize();
|
||||
|
||||
cy.initRenderer( util.assign({}, options, options.renderer, {
|
||||
// allow custom renderer name to be re-used, otherwise use canvas
|
||||
name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
|
||||
}) );
|
||||
|
||||
cy.startAnimationLoop();
|
||||
|
||||
cy.style( options.style );
|
||||
|
||||
cy.emit( 'mount' );
|
||||
|
||||
return cy;
|
||||
},
|
||||
|
||||
unmount: function(){
|
||||
let cy = this;
|
||||
|
||||
cy.stopAnimationLoop();
|
||||
|
||||
cy.destroyRenderer();
|
||||
|
||||
cy.initRenderer( { name: 'null' } );
|
||||
|
||||
cy.emit( 'unmount' );
|
||||
|
||||
return cy;
|
||||
},
|
||||
|
||||
options: function(){
|
||||
return util.copy( this._private.options );
|
||||
},
|
||||
|
||||
json: function( obj ){
|
||||
let cy = this;
|
||||
let _p = cy._private;
|
||||
let eles = cy.mutableElements();
|
||||
let getFreshRef = ele => cy.getElementById(ele.id());
|
||||
|
||||
if( is.plainObject( obj ) ){ // set
|
||||
|
||||
cy.startBatch();
|
||||
|
||||
if( obj.elements ){
|
||||
let idInJson = {};
|
||||
|
||||
let updateEles = function( jsons, gr ){
|
||||
let toAdd = [];
|
||||
let toMod = [];
|
||||
|
||||
for( let i = 0; i < jsons.length; i++ ){
|
||||
let json = jsons[ i ];
|
||||
|
||||
if( !json.data.id ){
|
||||
util.warn( 'cy.json() cannot handle elements without an ID attribute' );
|
||||
continue;
|
||||
}
|
||||
|
||||
let id = '' + json.data.id; // id must be string
|
||||
let ele = cy.getElementById( id );
|
||||
|
||||
idInJson[ id ] = true;
|
||||
|
||||
if( ele.length !== 0 ){ // existing element should be updated
|
||||
toMod.push({ ele, json });
|
||||
} else { // otherwise should be added
|
||||
if( gr ){
|
||||
json.group = gr;
|
||||
|
||||
toAdd.push( json );
|
||||
} else {
|
||||
toAdd.push( json );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cy.add( toAdd );
|
||||
|
||||
for( let i = 0; i < toMod.length; i++ ){
|
||||
let { ele, json } = toMod[i];
|
||||
|
||||
ele.json(json);
|
||||
}
|
||||
};
|
||||
|
||||
if( is.array( obj.elements ) ){ // elements: []
|
||||
updateEles( obj.elements );
|
||||
|
||||
} else { // elements: { nodes: [], edges: [] }
|
||||
let grs = [ 'nodes', 'edges' ];
|
||||
for( let i = 0; i < grs.length; i++ ){
|
||||
let gr = grs[ i ];
|
||||
let elements = obj.elements[ gr ];
|
||||
|
||||
if( is.array( elements ) ){
|
||||
updateEles( elements, gr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let parentsToRemove = cy.collection();
|
||||
|
||||
(eles
|
||||
.filter(ele => !idInJson[ ele.id() ])
|
||||
.forEach(ele => {
|
||||
if ( ele.isParent() ) {
|
||||
parentsToRemove.merge(ele);
|
||||
} else {
|
||||
ele.remove();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// so that children are not removed w/parent
|
||||
parentsToRemove.forEach(ele => ele.children().move({ parent: null }));
|
||||
|
||||
// intermediate parents may be moved by prior line, so make sure we remove by fresh refs
|
||||
parentsToRemove.forEach(ele => getFreshRef(ele).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 );
|
||||
}
|
||||
}
|
||||
|
||||
if( obj.data ){
|
||||
cy.data( obj.data );
|
||||
}
|
||||
|
||||
let fields = [
|
||||
'minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled',
|
||||
'panningEnabled', 'userPanningEnabled',
|
||||
'boxSelectionEnabled',
|
||||
'autolock', 'autoungrabify', 'autounselectify',
|
||||
'multiClickDebounceTime'
|
||||
];
|
||||
|
||||
for( let i = 0; i < fields.length; i++ ){
|
||||
let f = fields[ i ];
|
||||
|
||||
if( obj[ f ] != null ){
|
||||
cy[ f ]( obj[ f ] );
|
||||
}
|
||||
}
|
||||
|
||||
cy.endBatch();
|
||||
|
||||
return this; // chaining
|
||||
} else { // get
|
||||
let flat = !!obj;
|
||||
let json = {};
|
||||
|
||||
if( flat ){
|
||||
json.elements = this.elements().map( ele => ele.json() );
|
||||
} else {
|
||||
json.elements = {};
|
||||
|
||||
eles.forEach( function( ele ){
|
||||
let 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.data = util.copy( cy.data() );
|
||||
|
||||
let options = _p.options;
|
||||
|
||||
json.zoomingEnabled = _p.zoomingEnabled;
|
||||
json.userZoomingEnabled = _p.userZoomingEnabled;
|
||||
json.zoom = _p.zoom;
|
||||
json.minZoom = _p.minZoom;
|
||||
json.maxZoom = _p.maxZoom;
|
||||
json.panningEnabled = _p.panningEnabled;
|
||||
json.userPanningEnabled = _p.userPanningEnabled;
|
||||
json.pan = util.copy( _p.pan );
|
||||
json.boxSelectionEnabled = _p.boxSelectionEnabled;
|
||||
json.renderer = util.copy( options.renderer );
|
||||
json.hideEdgesOnViewport = options.hideEdgesOnViewport;
|
||||
json.textureOnViewport = options.textureOnViewport;
|
||||
json.wheelSensitivity = options.wheelSensitivity;
|
||||
json.motionBlur = options.motionBlur;
|
||||
json.multiClickDebounceTime = options.multiClickDebounceTime;
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
corefn.$id = corefn.getElementById;
|
||||
|
||||
[
|
||||
addRemove,
|
||||
animation,
|
||||
events,
|
||||
exportFormat,
|
||||
layout,
|
||||
notification,
|
||||
renderer,
|
||||
search,
|
||||
style,
|
||||
viewport,
|
||||
data
|
||||
].forEach( function( props ){
|
||||
util.extend( corefn, props );
|
||||
} );
|
||||
|
||||
export default Core;
|
||||
208
frontend/node_modules/cytoscape/src/define/data.mjs
generated
vendored
Normal file
208
frontend/node_modules/cytoscape/src/define/data.mjs
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import get from 'lodash/get.js';
|
||||
import set from 'lodash/set.js';
|
||||
import toPath from 'lodash/toPath.js';
|
||||
|
||||
let define = {
|
||||
|
||||
// access data field
|
||||
data: function( params ){
|
||||
let defaults = {
|
||||
field: 'data',
|
||||
bindingEvent: 'data',
|
||||
allowBinding: false,
|
||||
allowSetting: false,
|
||||
allowGetting: false,
|
||||
settingEvent: 'data',
|
||||
settingTriggersEvent: false,
|
||||
triggerFnName: 'trigger',
|
||||
immutableKeys: {}, // key => true if immutable
|
||||
updateStyle: false,
|
||||
beforeGet: function( self ){},
|
||||
beforeSet: function( self, obj ){},
|
||||
onSet: function( self ){},
|
||||
canSet: function( self ){ return true; }
|
||||
};
|
||||
params = util.extend( {}, defaults, params );
|
||||
|
||||
return function dataImpl( name, value ){
|
||||
let p = params;
|
||||
let self = this;
|
||||
let selfIsArrayLike = self.length !== undefined;
|
||||
let all = selfIsArrayLike ? self : [ self ]; // put in array if not array-like
|
||||
let single = selfIsArrayLike ? self[0] : self;
|
||||
|
||||
// .data('foo', ...)
|
||||
if (is.string(name)) { // set or get property
|
||||
let isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
|
||||
let path = isPathLike && toPath(name);
|
||||
|
||||
// .data('foo')
|
||||
if( p.allowGetting && value === undefined ){ // get
|
||||
|
||||
let ret;
|
||||
if( single ){
|
||||
p.beforeGet( single );
|
||||
|
||||
// check if it's path and a field with the same name doesn't exist
|
||||
if (path && single._private[ p.field ][ name ] === undefined) {
|
||||
ret = get(single._private[ p.field ], path);
|
||||
} else {
|
||||
ret = single._private[ p.field ][ name ];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
// .data('foo', 'bar')
|
||||
} else if( p.allowSetting && value !== undefined ){ // set
|
||||
let valid = !p.immutableKeys[ name ];
|
||||
if( valid ){
|
||||
let change = { [name]: value };
|
||||
|
||||
p.beforeSet( self, change );
|
||||
|
||||
for( let i = 0, l = all.length; i < l; i++ ){
|
||||
let ele = all[i];
|
||||
|
||||
if( p.canSet( ele ) ){
|
||||
if (path && single._private[ p.field ][ name ] === undefined) {
|
||||
set(ele._private[ p.field ], path, value);
|
||||
} else {
|
||||
ele._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
|
||||
let obj = name;
|
||||
let k, v;
|
||||
let keys = Object.keys( obj );
|
||||
|
||||
p.beforeSet( self, obj );
|
||||
|
||||
for( let i = 0; i < keys.length; i++ ){
|
||||
k = keys[ i ];
|
||||
v = obj[ k ];
|
||||
|
||||
let valid = !p.immutableKeys[ k ];
|
||||
if( valid ){
|
||||
for( let j = 0; j < all.length; j++ ){
|
||||
let ele = all[j];
|
||||
|
||||
if( p.canSet( ele ) ){
|
||||
ele._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
|
||||
let fn = name;
|
||||
self.on( p.bindingEvent, fn );
|
||||
|
||||
// .data()
|
||||
} else if( p.allowGetting && name === undefined ){ // get whole object
|
||||
let ret;
|
||||
if( single ){
|
||||
p.beforeGet( single );
|
||||
|
||||
ret = single._private[ p.field ];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return self; // maintain chainability
|
||||
}; // function
|
||||
}, // data
|
||||
|
||||
// remove data field
|
||||
removeData: function( params ){
|
||||
let defaults = {
|
||||
field: 'data',
|
||||
event: 'data',
|
||||
triggerFnName: 'trigger',
|
||||
triggerEvent: false,
|
||||
immutableKeys: {} // key => true if immutable
|
||||
};
|
||||
params = util.extend( {}, defaults, params );
|
||||
|
||||
return function removeDataImpl( names ){
|
||||
let p = params;
|
||||
let self = this;
|
||||
let selfIsArrayLike = self.length !== undefined;
|
||||
let 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
|
||||
let keys = names.split( /\s+/ );
|
||||
let l = keys.length;
|
||||
|
||||
for( let i = 0; i < l; i++ ){ // delete each non-empty key
|
||||
let key = keys[ i ];
|
||||
if( is.emptyString( key ) ){ continue; }
|
||||
|
||||
let valid = !p.immutableKeys[ key ]; // not valid if immutable
|
||||
if( valid ){
|
||||
for( let 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( let i_a = 0, l_a = all.length; i_a < l_a; i_a++ ){
|
||||
let _privateFields = all[ i_a ]._private[ p.field ];
|
||||
let keys = Object.keys( _privateFields );
|
||||
|
||||
for( let i = 0; i < keys.length; i++ ){
|
||||
let key = keys[i];
|
||||
let validKeyToDelete = !p.immutableKeys[ key ];
|
||||
|
||||
if( validKeyToDelete ){
|
||||
_privateFields[ key ] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( p.triggerEvent ){
|
||||
self[ p.triggerFnName ]( p.event );
|
||||
}
|
||||
}
|
||||
|
||||
return self; // maintain chaining
|
||||
}; // function
|
||||
}, // removeData
|
||||
}; // define
|
||||
|
||||
export default define;
|
||||
111
frontend/node_modules/cytoscape/src/event.mjs
generated
vendored
Normal file
111
frontend/node_modules/cytoscape/src/event.mjs
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/*!
|
||||
Event object based on jQuery events, MIT license
|
||||
|
||||
https://jquery.org/license/
|
||||
https://tldrlegal.com/license/mit-license
|
||||
https://github.com/jquery/jquery/blob/master/src/event.js
|
||||
*/
|
||||
|
||||
let Event = function( src, props ){
|
||||
this.recycle( src, props );
|
||||
};
|
||||
|
||||
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';
|
||||
},
|
||||
|
||||
recycle: function( src, props ){
|
||||
this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
|
||||
|
||||
if( src != null && src.preventDefault ){ // Browser Event object
|
||||
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;
|
||||
|
||||
} else if( src != null && src.type ){ // Plain object containing all event details
|
||||
props = src;
|
||||
|
||||
} else { // Event string
|
||||
this.type = src;
|
||||
}
|
||||
|
||||
// Put explicitly provided properties onto the event object
|
||||
if( props != null ){
|
||||
// more efficient to manually copy fields we use
|
||||
this.originalEvent = props.originalEvent;
|
||||
this.type = props.type != null ? props.type : this.type;
|
||||
this.cy = props.cy;
|
||||
this.target = props.target;
|
||||
this.position = props.position;
|
||||
this.renderedPosition = props.renderedPosition;
|
||||
this.namespace = props.namespace;
|
||||
this.layout = props.layout;
|
||||
}
|
||||
|
||||
if( this.cy != null && this.position != null && this.renderedPosition == null ){
|
||||
// create a rendered position based on the passed position
|
||||
let pos = this.position;
|
||||
let zoom = this.cy.zoom();
|
||||
let pan = this.cy.pan();
|
||||
|
||||
this.renderedPosition = {
|
||||
x: pos.x * zoom + pan.x,
|
||||
y: pos.y * zoom + pan.y
|
||||
};
|
||||
}
|
||||
|
||||
// Create a timestamp if incoming event doesn't have one
|
||||
this.timeStamp = src && src.timeStamp || Date.now();
|
||||
},
|
||||
|
||||
preventDefault: function(){
|
||||
this.isDefaultPrevented = returnTrue;
|
||||
|
||||
let 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;
|
||||
|
||||
let 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
|
||||
};
|
||||
|
||||
export default Event;
|
||||
246
frontend/node_modules/cytoscape/src/extension.mjs
generated
vendored
Normal file
246
frontend/node_modules/cytoscape/src/extension.mjs
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
import * as util from'./util/index.mjs';
|
||||
import define from './define/index.mjs';
|
||||
import Collection from './collection/index.mjs';
|
||||
import Core from './core/index.mjs';
|
||||
import incExts from './extensions/index.mjs';
|
||||
import * as is from './is.mjs';
|
||||
import Emitter from './emitter.mjs';
|
||||
|
||||
// registered extensions to cytoscape, indexed by name
|
||||
let extensions = {};
|
||||
|
||||
// registered modules for extensions, indexed by name
|
||||
let modules = {};
|
||||
|
||||
function setExtension( type, name, registrant ){
|
||||
|
||||
let ext = registrant;
|
||||
|
||||
let overrideErr = function( field ){
|
||||
util.warn( 'Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden' );
|
||||
};
|
||||
|
||||
if( type === 'core' ){
|
||||
if( Core.prototype[ name ] ){
|
||||
return overrideErr( name );
|
||||
} else {
|
||||
Core.prototype[ name ] = registrant;
|
||||
}
|
||||
|
||||
} else if( type === 'collection' ){
|
||||
if( Collection.prototype[ name ] ){
|
||||
return overrideErr( name );
|
||||
} else {
|
||||
Collection.prototype[ name ] = registrant;
|
||||
}
|
||||
|
||||
} else if( type === 'layout' ){
|
||||
// fill in missing layout functions in the prototype
|
||||
|
||||
let 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 = [];
|
||||
|
||||
this.createEmitter();
|
||||
};
|
||||
|
||||
let layoutProto = Layout.prototype = Object.create( registrant.prototype );
|
||||
|
||||
let optLayoutFns = [];
|
||||
|
||||
for( let i = 0; i < optLayoutFns.length; i++ ){
|
||||
let 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; };
|
||||
}
|
||||
|
||||
let regStop = registrant.prototype.stop;
|
||||
layoutProto.stop = function(){
|
||||
let opts = this.options;
|
||||
|
||||
if( opts && opts.animate ){
|
||||
let anis = this.animations;
|
||||
|
||||
if( anis ){
|
||||
for( let i = 0; i < anis.length; i++ ){
|
||||
anis[ i ].stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( regStop ){
|
||||
regStop.call( this );
|
||||
} else {
|
||||
this.emit( 'layoutstop' );
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
if( !layoutProto.destroy ){
|
||||
layoutProto.destroy = function(){
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
layoutProto.cy = function(){
|
||||
return this._private.cy;
|
||||
};
|
||||
|
||||
let getCy = layout => layout._private.cy;
|
||||
|
||||
let emitterOpts = {
|
||||
addEventFields: function( layout, evt ){
|
||||
evt.layout = layout;
|
||||
evt.cy = getCy(layout);
|
||||
evt.target = layout;
|
||||
},
|
||||
bubble: function(){ return true; },
|
||||
parent: function( layout ){ return getCy(layout); }
|
||||
};
|
||||
|
||||
util.assign( layoutProto, {
|
||||
createEmitter: function(){
|
||||
this._private.emitter = new Emitter( emitterOpts, this );
|
||||
|
||||
return this;
|
||||
},
|
||||
emitter: function(){ return this._private.emitter; },
|
||||
on: function( evt, cb ){ this.emitter().on( evt, cb ); return this; },
|
||||
one: function( evt, cb ){ this.emitter().one( evt, cb ); return this; },
|
||||
once: function( evt, cb ){ this.emitter().one( evt, cb ); return this; },
|
||||
removeListener: function( evt, cb ){ this.emitter().removeListener( evt, cb ); return this; },
|
||||
removeAllListeners: function(){ this.emitter().removeAllListeners(); return this; },
|
||||
emit: function( evt, params ){ this.emitter().emit( evt, params ); return this; }
|
||||
} );
|
||||
|
||||
define.eventAliasesOn( layoutProto );
|
||||
|
||||
ext = Layout; // replace with our wrapped layout
|
||||
|
||||
} else if( type === 'renderer' && name !== 'null' && name !== 'base' ){
|
||||
// user registered renderers inherit from base
|
||||
|
||||
let BaseRenderer = getExtension( 'renderer', 'base' );
|
||||
let bProto = BaseRenderer.prototype;
|
||||
let RegistrantRenderer = registrant;
|
||||
let rProto = registrant.prototype;
|
||||
|
||||
let Renderer = function(){
|
||||
BaseRenderer.apply( this, arguments );
|
||||
RegistrantRenderer.apply( this, arguments );
|
||||
};
|
||||
|
||||
let proto = Renderer.prototype;
|
||||
|
||||
for( let pName in bProto ){
|
||||
let pVal = bProto[ pName ];
|
||||
let existsInR = rProto[ pName ] != null;
|
||||
|
||||
if( existsInR ){
|
||||
return overrideErr( pName );
|
||||
}
|
||||
|
||||
proto[ pName ] = pVal; // take impl from base
|
||||
}
|
||||
|
||||
for( let pName in rProto ){
|
||||
proto[ pName ] = rProto[ pName ]; // take impl from registrant
|
||||
}
|
||||
|
||||
bProto.clientFunctions.forEach( function( name ){
|
||||
proto[ name ] = proto[ name ] || function(){
|
||||
util.error( 'Renderer does not implement `renderer.' + name + '()` on its prototype' );
|
||||
};
|
||||
} );
|
||||
|
||||
ext = Renderer;
|
||||
|
||||
} else if (type === '__proto__' || type === 'constructor' || type === 'prototype'){
|
||||
// to avoid potential prototype pollution
|
||||
return util.error( type + ' is an illegal type to be registered, possibly lead to prototype pollutions' );
|
||||
}
|
||||
|
||||
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 ]
|
||||
} );
|
||||
}
|
||||
|
||||
let 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 );
|
||||
} );
|
||||
} );
|
||||
|
||||
export default extension;
|
||||
105
frontend/node_modules/cytoscape/src/extensions/layout/circle.mjs
generated
vendored
Normal file
105
frontend/node_modules/cytoscape/src/extensions/layout/circle.mjs
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
import * as util from '../../util/index.mjs';
|
||||
import * as math from '../../math.mjs';
|
||||
import * as is from '../../is.mjs';
|
||||
|
||||
let 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
|
||||
nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm
|
||||
spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
|
||||
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
|
||||
animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
|
||||
ready: undefined, // callback on layoutready
|
||||
stop: undefined, // callback on layoutstop
|
||||
transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
|
||||
|
||||
};
|
||||
|
||||
function CircleLayout( options ){
|
||||
this.options = util.extend( {}, defaults, options );
|
||||
}
|
||||
|
||||
CircleLayout.prototype.run = function(){
|
||||
let params = this.options;
|
||||
let options = params;
|
||||
|
||||
let cy = params.cy;
|
||||
let eles = options.eles;
|
||||
|
||||
let clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
|
||||
|
||||
let nodes = eles.nodes().not( ':parent' );
|
||||
|
||||
if( options.sort ){
|
||||
nodes = nodes.sort( options.sort );
|
||||
}
|
||||
|
||||
let bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
|
||||
x1: 0, y1: 0, w: cy.width(), h: cy.height()
|
||||
} );
|
||||
|
||||
let center = {
|
||||
x: bb.x1 + bb.w / 2,
|
||||
y: bb.y1 + bb.h / 2
|
||||
};
|
||||
|
||||
let sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
|
||||
let dTheta = sweep / ( Math.max( 1, nodes.length - 1 ) );
|
||||
let r;
|
||||
|
||||
let minDistance = 0;
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let n = nodes[ i ];
|
||||
let nbb = n.layoutDimensions( options );
|
||||
let w = nbb.w;
|
||||
let 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
|
||||
|
||||
let dcos = Math.cos( dTheta ) - Math.cos( 0 );
|
||||
let dsin = Math.sin( dTheta ) - Math.sin( 0 );
|
||||
let rMin = Math.sqrt( minDistance * minDistance / ( dcos * dcos + dsin * dsin ) ); // s.t. no nodes overlapping
|
||||
r = Math.max( rMin, r );
|
||||
}
|
||||
|
||||
let getPos = function( ele, i ){
|
||||
let theta = options.startAngle + i * dTheta * ( clockwise ? 1 : -1 );
|
||||
|
||||
let rx = r * Math.cos( theta );
|
||||
let ry = r * Math.sin( theta );
|
||||
let pos = {
|
||||
x: center.x + rx,
|
||||
y: center.y + ry
|
||||
};
|
||||
|
||||
return pos;
|
||||
};
|
||||
|
||||
eles.nodes().layoutPositions( this, options, getPos );
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
export default CircleLayout;
|
||||
1340
frontend/node_modules/cytoscape/src/extensions/layout/cose.mjs
generated
vendored
Normal file
1340
frontend/node_modules/cytoscape/src/extensions/layout/cose.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
43
frontend/node_modules/cytoscape/src/extensions/layout/random.mjs
generated
vendored
Normal file
43
frontend/node_modules/cytoscape/src/extensions/layout/random.mjs
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import * as util from '../../util/index.mjs';
|
||||
import * as math from '../../math.mjs';
|
||||
|
||||
let 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
|
||||
animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
|
||||
ready: undefined, // callback on layoutready
|
||||
stop: undefined, // callback on layoutstop
|
||||
transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
|
||||
};
|
||||
|
||||
function RandomLayout( options ){
|
||||
this.options = util.extend( {}, defaults, options );
|
||||
}
|
||||
|
||||
RandomLayout.prototype.run = function(){
|
||||
let options = this.options;
|
||||
let cy = options.cy;
|
||||
let eles = options.eles;
|
||||
|
||||
|
||||
let bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
|
||||
x1: 0, y1: 0, w: cy.width(), h: cy.height()
|
||||
} );
|
||||
|
||||
let getPos = function( node, i ){
|
||||
return {
|
||||
x: bb.x1 + Math.round( Math.random() * bb.w ),
|
||||
y: bb.y1 + Math.round( Math.random() * bb.h )
|
||||
};
|
||||
};
|
||||
|
||||
eles.nodes().layoutPositions( this, options, getPos );
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
export default RandomLayout;
|
||||
29
frontend/node_modules/cytoscape/src/extensions/renderer/base/coord-ele-math/index.mjs
generated
vendored
Normal file
29
frontend/node_modules/cytoscape/src/extensions/renderer/base/coord-ele-math/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as util from '../../../../util/index.mjs';
|
||||
|
||||
import coords from './coords.mjs';
|
||||
import edgeArrows from './edge-arrows.mjs';
|
||||
import edgeControlPoints from './edge-control-points.mjs';
|
||||
import edgeEndpoints from './edge-endpoints.mjs';
|
||||
import edgeProjection from './edge-projection.mjs';
|
||||
import labels from './labels.mjs';
|
||||
import nodes from './nodes.mjs';
|
||||
import renderedStyle from './rendered-style.mjs';
|
||||
import zOrdering from './z-ordering.mjs';
|
||||
|
||||
var BRp = {};
|
||||
|
||||
[
|
||||
coords,
|
||||
edgeArrows,
|
||||
edgeControlPoints,
|
||||
edgeEndpoints,
|
||||
edgeProjection,
|
||||
labels,
|
||||
nodes,
|
||||
renderedStyle,
|
||||
zOrdering
|
||||
].forEach(function( props ){
|
||||
util.extend( BRp, props );
|
||||
});
|
||||
|
||||
export default BRp;
|
||||
38
frontend/node_modules/cytoscape/src/extensions/renderer/base/images.mjs
generated
vendored
Normal file
38
frontend/node_modules/cytoscape/src/extensions/renderer/base/images.mjs
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
var BRp = {};
|
||||
|
||||
BRp.getCachedImage = function( url, crossOrigin, onLoad ){
|
||||
var r = this;
|
||||
var imageCache = r.imageCache = r.imageCache || {};
|
||||
var cache = imageCache[ url ];
|
||||
|
||||
if( cache ){
|
||||
if( !cache.image.complete ){
|
||||
cache.image.addEventListener('load', onLoad);
|
||||
}
|
||||
|
||||
return cache.image;
|
||||
} else {
|
||||
cache = imageCache[ url ] = imageCache[ url ] || {};
|
||||
|
||||
var image = cache.image = new Image(); // eslint-disable-line no-undef
|
||||
|
||||
image.addEventListener('load', onLoad);
|
||||
image.addEventListener('error', function(){ image.error = true; });
|
||||
|
||||
// #1582 safari doesn't load data uris with crossOrigin properly
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=123978
|
||||
var dataUriPrefix = 'data:';
|
||||
var isDataUri = url.substring( 0, dataUriPrefix.length ).toLowerCase() === dataUriPrefix;
|
||||
if( !isDataUri ){
|
||||
// if crossorigin is 'null'(stringified), then manually set it to null
|
||||
crossOrigin = crossOrigin === 'null' ? null : crossOrigin;
|
||||
image.crossOrigin = crossOrigin; // prevent tainted canvas
|
||||
}
|
||||
|
||||
image.src = url;
|
||||
|
||||
return image;
|
||||
}
|
||||
};
|
||||
|
||||
export default BRp;
|
||||
224
frontend/node_modules/cytoscape/src/extensions/renderer/base/index.mjs
generated
vendored
Normal file
224
frontend/node_modules/cytoscape/src/extensions/renderer/base/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
import * as util from '../../../util/index.mjs';
|
||||
import * as is from '../../../is.mjs';
|
||||
|
||||
import arrowShapes from './arrow-shapes.mjs';
|
||||
import coordEleMath from './coord-ele-math/index.mjs';
|
||||
import images from './images.mjs';
|
||||
import loadListeners from './load-listeners.mjs';
|
||||
import nodeShapes from './node-shapes.mjs';
|
||||
import redraw from './redraw.mjs';
|
||||
|
||||
var BaseRenderer = function( options ){ this.init( options ); };
|
||||
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;
|
||||
|
||||
var ctr = r.container = options.cy.container();
|
||||
var containerWindow = r.cy.window();
|
||||
|
||||
|
||||
// prepend a stylesheet in the head such that
|
||||
if( containerWindow ){
|
||||
var document = containerWindow.document;
|
||||
var head = document.head;
|
||||
var stylesheetId = '__________cytoscape_stylesheet';
|
||||
var className = '__________cytoscape_container';
|
||||
var stylesheetAlreadyExists = document.getElementById( stylesheetId ) != null;
|
||||
|
||||
if( ctr.className.indexOf( className ) < 0 ){
|
||||
ctr.className = ( ctr.className || '' ) + ' ' + className;
|
||||
}
|
||||
|
||||
if( !stylesheetAlreadyExists ){
|
||||
var stylesheet = document.createElement('style');
|
||||
|
||||
stylesheet.id = stylesheetId;
|
||||
stylesheet.textContent = '.'+className+' { position: relative; }';
|
||||
|
||||
head.insertBefore( stylesheet, head.children[0] ); // first so lowest priority
|
||||
}
|
||||
|
||||
var computedStyle = containerWindow.getComputedStyle( ctr );
|
||||
var position = computedStyle.getPropertyValue('position');
|
||||
|
||||
if( position === 'static' ){
|
||||
util.warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
|
||||
}
|
||||
}
|
||||
|
||||
r.selection = [ undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
|
||||
|
||||
r.bezierProjPcts = [ 0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95 ];
|
||||
|
||||
//--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.debug = options.debug;
|
||||
r.webgl = options.webgl;
|
||||
|
||||
r.hideEdgesOnViewport = options.hideEdgesOnViewport;
|
||||
r.textureOnViewport = options.textureOnViewport;
|
||||
r.wheelSensitivity = options.wheelSensitivity;
|
||||
r.motionBlurEnabled = options.motionBlur; // on by default
|
||||
r.forcedPixelRatio = is.number(options.pixelRatio) ? options.pixelRatio : null;
|
||||
r.motionBlur = options.motionBlur; // 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.beforeRenderCallbacks = [];
|
||||
r.beforeRenderPriorities = { // higher priority execs before lower one
|
||||
animations: 400,
|
||||
eleCalcs: 300,
|
||||
eleTxrDeq: 200,
|
||||
lyrTxrDeq: 150,
|
||||
lyrTxrSkip: 100,
|
||||
};
|
||||
|
||||
r.registerNodeShapes();
|
||||
r.registerArrowShapes();
|
||||
r.registerCalculationListeners();
|
||||
};
|
||||
|
||||
BRp.notify = function( eventName, eles ) {
|
||||
var r = this;
|
||||
var cy = r.cy;
|
||||
|
||||
// the renderer can't be notified after it's destroyed
|
||||
if( this.destroyed ){ return; }
|
||||
|
||||
if( eventName === 'init' ){
|
||||
r.load();
|
||||
return;
|
||||
}
|
||||
|
||||
if( eventName === 'destroy' ){
|
||||
r.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if(
|
||||
eventName === 'add'
|
||||
|| eventName === 'remove'
|
||||
|| (eventName === 'move' && cy.hasCompoundNodes())
|
||||
|| eventName === 'load'
|
||||
|| eventName === 'zorder'
|
||||
|| eventName === 'mount'
|
||||
){
|
||||
r.invalidateCachedZSortedEles();
|
||||
}
|
||||
|
||||
if( eventName === 'viewport' ){
|
||||
r.redrawHint( 'select', true );
|
||||
}
|
||||
|
||||
if( eventName === 'gc' ){
|
||||
r.redrawHint( 'gc', true );
|
||||
}
|
||||
|
||||
if( eventName === 'load' || eventName === 'resize' || eventName === 'mount' ){
|
||||
r.invalidateContainerClientCoordsCache();
|
||||
r.matchCanvasSize( r.container );
|
||||
}
|
||||
|
||||
r.redrawHint( 'eles', true );
|
||||
r.redrawHint( 'drag', true );
|
||||
|
||||
this.startRenderLoop();
|
||||
|
||||
this.redraw();
|
||||
};
|
||||
|
||||
BRp.destroy = function(){
|
||||
var r = this;
|
||||
|
||||
r.destroyed = true;
|
||||
|
||||
r.cy.stopAnimationLoop();
|
||||
|
||||
for( var i = 0; i < r.bindings.length; i++ ){
|
||||
var binding = r.bindings[ i ];
|
||||
var b = binding;
|
||||
var tgt = b.target;
|
||||
|
||||
( tgt.off || tgt.removeEventListener ).apply( tgt, b.args );
|
||||
}
|
||||
|
||||
r.bindings = [];
|
||||
r.beforeRenderCallbacks = [];
|
||||
r.onUpdateEleCalcsFns = [];
|
||||
|
||||
if( r.removeObserver ){
|
||||
r.removeObserver.disconnect();
|
||||
}
|
||||
|
||||
if( r.styleObserver ){
|
||||
r.styleObserver.disconnect();
|
||||
}
|
||||
|
||||
if( r.resizeObserver ){
|
||||
r.resizeObserver.disconnect();
|
||||
}
|
||||
|
||||
if( r.labelCalcDiv ){
|
||||
try {
|
||||
document.body.removeChild( r.labelCalcDiv ); // eslint-disable-line no-undef
|
||||
} catch( e ){
|
||||
// ie10 issue #1014
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BRp.isHeadless = function(){
|
||||
return false;
|
||||
};
|
||||
|
||||
[
|
||||
arrowShapes,
|
||||
coordEleMath,
|
||||
images,
|
||||
loadListeners,
|
||||
nodeShapes,
|
||||
redraw
|
||||
].forEach( function( props ){
|
||||
util.extend( BRp, props );
|
||||
} );
|
||||
|
||||
export default BR;
|
||||
431
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-edges.mjs
generated
vendored
Normal file
431
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-edges.mjs
generated
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
/* global Path2D */
|
||||
|
||||
import * as util from '../../../util/index.mjs';
|
||||
import {drawPreparedRoundCorner} from "../../../round.mjs";
|
||||
|
||||
let CRp = {};
|
||||
|
||||
CRp.drawEdge = function( context, edge, shiftToOriginWithBb, drawLabel = true, shouldDrawOverlay = true, shouldDrawOpacity = true ){
|
||||
let r = this;
|
||||
let rs = edge._private.rscratch;
|
||||
|
||||
if( shouldDrawOpacity && !edge.visible() ){ return; }
|
||||
|
||||
// if bezier ctrl pts can not be calculated, then die
|
||||
if( rs.badLine || rs.allpts == null || isNaN(rs.allpts[0]) ){ // isNaN in case edge is impossible and browser bugs (e.g. safari)
|
||||
return;
|
||||
}
|
||||
|
||||
let bb;
|
||||
if( shiftToOriginWithBb ){
|
||||
bb = shiftToOriginWithBb;
|
||||
|
||||
context.translate( -bb.x1, -bb.y1 );
|
||||
}
|
||||
|
||||
let opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
|
||||
let lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
|
||||
|
||||
let curveStyle = edge.pstyle('curve-style').value;
|
||||
let lineStyle = edge.pstyle('line-style').value;
|
||||
let edgeWidth = edge.pstyle('width').pfValue;
|
||||
let lineCap = edge.pstyle('line-cap').value;
|
||||
let lineOutlineWidth = edge.pstyle('line-outline-width').value;
|
||||
let lineOutlineColor = edge.pstyle('line-outline-color').value;
|
||||
|
||||
let effectiveLineOpacity = opacity * lineOpacity;
|
||||
// separate arrow opacity would require arrow-opacity property
|
||||
let effectiveArrowOpacity = opacity * lineOpacity;
|
||||
|
||||
let drawLine = ( strokeOpacity = effectiveLineOpacity) => {
|
||||
if (curveStyle === 'straight-triangle') {
|
||||
r.eleStrokeStyle( context, edge, strokeOpacity );
|
||||
r.drawEdgeTrianglePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts
|
||||
);
|
||||
} else {
|
||||
context.lineWidth = edgeWidth;
|
||||
context.lineCap = lineCap;
|
||||
|
||||
r.eleStrokeStyle( context, edge, strokeOpacity );
|
||||
r.drawEdgePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts,
|
||||
lineStyle
|
||||
);
|
||||
|
||||
context.lineCap = 'butt'; // reset for other drawing functions
|
||||
}
|
||||
};
|
||||
|
||||
let drawLineOutline = ( strokeOpacity = effectiveLineOpacity) => {
|
||||
context.lineWidth = edgeWidth + lineOutlineWidth;
|
||||
context.lineCap = lineCap;
|
||||
|
||||
if (lineOutlineWidth > 0) {
|
||||
r.colorStrokeStyle( context, lineOutlineColor[0], lineOutlineColor[1], lineOutlineColor[2], strokeOpacity );
|
||||
} else {
|
||||
// do not draw any lineOutline
|
||||
context.lineCap = 'butt'; // reset for other drawing functions
|
||||
return;
|
||||
}
|
||||
|
||||
if (curveStyle === 'straight-triangle') {
|
||||
r.drawEdgeTrianglePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts
|
||||
);
|
||||
} else {
|
||||
r.drawEdgePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts,
|
||||
lineStyle
|
||||
);
|
||||
|
||||
context.lineCap = 'butt'; // reset for other drawing functions
|
||||
}
|
||||
};
|
||||
|
||||
let drawOverlay = () => {
|
||||
if( !shouldDrawOverlay ){ return; }
|
||||
|
||||
r.drawEdgeOverlay( context, edge );
|
||||
};
|
||||
|
||||
let drawUnderlay = () => {
|
||||
if( !shouldDrawOverlay ){ return; }
|
||||
|
||||
r.drawEdgeUnderlay( context, edge );
|
||||
};
|
||||
|
||||
let drawArrows = ( arrowOpacity = effectiveArrowOpacity) => {
|
||||
r.drawArrowheads( context, edge, arrowOpacity );
|
||||
};
|
||||
|
||||
let drawText = () => {
|
||||
r.drawElementText( context, edge, null, drawLabel );
|
||||
};
|
||||
|
||||
context.lineJoin = 'round';
|
||||
|
||||
let ghost = edge.pstyle('ghost').value === 'yes';
|
||||
|
||||
if( ghost ){
|
||||
let gx = edge.pstyle('ghost-offset-x').pfValue;
|
||||
let gy = edge.pstyle('ghost-offset-y').pfValue;
|
||||
let ghostOpacity = edge.pstyle('ghost-opacity').value;
|
||||
let effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
|
||||
|
||||
context.translate( gx, gy );
|
||||
|
||||
drawLine( effectiveGhostOpacity );
|
||||
drawArrows( effectiveGhostOpacity );
|
||||
|
||||
context.translate( -gx, -gy );
|
||||
} else {
|
||||
drawLineOutline();
|
||||
}
|
||||
|
||||
drawUnderlay();
|
||||
drawLine();
|
||||
drawArrows();
|
||||
drawOverlay();
|
||||
drawText();
|
||||
|
||||
if( shiftToOriginWithBb ){
|
||||
context.translate( bb.x1, bb.y1 );
|
||||
}
|
||||
};
|
||||
|
||||
const drawEdgeOverlayUnderlay = function( overlayOrUnderlay ) {
|
||||
if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
|
||||
throw new Error('Invalid state');
|
||||
}
|
||||
|
||||
return function( context, edge ){
|
||||
if( !edge.visible() ){ return; }
|
||||
|
||||
let opacity = edge.pstyle(`${overlayOrUnderlay}-opacity`).value;
|
||||
|
||||
if( opacity === 0 ){ return; }
|
||||
|
||||
let r = this;
|
||||
let usePaths = r.usePaths();
|
||||
let rs = edge._private.rscratch;
|
||||
|
||||
let padding = edge.pstyle(`${overlayOrUnderlay}-padding`).pfValue;
|
||||
let width = 2 * padding;
|
||||
let color = edge.pstyle(`${overlayOrUnderlay}-color`).value;
|
||||
|
||||
context.lineWidth = width;
|
||||
|
||||
if( rs.edgeType === 'self' && !usePaths ){
|
||||
context.lineCap = 'butt';
|
||||
} else {
|
||||
context.lineCap = 'round';
|
||||
}
|
||||
|
||||
r.colorStrokeStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
r.drawEdgePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts,
|
||||
'solid'
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
CRp.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
|
||||
|
||||
CRp.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
|
||||
|
||||
|
||||
CRp.drawEdgePath = function( edge, context, pts, type ){
|
||||
let rs = edge._private.rscratch;
|
||||
let canvasCxt = context;
|
||||
let path;
|
||||
let pathCacheHit = false;
|
||||
let usePaths = this.usePaths();
|
||||
let lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
|
||||
let lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
|
||||
|
||||
if( usePaths ){
|
||||
let pathCacheKey = pts.join( '$' );
|
||||
let 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( lineDashPattern );
|
||||
canvasCxt.lineDashOffset = lineDashOffset;
|
||||
break;
|
||||
|
||||
case 'solid':
|
||||
canvasCxt.setLineDash( [ ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !pathCacheHit && !rs.badLine ){
|
||||
if( context.beginPath ){ context.beginPath(); }
|
||||
context.moveTo( pts[0], pts[1] );
|
||||
|
||||
switch( rs.edgeType ){
|
||||
case 'bezier':
|
||||
case 'self':
|
||||
case 'compound':
|
||||
case 'multibezier':
|
||||
for( let 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 'haystack':
|
||||
for( let i = 2; i + 1 < pts.length; i += 2 ) {
|
||||
context.lineTo( pts[ i ], pts[ i + 1] );
|
||||
}
|
||||
break;
|
||||
case 'segments':
|
||||
if (rs.isRound) {
|
||||
for( let corner of rs.roundCorners ){
|
||||
drawPreparedRoundCorner(context, corner);
|
||||
}
|
||||
context.lineTo( pts[ pts.length - 2 ], pts[ pts.length - 1] );
|
||||
} else {
|
||||
for( let 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.drawEdgeTrianglePath = function( edge, context, pts ){
|
||||
// use line stroke style for triangle fill style
|
||||
context.fillStyle = context.strokeStyle;
|
||||
|
||||
let edgeWidth = edge.pstyle('width').pfValue;
|
||||
|
||||
for( let i = 0; i + 1 < pts.length; i += 2 ){
|
||||
const vector = [ pts[ i + 2 ] - pts[ i ], pts[ i + 3 ] - pts[ i + 1 ] ];
|
||||
const length = Math.sqrt( vector[0] * vector[0] + vector[1] * vector[1] );
|
||||
const normal = [ vector[1] / length, -vector[0] / length ];
|
||||
const triangleHead = [ normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2 ];
|
||||
|
||||
context.beginPath();
|
||||
context.moveTo( pts[ i ] - triangleHead[0], pts[ i + 1 ] - triangleHead[1] );
|
||||
context.lineTo( pts[ i ] + triangleHead[0], pts[ i + 1 ] + triangleHead[1] );
|
||||
context.lineTo( pts[ i + 2 ], pts[ i + 3 ] );
|
||||
context.closePath();
|
||||
context.fill();
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawArrowheads = function( context, edge, opacity ){
|
||||
let rs = edge._private.rscratch;
|
||||
let isHaystack = rs.edgeType === 'haystack';
|
||||
|
||||
if( !isHaystack ){
|
||||
this.drawArrowhead( context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity );
|
||||
}
|
||||
|
||||
this.drawArrowhead( context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity );
|
||||
|
||||
this.drawArrowhead( context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity );
|
||||
|
||||
if( !isHaystack ){
|
||||
this.drawArrowhead( context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawArrowhead = function( context, edge, prefix, x, y, angle, opacity ){
|
||||
if( isNaN( x ) || x == null || isNaN( y ) || y == null || isNaN( angle ) || angle == null ){ return; }
|
||||
|
||||
let self = this;
|
||||
let arrowShape = edge.pstyle( prefix + '-arrow-shape' ).value;
|
||||
if( arrowShape === 'none' ) { return; }
|
||||
|
||||
let arrowClearFill = edge.pstyle( prefix + '-arrow-fill' ).value === 'hollow' ? 'both' : 'filled';
|
||||
let arrowFill = edge.pstyle( prefix + '-arrow-fill' ).value;
|
||||
let edgeWidth = edge.pstyle( 'width' ).pfValue;
|
||||
|
||||
let pArrowWidth = edge.pstyle( prefix + '-arrow-width' );
|
||||
let arrowWidth = pArrowWidth.value === 'match-line' ? edgeWidth : pArrowWidth.pfValue;
|
||||
if (pArrowWidth.units === '%') arrowWidth *= edgeWidth;
|
||||
|
||||
let edgeOpacity = edge.pstyle( 'opacity' ).value;
|
||||
|
||||
if( opacity === undefined ){
|
||||
opacity = edgeOpacity;
|
||||
}
|
||||
|
||||
let gco = context.globalCompositeOperation;
|
||||
|
||||
if( opacity !== 1 || arrowFill === 'hollow' ){ // then extra clear is needed
|
||||
context.globalCompositeOperation = 'destination-out';
|
||||
|
||||
self.colorFillStyle( context, 255, 255, 255, 1 );
|
||||
self.colorStrokeStyle( context, 255, 255, 255, 1 );
|
||||
|
||||
self.drawArrowShape( edge, context,
|
||||
arrowClearFill, edgeWidth, arrowShape, arrowWidth, x, y, angle
|
||||
);
|
||||
|
||||
context.globalCompositeOperation = gco;
|
||||
} // otherwise, the opaque arrow clears it for free :)
|
||||
|
||||
let color = edge.pstyle( prefix + '-arrow-color' ).value;
|
||||
self.colorFillStyle( context, color[0], color[1], color[2], opacity );
|
||||
self.colorStrokeStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
self.drawArrowShape( edge, context,
|
||||
arrowFill, edgeWidth, arrowShape, arrowWidth, x, y, angle
|
||||
);
|
||||
};
|
||||
|
||||
CRp.drawArrowShape = function( edge, context, fill, edgeWidth, shape, shapeWidth, x, y, angle ){
|
||||
let r = this;
|
||||
let usePaths = this.usePaths() && shape !== 'triangle-cross';
|
||||
let pathCacheHit = false;
|
||||
let path;
|
||||
let canvasContext = context;
|
||||
let translation = { x, y };
|
||||
let scale = edge.pstyle( 'arrow-scale' ).value;
|
||||
let size = this.getArrowWidth( edgeWidth, scale );
|
||||
let shapeImpl = r.arrowShapes[ shape ];
|
||||
|
||||
if( usePaths ){
|
||||
let cache = r.arrowPathCache = r.arrowPathCache || [];
|
||||
let key = util.hashString(shape);
|
||||
let cachedPath = cache[ key ];
|
||||
|
||||
if( cachedPath != null ){
|
||||
path = context = cachedPath;
|
||||
pathCacheHit = true;
|
||||
} else {
|
||||
path = context = new Path2D();
|
||||
cache[ key ] = path;
|
||||
}
|
||||
}
|
||||
|
||||
if( !pathCacheHit ){
|
||||
if( context.beginPath ){ context.beginPath(); }
|
||||
if( usePaths ){ // store in the path cache with values easily manipulated later
|
||||
shapeImpl.draw( context, 1, 0, { x: 0, y: 0 }, 1 );
|
||||
} else {
|
||||
shapeImpl.draw( context, size, angle, translation, edgeWidth );
|
||||
}
|
||||
if( context.closePath ){ context.closePath(); }
|
||||
}
|
||||
|
||||
context = canvasContext;
|
||||
|
||||
if( usePaths ){ // set transform to arrow position/orientation
|
||||
context.translate( x, y );
|
||||
context.rotate( angle );
|
||||
context.scale( size, size );
|
||||
}
|
||||
|
||||
if( fill === 'filled' || fill === 'both' ){
|
||||
if( usePaths ){
|
||||
context.fill( path );
|
||||
} else {
|
||||
context.fill();
|
||||
}
|
||||
}
|
||||
|
||||
if( fill === 'hollow' || fill === 'both' ){
|
||||
context.lineWidth = shapeWidth / (usePaths ? size : 1);
|
||||
context.lineJoin = 'miter';
|
||||
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
if( usePaths ){ // reset transform by applying inverse
|
||||
context.scale( 1/size, 1/size );
|
||||
context.rotate( -angle );
|
||||
context.translate( -x, -y );
|
||||
}
|
||||
};
|
||||
|
||||
export default CRp;
|
||||
218
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-elements.mjs
generated
vendored
Normal file
218
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-elements.mjs
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
import * as math from '../../../math.mjs';
|
||||
|
||||
let CRp = {};
|
||||
|
||||
CRp.drawElement = function( context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity ){
|
||||
let r = this;
|
||||
|
||||
if( ele.isNode() ){
|
||||
r.drawNode( context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity );
|
||||
} else {
|
||||
r.drawEdge( context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawElementOverlay = function( context, ele ){
|
||||
let r = this;
|
||||
|
||||
if( ele.isNode() ){
|
||||
r.drawNodeOverlay( context, ele );
|
||||
} else {
|
||||
r.drawEdgeOverlay( context, ele );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawElementUnderlay = function( context, ele ){
|
||||
let r = this;
|
||||
|
||||
if( ele.isNode() ){
|
||||
r.drawNodeUnderlay( context, ele );
|
||||
} else {
|
||||
r.drawEdgeUnderlay( context, ele );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawCachedElementPortion = function( context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity ){
|
||||
let r = this;
|
||||
let bb = eleTxrCache.getBoundingBox(ele);
|
||||
|
||||
if( bb.w === 0 || bb.h === 0 ){ return; } // ignore zero size case
|
||||
|
||||
let eleCache = eleTxrCache.getElement( ele, bb, pxRatio, lvl, reason );
|
||||
|
||||
if( eleCache != null ){
|
||||
let opacity = getOpacity(r, ele);
|
||||
|
||||
if( opacity === 0 ){ return; }
|
||||
|
||||
let theta = getRotation(r, ele);
|
||||
let { x1, y1, w, h } = bb;
|
||||
let x, y, sx, sy, smooth;
|
||||
|
||||
if( theta !== 0 ){
|
||||
let rotPt = eleTxrCache.getRotationPoint(ele);
|
||||
|
||||
sx = rotPt.x;
|
||||
sy = rotPt.y;
|
||||
|
||||
context.translate(sx, sy);
|
||||
context.rotate(theta);
|
||||
|
||||
smooth = r.getImgSmoothing(context);
|
||||
|
||||
if( !smooth ){ r.setImgSmoothing(context, true); }
|
||||
|
||||
let off = eleTxrCache.getRotationOffset(ele);
|
||||
|
||||
x = off.x;
|
||||
y = off.y;
|
||||
} else {
|
||||
x = x1;
|
||||
y = y1;
|
||||
}
|
||||
|
||||
let oldGlobalAlpha;
|
||||
|
||||
if( opacity !== 1 ){
|
||||
oldGlobalAlpha = context.globalAlpha;
|
||||
context.globalAlpha = oldGlobalAlpha * opacity;
|
||||
}
|
||||
|
||||
context.drawImage( eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h );
|
||||
|
||||
if( opacity !== 1 ){
|
||||
context.globalAlpha = oldGlobalAlpha;
|
||||
}
|
||||
|
||||
if( theta !== 0 ){
|
||||
context.rotate(-theta);
|
||||
context.translate(-sx, -sy);
|
||||
|
||||
if( !smooth ){ r.setImgSmoothing(context, false); }
|
||||
}
|
||||
} else {
|
||||
eleTxrCache.drawElement( context, ele ); // direct draw fallback
|
||||
}
|
||||
};
|
||||
|
||||
const getZeroRotation = () => 0;
|
||||
const getLabelRotation = (r, ele) => r.getTextAngle(ele, null);
|
||||
const getSourceLabelRotation = (r, ele) => r.getTextAngle(ele, 'source');
|
||||
const getTargetLabelRotation = (r, ele) => r.getTextAngle(ele, 'target');
|
||||
const getOpacity = (r, ele) => ele.effectiveOpacity();
|
||||
const getTextOpacity = (e, ele) => ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
|
||||
|
||||
CRp.drawCachedElement = function( context, ele, pxRatio, extent, lvl, requestHighQuality ){
|
||||
let r = this;
|
||||
let { eleTxrCache, lblTxrCache, slbTxrCache, tlbTxrCache } = r.data;
|
||||
|
||||
let bb = ele.boundingBox();
|
||||
let reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
|
||||
|
||||
if( bb.w === 0 || bb.h === 0 || !ele.visible() ){ return; }
|
||||
|
||||
if( !extent || math.boundingBoxesIntersect( bb, extent ) ){
|
||||
let isEdge = ele.isEdge();
|
||||
let badLine = ele.element()._private.rscratch.badLine;
|
||||
|
||||
r.drawElementUnderlay( context, ele );
|
||||
|
||||
r.drawCachedElementPortion( context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity );
|
||||
|
||||
if( !isEdge || !badLine ){
|
||||
r.drawCachedElementPortion( context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity );
|
||||
}
|
||||
|
||||
if( isEdge && !badLine ){
|
||||
r.drawCachedElementPortion( context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity );
|
||||
r.drawCachedElementPortion( context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity );
|
||||
}
|
||||
|
||||
r.drawElementOverlay( context, ele );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawElements = function( context, eles ){
|
||||
let r = this;
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
r.drawElement( context, ele );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawCachedElements = function( context, eles, pxRatio, extent ){
|
||||
let r = this;
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
r.drawCachedElement( context, ele, pxRatio, extent );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawCachedNodes = function( context, eles, pxRatio, extent ){
|
||||
let r = this;
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
if( !ele.isNode() ){ continue; }
|
||||
|
||||
r.drawCachedElement( context, ele, pxRatio, extent );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawLayeredElements = function( context, eles, pxRatio, extent ){
|
||||
let r = this;
|
||||
|
||||
let layers = r.data.lyrTxrCache.getLayers( eles, pxRatio );
|
||||
|
||||
if( layers ){
|
||||
for( let i = 0; i < layers.length; i++ ){
|
||||
let layer = layers[i];
|
||||
let bb = layer.bb;
|
||||
|
||||
if( bb.w === 0 || bb.h === 0 ){ continue; }
|
||||
|
||||
context.drawImage( layer.canvas, bb.x1, bb.y1, bb.w, bb.h );
|
||||
}
|
||||
} else { // fall back on plain caching if no layers
|
||||
r.drawCachedElements( context, eles, pxRatio, extent );
|
||||
}
|
||||
};
|
||||
|
||||
if( process.env.NODE_ENV !== 'production' ){
|
||||
CRp.drawDebugPoints = function( context, eles ){
|
||||
let draw = function( x, y, color ){
|
||||
context.fillStyle = color;
|
||||
context.fillRect( x - 1, y - 1, 3, 3 );
|
||||
};
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[i];
|
||||
let rs = ele._private.rscratch;
|
||||
|
||||
if( ele.isNode() ){
|
||||
let p = ele.position();
|
||||
|
||||
draw( rs.labelX, rs.labelY, 'red' );
|
||||
draw( p.x, p.y, 'magenta' );
|
||||
} else {
|
||||
let pts = rs.allpts;
|
||||
|
||||
for( let j = 0; j + 1 < pts.length; j += 2 ){
|
||||
let x = pts[ j ];
|
||||
let y = pts[ j + 1 ];
|
||||
|
||||
draw( x, y, 'cyan' );
|
||||
}
|
||||
|
||||
draw( rs.midX, rs.midY, 'yellow' );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default CRp;
|
||||
804
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-nodes.mjs
generated
vendored
Normal file
804
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-nodes.mjs
generated
vendored
Normal file
@@ -0,0 +1,804 @@
|
||||
/* global Path2D */
|
||||
|
||||
import * as is from '../../../is.mjs';
|
||||
import {expandPolygon, joinLines} from '../../../math.mjs';
|
||||
import * as util from '../../../util/index.mjs';
|
||||
import * as round from "../../../round.mjs";
|
||||
import * as math from "../../../math.mjs";
|
||||
|
||||
let CRp = {};
|
||||
|
||||
CRp.drawNode = function( context, node, shiftToOriginWithBb, drawLabel = true, shouldDrawOverlay = true, shouldDrawOpacity = true ){
|
||||
let r = this;
|
||||
let nodeWidth, nodeHeight;
|
||||
let _p = node._private;
|
||||
let rs = _p.rscratch;
|
||||
let pos = node.position();
|
||||
|
||||
if( !is.number( pos.x ) || !is.number( pos.y ) ){
|
||||
return; // can't draw node with undefined position
|
||||
}
|
||||
|
||||
if( shouldDrawOpacity && !node.visible() ){ return; }
|
||||
|
||||
let eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
|
||||
|
||||
let usePaths = r.usePaths();
|
||||
let path;
|
||||
let pathCacheHit = false;
|
||||
|
||||
let padding = node.padding();
|
||||
|
||||
nodeWidth = node.width() + 2 * padding;
|
||||
nodeHeight = node.height() + 2 * padding;
|
||||
|
||||
//
|
||||
// setup shift
|
||||
|
||||
let bb;
|
||||
if( shiftToOriginWithBb ){
|
||||
bb = shiftToOriginWithBb;
|
||||
|
||||
context.translate( -bb.x1, -bb.y1 );
|
||||
}
|
||||
|
||||
//
|
||||
// load bg image
|
||||
|
||||
let bgImgProp = node.pstyle( 'background-image' );
|
||||
let urls = bgImgProp.value;
|
||||
let urlDefined = new Array( urls.length );
|
||||
let image = new Array( urls.length );
|
||||
let numImages = 0;
|
||||
for( let i = 0; i < urls.length; i++ ){
|
||||
let url = urls[i];
|
||||
let defd = urlDefined[i] = url != null && url !== 'none';
|
||||
|
||||
if( defd ){
|
||||
let bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
|
||||
|
||||
numImages++;
|
||||
|
||||
// get image, and if not loaded then ask to redraw when later loaded
|
||||
image[i] = r.getCachedImage( url, bgImgCrossOrigin, function(){
|
||||
_p.backgroundTimestamp = Date.now();
|
||||
|
||||
node.emitAndNotify('background');
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// setup styles
|
||||
|
||||
let darkness = node.pstyle('background-blacken').value;
|
||||
let borderWidth = node.pstyle('border-width').pfValue;
|
||||
let bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
|
||||
let borderColor = node.pstyle('border-color').value;
|
||||
let borderStyle = node.pstyle('border-style').value;
|
||||
let borderJoin = node.pstyle('border-join').value;
|
||||
let borderCap = node.pstyle('border-cap').value;
|
||||
let borderPosition = node.pstyle('border-position').value;
|
||||
let borderPattern = node.pstyle('border-dash-pattern').pfValue;
|
||||
let borderOffset = node.pstyle('border-dash-offset').pfValue;
|
||||
let borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
|
||||
let outlineWidth = node.pstyle('outline-width').pfValue;
|
||||
let outlineColor = node.pstyle('outline-color').value;
|
||||
let outlineStyle = node.pstyle('outline-style').value;
|
||||
let outlineOpacity = node.pstyle('outline-opacity').value * eleOpacity;
|
||||
let outlineOffset = node.pstyle('outline-offset').value;
|
||||
let cornerRadius = node.pstyle('corner-radius').value;
|
||||
if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
|
||||
|
||||
let setupShapeColor = ( bgOpy = bgOpacity ) => {
|
||||
r.eleFillStyle( context, node, bgOpy );
|
||||
};
|
||||
|
||||
let setupBorderColor = ( bdrOpy = borderOpacity ) => {
|
||||
r.colorStrokeStyle( context, borderColor[0], borderColor[1], borderColor[2], bdrOpy );
|
||||
};
|
||||
|
||||
let setupOutlineColor = ( otlnOpy = outlineOpacity ) => {
|
||||
r.colorStrokeStyle( context, outlineColor[0], outlineColor[1], outlineColor[2], otlnOpy );
|
||||
};
|
||||
|
||||
//
|
||||
// setup shape
|
||||
|
||||
let getPath = (width, height, shape, points) => {
|
||||
let pathCache = r.nodePathCache = r.nodePathCache || [];
|
||||
|
||||
let key = util.hashStrings(
|
||||
shape === 'polygon' ? shape + ',' + points.join(',') : shape,
|
||||
'' + height,
|
||||
'' + width,
|
||||
'' + cornerRadius
|
||||
);
|
||||
|
||||
let cachedPath = pathCache[ key ];
|
||||
let path;
|
||||
let cacheHit = false;
|
||||
|
||||
if( cachedPath != null ){
|
||||
path = cachedPath;
|
||||
cacheHit = true;
|
||||
rs.pathCache = path;
|
||||
} else {
|
||||
path = new Path2D();
|
||||
pathCache[ key ] = rs.pathCache = path;
|
||||
}
|
||||
|
||||
return {
|
||||
path,
|
||||
cacheHit
|
||||
};
|
||||
};
|
||||
|
||||
let styleShape = node.pstyle('shape').strValue;
|
||||
let shapePts = node.pstyle('shape-polygon-points').pfValue;
|
||||
|
||||
if( usePaths ){
|
||||
context.translate( pos.x, pos.y );
|
||||
|
||||
const shapePath = getPath(nodeWidth, nodeHeight, styleShape, shapePts);
|
||||
path = shapePath.path;
|
||||
pathCacheHit = shapePath.cacheHit;
|
||||
}
|
||||
|
||||
let drawShape = () => {
|
||||
if( !pathCacheHit ){
|
||||
|
||||
let npos = pos;
|
||||
|
||||
if( usePaths ){
|
||||
npos = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
}
|
||||
|
||||
r.nodeShapes[ r.getNodeShape( node ) ].draw(
|
||||
( path || context ),
|
||||
npos.x,
|
||||
npos.y,
|
||||
nodeWidth,
|
||||
nodeHeight, cornerRadius, rs );
|
||||
}
|
||||
|
||||
if( usePaths ){
|
||||
context.fill( path );
|
||||
} else {
|
||||
context.fill();
|
||||
}
|
||||
};
|
||||
|
||||
let drawImages = ( nodeOpacity = eleOpacity, inside = true ) => {
|
||||
let prevBging = _p.backgrounding;
|
||||
let totalCompleted = 0;
|
||||
|
||||
for( let i = 0; i < image.length; i++ ){
|
||||
const bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', i);
|
||||
if( inside && bgContainment === 'over' || !inside && bgContainment === 'inside' ){
|
||||
totalCompleted++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( urlDefined[i] && image[i].complete && !image[i].error ){
|
||||
totalCompleted++;
|
||||
r.drawInscribedImage( context, image[i], node, i, nodeOpacity );
|
||||
}
|
||||
}
|
||||
|
||||
_p.backgrounding = !(totalCompleted === numImages);
|
||||
if( prevBging !== _p.backgrounding ){ // update style b/c :backgrounding state changed
|
||||
node.updateStyle( false );
|
||||
}
|
||||
};
|
||||
|
||||
let drawPie = ( redrawShape = false, pieOpacity = eleOpacity ) => {
|
||||
if( r.hasPie( node ) ){
|
||||
r.drawPie( context, node, pieOpacity );
|
||||
|
||||
// redraw/restore path if steps after pie need it
|
||||
if( redrawShape ){
|
||||
|
||||
if( !usePaths ){
|
||||
r.nodeShapes[ r.getNodeShape( node ) ].draw(
|
||||
context,
|
||||
pos.x,
|
||||
pos.y,
|
||||
nodeWidth,
|
||||
nodeHeight, cornerRadius, rs );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let drawStripe = (redrawShape = false, stripeOpacity = eleOpacity) => {
|
||||
if( r.hasStripe( node ) ){
|
||||
context.save();
|
||||
|
||||
if (usePaths) {
|
||||
context.clip( rs.pathCache );
|
||||
} else {
|
||||
r.nodeShapes[ r.getNodeShape( node ) ].draw(
|
||||
context,
|
||||
pos.x,
|
||||
pos.y,
|
||||
nodeWidth,
|
||||
nodeHeight, cornerRadius, rs );
|
||||
context.clip();
|
||||
}
|
||||
|
||||
r.drawStripe( context, node, stripeOpacity );
|
||||
|
||||
context.restore();
|
||||
|
||||
// redraw/restore path if steps after stripes need it
|
||||
if( redrawShape ){
|
||||
|
||||
if( !usePaths ){
|
||||
r.nodeShapes[ r.getNodeShape( node ) ].draw(
|
||||
context,
|
||||
pos.x,
|
||||
pos.y,
|
||||
nodeWidth,
|
||||
nodeHeight, cornerRadius, rs );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let darken = ( darkenOpacity = eleOpacity ) => {
|
||||
let opacity = ( darkness > 0 ? darkness : -darkness ) * darkenOpacity;
|
||||
let c = darkness > 0 ? 0 : 255;
|
||||
|
||||
if( darkness !== 0 ){
|
||||
r.colorFillStyle( context, c, c, c, opacity );
|
||||
|
||||
if( usePaths ){
|
||||
context.fill( path );
|
||||
} else {
|
||||
context.fill();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let drawBorder = () => {
|
||||
if( borderWidth > 0 ){
|
||||
|
||||
context.lineWidth = borderWidth;
|
||||
context.lineCap = borderCap;
|
||||
context.lineJoin = borderJoin;
|
||||
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
switch( borderStyle ){
|
||||
case 'dotted':
|
||||
context.setLineDash( [ 1, 1 ] );
|
||||
break;
|
||||
|
||||
case 'dashed':
|
||||
context.setLineDash( borderPattern );
|
||||
context.lineDashOffset = borderOffset;
|
||||
break;
|
||||
|
||||
case 'solid':
|
||||
case 'double':
|
||||
context.setLineDash( [ ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( borderPosition !== 'center') {
|
||||
context.save();
|
||||
context.lineWidth *= 2;
|
||||
if (borderPosition === 'inside') {
|
||||
usePaths ? context.clip(path) : context.clip();
|
||||
} else {
|
||||
const region = new Path2D();
|
||||
region.rect(
|
||||
-nodeWidth / 2 - borderWidth,
|
||||
-nodeHeight / 2 - borderWidth,
|
||||
nodeWidth + 2 * borderWidth,
|
||||
nodeHeight + 2 * borderWidth
|
||||
);
|
||||
region.addPath(path);
|
||||
context.clip(region, 'evenodd');
|
||||
}
|
||||
usePaths ? context.stroke(path) : context.stroke();
|
||||
context.restore();
|
||||
} else {
|
||||
usePaths ? context.stroke(path) : context.stroke();
|
||||
}
|
||||
|
||||
if( borderStyle === 'double' ){
|
||||
context.lineWidth = borderWidth / 3;
|
||||
|
||||
let gco = context.globalCompositeOperation;
|
||||
context.globalCompositeOperation = 'destination-out';
|
||||
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
context.globalCompositeOperation = gco;
|
||||
}
|
||||
|
||||
// reset in case we changed the border style
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
context.setLineDash( [ ] );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let drawOutline = () => {
|
||||
if( outlineWidth > 0 ){
|
||||
context.lineWidth = outlineWidth;
|
||||
context.lineCap = 'butt';
|
||||
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
switch( outlineStyle ){
|
||||
case 'dotted':
|
||||
context.setLineDash( [ 1, 1 ] );
|
||||
break;
|
||||
|
||||
case 'dashed':
|
||||
context.setLineDash( [ 4, 2 ] );
|
||||
break;
|
||||
|
||||
case 'solid':
|
||||
case 'double':
|
||||
context.setLineDash( [ ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let npos = pos;
|
||||
|
||||
if( usePaths ){
|
||||
npos = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
}
|
||||
|
||||
let shape = r.getNodeShape( node );
|
||||
|
||||
let bWidth = borderWidth;
|
||||
if( borderPosition === 'inside' ) bWidth = 0;
|
||||
if( borderPosition === 'outside' ) bWidth *= 2;
|
||||
|
||||
let scaleX = (nodeWidth + bWidth + (outlineWidth + outlineOffset)) / nodeWidth;
|
||||
let scaleY = (nodeHeight + bWidth + (outlineWidth + outlineOffset)) / nodeHeight;
|
||||
let sWidth = nodeWidth * scaleX;
|
||||
let sHeight = nodeHeight * scaleY;
|
||||
|
||||
let points = r.nodeShapes[ shape ].points;
|
||||
let path;
|
||||
|
||||
if (usePaths) {
|
||||
let outlinePath = getPath(sWidth, sHeight, shape, points);
|
||||
path = outlinePath.path;
|
||||
}
|
||||
|
||||
// draw the outline path, either by using expanded points or by scaling
|
||||
// the dimensions, depending on shape
|
||||
if (shape === "ellipse") {
|
||||
r.drawEllipsePath(path || context, npos.x, npos.y, sWidth, sHeight);
|
||||
} else if ([
|
||||
'round-diamond', 'round-heptagon', 'round-hexagon', 'round-octagon',
|
||||
'round-pentagon', 'round-polygon', 'round-triangle', 'round-tag'
|
||||
].includes(shape)) {
|
||||
let sMult = 0;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
if (shape === 'round-diamond') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.4;
|
||||
} else if (shape === 'round-heptagon') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.075;
|
||||
offsetY = -(bWidth/2 + outlineOffset + outlineWidth) / 35;
|
||||
} else if (shape === 'round-hexagon') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
|
||||
} else if (shape === 'round-pentagon') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.13;
|
||||
offsetY = -(bWidth/2 + outlineOffset + outlineWidth) / 15;
|
||||
} else if (shape === 'round-tag') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
|
||||
offsetX = (bWidth/2 + outlineWidth + outlineOffset) * .07;
|
||||
} else if (shape === 'round-triangle') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * (Math.PI/2);
|
||||
offsetY = -(bWidth + outlineOffset/2 + outlineWidth) / Math.PI;
|
||||
}
|
||||
|
||||
if (sMult !== 0) {
|
||||
scaleX = (nodeWidth + sMult)/nodeWidth;
|
||||
sWidth = nodeWidth * scaleX;
|
||||
if ( ! ['round-hexagon', 'round-tag'].includes(shape) ) {
|
||||
scaleY = (nodeHeight + sMult)/nodeHeight;
|
||||
sHeight = nodeHeight * scaleY;
|
||||
}
|
||||
}
|
||||
|
||||
cornerRadius = cornerRadius === 'auto' ? math.getRoundPolygonRadius( sWidth, sHeight ) : cornerRadius;
|
||||
|
||||
const halfW = sWidth / 2;
|
||||
const halfH = sHeight / 2;
|
||||
const radius = cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2;
|
||||
const p = new Array( points.length / 2 );
|
||||
const corners = new Array( points.length / 2 );
|
||||
|
||||
for ( let i = 0; i < points.length / 2; i++ ){
|
||||
p[i] = {
|
||||
x: npos.x + offsetX + halfW * points[ i * 2 ],
|
||||
y: npos.y + offsetY + halfH * points[ i * 2 + 1 ]
|
||||
};
|
||||
}
|
||||
|
||||
let i, p1, p2, p3, len = p.length;
|
||||
|
||||
p1 = p[ len - 1 ];
|
||||
// for each point
|
||||
for( i = 0; i < len; i++ ){
|
||||
p2 = p[ (i) % len ];
|
||||
p3 = p[ (i + 1) % len ];
|
||||
corners[ i ] = round.getRoundCorner( p1, p2, p3, radius );
|
||||
p1 = p2;
|
||||
p2 = p3;
|
||||
}
|
||||
|
||||
r.drawRoundPolygonPath(path || context, npos.x + offsetX, npos.y + offsetY, nodeWidth * scaleX, nodeHeight * scaleY, points, corners );
|
||||
} else if (['roundrectangle', 'round-rectangle'].includes(shape)) {
|
||||
cornerRadius = cornerRadius === 'auto' ? math.getRoundRectangleRadius( sWidth, sHeight ) : cornerRadius;
|
||||
r.drawRoundRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2 );
|
||||
} else if (['cutrectangle', 'cut-rectangle'].includes(shape)) {
|
||||
cornerRadius = cornerRadius === 'auto' ? math.getCutRectangleCornerLength( sWidth, sHeight ) : cornerRadius;
|
||||
r.drawCutRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, null ,cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 4 );
|
||||
} else if (['bottomroundrectangle', 'bottom-round-rectangle'].includes(shape)) {
|
||||
cornerRadius = cornerRadius === 'auto' ? math.getRoundRectangleRadius( sWidth, sHeight ) : cornerRadius;
|
||||
r.drawBottomRoundRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2 );
|
||||
} else if (shape === "barrel") {
|
||||
r.drawBarrelPath(path || context, npos.x, npos.y, sWidth, sHeight);
|
||||
} else if (shape.startsWith("polygon") || ['rhomboid', 'right-rhomboid', 'round-tag', 'tag', 'vee'].includes(shape)) {
|
||||
let pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
|
||||
points = joinLines(expandPolygon(points, pad));
|
||||
r.drawPolygonPath(path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
|
||||
} else {
|
||||
let pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
|
||||
points = joinLines(expandPolygon(points, -pad));
|
||||
r.drawPolygonPath(path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
|
||||
}
|
||||
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
if( outlineStyle === 'double' ){
|
||||
context.lineWidth = bWidth / 3;
|
||||
|
||||
let gco = context.globalCompositeOperation;
|
||||
context.globalCompositeOperation = 'destination-out';
|
||||
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
context.globalCompositeOperation = gco;
|
||||
}
|
||||
|
||||
// reset in case we changed the border style
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
context.setLineDash( [ ] );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let drawOverlay = () => {
|
||||
if( shouldDrawOverlay ){
|
||||
r.drawNodeOverlay( context, node, pos, nodeWidth, nodeHeight );
|
||||
}
|
||||
};
|
||||
|
||||
let drawUnderlay = () => {
|
||||
if( shouldDrawOverlay ){
|
||||
r.drawNodeUnderlay( context, node, pos, nodeWidth, nodeHeight );
|
||||
}
|
||||
};
|
||||
|
||||
let drawText = () => {
|
||||
r.drawElementText( context, node, null, drawLabel );
|
||||
};
|
||||
|
||||
let ghost = node.pstyle('ghost').value === 'yes';
|
||||
|
||||
if( ghost ){
|
||||
let gx = node.pstyle('ghost-offset-x').pfValue;
|
||||
let gy = node.pstyle('ghost-offset-y').pfValue;
|
||||
let ghostOpacity = node.pstyle('ghost-opacity').value;
|
||||
let effGhostOpacity = ghostOpacity * eleOpacity;
|
||||
|
||||
context.translate( gx, gy );
|
||||
|
||||
setupOutlineColor();
|
||||
drawOutline();
|
||||
setupShapeColor( ghostOpacity * bgOpacity );
|
||||
drawShape();
|
||||
drawImages( effGhostOpacity, true );
|
||||
setupBorderColor( ghostOpacity * borderOpacity );
|
||||
drawBorder();
|
||||
drawPie( darkness !== 0 || borderWidth !== 0 );
|
||||
drawStripe( darkness !== 0 || borderWidth !== 0 );
|
||||
drawImages( effGhostOpacity, false );
|
||||
darken( effGhostOpacity );
|
||||
|
||||
context.translate( -gx, -gy );
|
||||
}
|
||||
|
||||
if( usePaths ){
|
||||
context.translate( -pos.x, -pos.y );
|
||||
}
|
||||
drawUnderlay();
|
||||
if( usePaths ){
|
||||
context.translate( pos.x, pos.y );
|
||||
}
|
||||
|
||||
setupOutlineColor();
|
||||
drawOutline();
|
||||
setupShapeColor();
|
||||
drawShape();
|
||||
drawImages(eleOpacity, true);
|
||||
setupBorderColor();
|
||||
drawBorder();
|
||||
drawPie( darkness !== 0 || borderWidth !== 0 );
|
||||
drawStripe( darkness !== 0 || borderWidth !== 0 );
|
||||
drawImages(eleOpacity, false);
|
||||
|
||||
darken();
|
||||
|
||||
if( usePaths ){
|
||||
context.translate( -pos.x, -pos.y );
|
||||
}
|
||||
|
||||
drawText();
|
||||
|
||||
drawOverlay();
|
||||
|
||||
//
|
||||
// clean up shift
|
||||
|
||||
if( shiftToOriginWithBb ){
|
||||
context.translate( bb.x1, bb.y1 );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const drawNodeOverlayUnderlay = function( overlayOrUnderlay ) {
|
||||
if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
|
||||
throw new Error('Invalid state');
|
||||
}
|
||||
|
||||
return function( context, node, pos, nodeWidth, nodeHeight ){
|
||||
let r = this;
|
||||
|
||||
if( !node.visible() ){ return; }
|
||||
|
||||
let padding = node.pstyle( `${overlayOrUnderlay}-padding` ).pfValue;
|
||||
let opacity = node.pstyle( `${overlayOrUnderlay}-opacity` ).value;
|
||||
let color = node.pstyle( `${overlayOrUnderlay}-color` ).value;
|
||||
let shape = node.pstyle( `${overlayOrUnderlay}-shape` ).value;
|
||||
let radius = node.pstyle( `${overlayOrUnderlay}-corner-radius` ).value;
|
||||
|
||||
if( opacity > 0 ){
|
||||
pos = pos || node.position();
|
||||
|
||||
if( nodeWidth == null || nodeHeight == null ){
|
||||
let padding = node.padding();
|
||||
|
||||
nodeWidth = node.width() + 2 * padding;
|
||||
nodeHeight = node.height() + 2 * padding;
|
||||
}
|
||||
|
||||
r.colorFillStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
r.nodeShapes[shape].draw(
|
||||
context,
|
||||
pos.x,
|
||||
pos.y,
|
||||
nodeWidth + padding * 2,
|
||||
nodeHeight + padding * 2,
|
||||
radius
|
||||
);
|
||||
|
||||
context.fill();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
CRp.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
|
||||
|
||||
CRp.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay');
|
||||
|
||||
// does the node have at least one pie piece?
|
||||
CRp.hasPie = function( node ){
|
||||
node = node[0]; // ensure ele ref
|
||||
|
||||
return node._private.hasPie;
|
||||
};
|
||||
|
||||
CRp.hasStripe = function( node ){
|
||||
node = node[0]; // ensure ele ref
|
||||
|
||||
return node._private.hasStripe;
|
||||
};
|
||||
|
||||
CRp.drawPie = function( context, node, nodeOpacity, pos ){
|
||||
node = node[0]; // ensure ele ref
|
||||
pos = pos || node.position();
|
||||
|
||||
let cyStyle = node.cy().style();
|
||||
let pieSize = node.pstyle( 'pie-size' );
|
||||
let hole = node.pstyle('pie-hole');
|
||||
let overallStartAngle = node.pstyle('pie-start-angle').pfValue;
|
||||
let x = pos.x;
|
||||
let y = pos.y;
|
||||
let nodeW = node.width();
|
||||
let nodeH = node.height();
|
||||
let radius = Math.min( nodeW, nodeH ) / 2; // must fit in node
|
||||
let holeRadius;
|
||||
let lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
|
||||
let usePaths = this.usePaths();
|
||||
|
||||
if( usePaths ){
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
if( pieSize.units === '%' ){
|
||||
radius = radius * pieSize.pfValue;
|
||||
} else if( pieSize.pfValue !== undefined ){
|
||||
radius = pieSize.pfValue / 2; // diameter in pixels => radius
|
||||
}
|
||||
|
||||
if (hole.units === '%') {
|
||||
holeRadius = radius * hole.pfValue;
|
||||
} else if (hole.pfValue !== undefined) {
|
||||
holeRadius = hole.pfValue / 2; // diameter in pixels => radius
|
||||
}
|
||||
|
||||
if (holeRadius >= radius) {
|
||||
return; // the pie would be invisible anyway
|
||||
}
|
||||
|
||||
for( let i = 1; i <= cyStyle.pieBackgroundN; i++ ){ // 1..N
|
||||
let size = node.pstyle( 'pie-' + i + '-background-size' ).value;
|
||||
let color = node.pstyle( 'pie-' + i + '-background-color' ).value;
|
||||
let opacity = node.pstyle( 'pie-' + i + '-background-opacity' ).value * nodeOpacity;
|
||||
let percent = size / 100; // map integer range [0, 100] to [0, 1]
|
||||
|
||||
// percent can't push beyond 1
|
||||
if( percent + lastPercent > 1 ){
|
||||
percent = 1 - lastPercent;
|
||||
}
|
||||
|
||||
let angleStart = (1.5 * Math.PI) + (2 * Math.PI * lastPercent); // start at 12 o'clock and go clockwise
|
||||
angleStart += overallStartAngle; // shift by the overall pie start angle
|
||||
let angleDelta = 2 * Math.PI * percent;
|
||||
let 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;
|
||||
}
|
||||
|
||||
if (holeRadius === 0) { // make a pie slice
|
||||
context.beginPath();
|
||||
context.moveTo( x, y );
|
||||
context.arc( x, y, radius, angleStart, angleEnd );
|
||||
context.closePath();
|
||||
} else { // make a pie slice that's like the above but with a hole in the middle
|
||||
context.beginPath();
|
||||
context.arc(x, y, radius, angleStart, angleEnd);
|
||||
context.arc(x, y, holeRadius, angleEnd, angleStart, true); // true for anticlockwise
|
||||
context.closePath();
|
||||
}
|
||||
|
||||
this.colorFillStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
context.fill();
|
||||
|
||||
lastPercent += percent;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CRp.drawStripe = function( context, node, nodeOpacity, pos ){
|
||||
node = node[0]; // ensure ele ref
|
||||
pos = pos || node.position();
|
||||
|
||||
let cyStyle = node.cy().style();
|
||||
let x = pos.x;
|
||||
let y = pos.y;
|
||||
let nodeW = node.width();
|
||||
let nodeH = node.height();
|
||||
let lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
|
||||
let usePaths = this.usePaths();
|
||||
|
||||
context.save();
|
||||
|
||||
let direction = node.pstyle('stripe-direction').value;
|
||||
let stripeSize = node.pstyle('stripe-size');
|
||||
|
||||
switch (direction) {
|
||||
case 'vertical':
|
||||
break; // default
|
||||
case 'righward':
|
||||
context.rotate(-Math.PI / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
let stripeW = nodeW;
|
||||
let stripeH = nodeH;
|
||||
|
||||
if( stripeSize.units === '%' ){
|
||||
stripeW = stripeW * stripeSize.pfValue;
|
||||
stripeH = stripeH * stripeSize.pfValue;
|
||||
} else if( stripeSize.pfValue !== undefined ){
|
||||
stripeW = stripeSize.pfValue;
|
||||
stripeH = stripeSize.pfValue;
|
||||
}
|
||||
|
||||
if( usePaths ){
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
// shift up from the centre of the node to the top-left corner
|
||||
y -= stripeW / 2;
|
||||
x -= stripeH / 2;
|
||||
|
||||
for( let i = 1; i <= cyStyle.stripeBackgroundN; i++ ){ // 1..N
|
||||
let size = node.pstyle( 'stripe-' + i + '-background-size' ).value;
|
||||
let color = node.pstyle( 'stripe-' + i + '-background-color' ).value;
|
||||
let opacity = node.pstyle( 'stripe-' + i + '-background-opacity' ).value * nodeOpacity;
|
||||
let percent = size / 100; // map integer range [0, 100] to [0, 1]
|
||||
|
||||
// percent can't push beyond 1
|
||||
if( percent + lastPercent > 1 ){
|
||||
percent = 1 - lastPercent;
|
||||
}
|
||||
|
||||
// ignore if
|
||||
// - zero size
|
||||
// - we're already beyond the full chart
|
||||
// - adding the current slice would go beyond the full chart
|
||||
if( size === 0 || lastPercent >= 1 || lastPercent + percent > 1 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
// draw rect for the current stripe
|
||||
context.beginPath();
|
||||
context.rect( x, y + stripeH * lastPercent, stripeW, stripeH * percent );
|
||||
context.closePath();
|
||||
|
||||
this.colorFillStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
context.fill();
|
||||
|
||||
lastPercent += percent;
|
||||
}
|
||||
|
||||
context.restore();
|
||||
|
||||
};
|
||||
|
||||
|
||||
export default CRp;
|
||||
555
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/ele-texture-cache.mjs
generated
vendored
Normal file
555
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/ele-texture-cache.mjs
generated
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
import * as math from '../../../math.mjs';
|
||||
import { trueify, falsify, removeFromArray, clearArray, MAX_INT, assign, defaults } from '../../../util/index.mjs';
|
||||
import Heap from '../../../heap.mjs';
|
||||
import defs from './texture-cache-defs.mjs';
|
||||
import ElementTextureCacheLookup from './ele-texture-cache-lookup.mjs';
|
||||
|
||||
const minTxrH = 25; // the size of the texture cache for small height eles (special case)
|
||||
const txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
|
||||
const minLvl = -4; // when scaling smaller than that we don't need to re-render
|
||||
export const maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
|
||||
export const maxZoom = 7.99; // beyond this zoom level, layered textures are not used
|
||||
const eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
|
||||
const defTxrWidth = 1024; // default/minimum texture width
|
||||
const maxTxrW = 1024; // the maximum width of a texture
|
||||
const maxTxrH = 1024; // the maximum height of a texture
|
||||
const minUtility = 0.2; // if usage of texture is less than this, it is retired
|
||||
const maxFullness = 0.8; // fullness of texture after which queue removal is checked
|
||||
const maxFullnessChecks = 10; // dequeued after this many checks
|
||||
const deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
|
||||
const deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
|
||||
const deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
|
||||
const deqFastCost = 0.9; // % of frame time to be used when >60fps
|
||||
const deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
|
||||
const maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
|
||||
|
||||
const getTxrReasons = {
|
||||
dequeue: 'dequeue',
|
||||
downscale: 'downscale',
|
||||
highQuality: 'highQuality'
|
||||
};
|
||||
|
||||
const initDefaults = defaults({
|
||||
getKey: null,
|
||||
doesEleInvalidateKey: falsify,
|
||||
drawElement: null,
|
||||
getBoundingBox: null,
|
||||
getRotationPoint: null,
|
||||
getRotationOffset: null,
|
||||
isVisible: trueify,
|
||||
allowEdgeTxrCaching: true,
|
||||
allowParentTxrCaching: true
|
||||
});
|
||||
|
||||
const ElementTextureCache = function( renderer, initOptions ){
|
||||
let self = this;
|
||||
|
||||
self.renderer = renderer;
|
||||
self.onDequeues = [];
|
||||
|
||||
let opts = initDefaults(initOptions);
|
||||
|
||||
assign(self, opts);
|
||||
|
||||
self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
|
||||
|
||||
self.setupDequeueing();
|
||||
};
|
||||
|
||||
const ETCp = ElementTextureCache.prototype;
|
||||
|
||||
ETCp.reasons = getTxrReasons;
|
||||
|
||||
// the list of textures in which new subtextures for elements can be placed
|
||||
ETCp.getTextureQueue = function( txrH ){
|
||||
let self = this;
|
||||
self.eleImgCaches = self.eleImgCaches || {};
|
||||
|
||||
return ( self.eleImgCaches[ txrH ] = self.eleImgCaches[ txrH ] || [] );
|
||||
};
|
||||
|
||||
// the list of usused textures which can be recycled (in use in texture queue)
|
||||
ETCp.getRetiredTextureQueue = function( txrH ){
|
||||
let self = this;
|
||||
|
||||
let rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
|
||||
let rtxtrQ = rtxtrQs[ txrH ] = rtxtrQs[ txrH ] || [];
|
||||
|
||||
return rtxtrQ;
|
||||
};
|
||||
|
||||
// queue of element draw requests at different scale levels
|
||||
ETCp.getElementQueue = function(){
|
||||
let self = this;
|
||||
|
||||
let q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function( a, b ){
|
||||
return b.reqs - a.reqs;
|
||||
});
|
||||
|
||||
return q;
|
||||
};
|
||||
|
||||
// queue of element draw requests at different scale levels (element id lookup)
|
||||
ETCp.getElementKeyToQueue = function(){
|
||||
let self = this;
|
||||
|
||||
let k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
|
||||
|
||||
return k2q;
|
||||
};
|
||||
|
||||
ETCp.getElement = function( ele, bb, pxRatio, lvl, reason ){
|
||||
let self = this;
|
||||
let r = this.renderer;
|
||||
let zoom = r.cy.zoom();
|
||||
let lookup = this.lookup;
|
||||
|
||||
if( !bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed() ){ return null; }
|
||||
|
||||
if(
|
||||
( !self.allowEdgeTxrCaching && ele.isEdge() )
|
||||
|| ( !self.allowParentTxrCaching && ele.isParent() )
|
||||
){
|
||||
return null;
|
||||
}
|
||||
|
||||
if( lvl == null ){
|
||||
lvl = Math.ceil( math.log2( zoom * pxRatio ) );
|
||||
}
|
||||
|
||||
if( lvl < minLvl ){
|
||||
lvl = minLvl;
|
||||
} else if( zoom >= maxZoom || lvl > maxLvl ){
|
||||
return null;
|
||||
}
|
||||
|
||||
let scale = Math.pow( 2, lvl );
|
||||
let eleScaledH = bb.h * scale;
|
||||
let eleScaledW = bb.w * scale;
|
||||
let scaledLabelShown = r.eleTextBiggerThanMin( ele, scale );
|
||||
|
||||
if( !this.isVisible(ele, scaledLabelShown) ){ return null; }
|
||||
|
||||
let eleCache = lookup.get( ele, lvl );
|
||||
|
||||
// if this get was on an unused/invalidated cache, then restore the texture usage metric
|
||||
if( eleCache && eleCache.invalidated ){
|
||||
eleCache.invalidated = false;
|
||||
eleCache.texture.invalidatedWidth -= eleCache.width;
|
||||
}
|
||||
|
||||
if( eleCache ){
|
||||
return eleCache;
|
||||
}
|
||||
|
||||
let txrH; // which texture height this ele belongs to
|
||||
|
||||
if( eleScaledH <= minTxrH ){
|
||||
txrH = minTxrH;
|
||||
} else if( eleScaledH <= txrStepH ){
|
||||
txrH = txrStepH;
|
||||
} else {
|
||||
txrH = Math.ceil( eleScaledH / txrStepH ) * txrStepH;
|
||||
}
|
||||
|
||||
if( eleScaledH > maxTxrH || eleScaledW > maxTxrW ){
|
||||
return null; // caching large elements is not efficient
|
||||
}
|
||||
|
||||
let txrQ = self.getTextureQueue( txrH );
|
||||
|
||||
// first try the second last one in case it has space at the end
|
||||
let txr = txrQ[ txrQ.length - 2 ];
|
||||
|
||||
let addNewTxr = function(){
|
||||
return self.recycleTexture( txrH, eleScaledW ) || self.addTexture( txrH, eleScaledW );
|
||||
};
|
||||
|
||||
// try the last one if there is no second last one
|
||||
if( !txr ){
|
||||
txr = txrQ[ txrQ.length - 1 ];
|
||||
}
|
||||
|
||||
// if the last one doesn't exist, we need a first one
|
||||
if( !txr ){
|
||||
txr = addNewTxr();
|
||||
}
|
||||
|
||||
// if there's no room in the current texture, we need a new one
|
||||
if( txr.width - txr.usedWidth < eleScaledW ){
|
||||
txr = addNewTxr();
|
||||
}
|
||||
|
||||
let scalableFrom = function( otherCache ){
|
||||
return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
|
||||
};
|
||||
|
||||
let deqing = reason && reason === getTxrReasons.dequeue;
|
||||
let highQualityReq = reason && reason === getTxrReasons.highQuality;
|
||||
let downscaleReq = reason && reason === getTxrReasons.downscale;
|
||||
|
||||
let higherCache; // the nearest cache with a higher level
|
||||
for( let l = lvl + 1; l <= maxLvl; l++ ){
|
||||
let c = lookup.get( ele, l );
|
||||
|
||||
if( c ){ higherCache = c; break; }
|
||||
}
|
||||
|
||||
let oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
|
||||
|
||||
let downscale = function(){
|
||||
txr.context.drawImage(
|
||||
oneUpCache.texture.canvas,
|
||||
oneUpCache.x, 0,
|
||||
oneUpCache.width, oneUpCache.height,
|
||||
txr.usedWidth, 0,
|
||||
eleScaledW, eleScaledH
|
||||
);
|
||||
};
|
||||
|
||||
// reset ele area in texture
|
||||
txr.context.setTransform( 1, 0, 0, 1, 0, 0 );
|
||||
txr.context.clearRect( txr.usedWidth, 0, eleScaledW, txrH );
|
||||
|
||||
if( scalableFrom(oneUpCache) ){
|
||||
// then we can relatively cheaply rescale the existing image w/o rerendering
|
||||
downscale();
|
||||
|
||||
} else if( scalableFrom(higherCache) ){
|
||||
// then use the higher cache for now and queue the next level down
|
||||
// to cheaply scale towards the smaller level
|
||||
|
||||
if( highQualityReq ){
|
||||
for( let l = higherCache.level; l > lvl; l-- ){
|
||||
oneUpCache = self.getElement( ele, bb, pxRatio, l, getTxrReasons.downscale );
|
||||
}
|
||||
|
||||
downscale();
|
||||
|
||||
} else {
|
||||
self.queueElement( ele, higherCache.level - 1 );
|
||||
|
||||
return higherCache;
|
||||
}
|
||||
} else {
|
||||
|
||||
let lowerCache; // the nearest cache with a lower level
|
||||
if( !deqing && !highQualityReq && !downscaleReq ){
|
||||
for( let l = lvl - 1; l >= minLvl; l-- ){
|
||||
let c = lookup.get( ele, l );
|
||||
|
||||
if( c ){ lowerCache = c; break; }
|
||||
}
|
||||
}
|
||||
|
||||
if( scalableFrom(lowerCache) ){
|
||||
// then use the lower quality cache for now and queue the better one for later
|
||||
|
||||
self.queueElement( ele, lvl );
|
||||
|
||||
return lowerCache;
|
||||
}
|
||||
|
||||
txr.context.translate( txr.usedWidth, 0 );
|
||||
txr.context.scale( scale, scale );
|
||||
|
||||
this.drawElement( txr.context, ele, bb, scaledLabelShown, false );
|
||||
|
||||
txr.context.scale( 1/scale, 1/scale );
|
||||
txr.context.translate( -txr.usedWidth, 0 );
|
||||
}
|
||||
|
||||
eleCache = {
|
||||
x: txr.usedWidth,
|
||||
texture: txr,
|
||||
level: lvl,
|
||||
scale: scale,
|
||||
width: eleScaledW,
|
||||
height: eleScaledH,
|
||||
scaledLabelShown: scaledLabelShown
|
||||
};
|
||||
|
||||
txr.usedWidth += Math.ceil( eleScaledW + eleTxrSpacing );
|
||||
|
||||
txr.eleCaches.push( eleCache );
|
||||
|
||||
lookup.set( ele, lvl, eleCache );
|
||||
|
||||
self.checkTextureFullness( txr );
|
||||
|
||||
return eleCache;
|
||||
};
|
||||
|
||||
ETCp.invalidateElements = function( eles ){
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
this.invalidateElement(eles[i]);
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.invalidateElement = function( ele ){
|
||||
let self = this;
|
||||
let lookup = self.lookup;
|
||||
let caches = [];
|
||||
let invalid = lookup.isInvalid(ele);
|
||||
|
||||
if( !invalid ){
|
||||
return; // override the invalidation request if the element key has not changed
|
||||
}
|
||||
|
||||
for( let lvl = minLvl; lvl <= maxLvl; lvl++ ){
|
||||
let cache = lookup.getForCachedKey( ele, lvl );
|
||||
|
||||
if( cache ){
|
||||
caches.push( cache );
|
||||
}
|
||||
}
|
||||
|
||||
let noOtherElesUseCache = lookup.invalidate(ele);
|
||||
|
||||
if( noOtherElesUseCache ){
|
||||
for( let i = 0; i < caches.length; i++ ){
|
||||
let cache = caches[i];
|
||||
let txr = cache.texture;
|
||||
|
||||
// remove space from the texture it belongs to
|
||||
txr.invalidatedWidth += cache.width;
|
||||
|
||||
// mark the cache as invalidated
|
||||
cache.invalidated = true;
|
||||
|
||||
// retire the texture if its utility is low
|
||||
self.checkTextureUtility( txr );
|
||||
}
|
||||
}
|
||||
|
||||
// remove from queue since the old req was for the old state
|
||||
self.removeFromQueue( ele );
|
||||
};
|
||||
|
||||
ETCp.checkTextureUtility = function( txr ){
|
||||
// invalidate all entries in the cache if the cache size is small
|
||||
if( txr.invalidatedWidth >= minUtility * txr.width ){
|
||||
this.retireTexture( txr );
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.checkTextureFullness = function( txr ){
|
||||
// if texture has been mostly filled and passed over several times, remove
|
||||
// it from the queue so we don't need to waste time looking at it to put new things
|
||||
|
||||
let self = this;
|
||||
let txrQ = self.getTextureQueue( txr.height );
|
||||
|
||||
if( txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks ){
|
||||
removeFromArray( txrQ, txr );
|
||||
} else {
|
||||
txr.fullnessChecks++;
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.retireTexture = function( txr ){
|
||||
let self = this;
|
||||
let txrH = txr.height;
|
||||
let txrQ = self.getTextureQueue( txrH );
|
||||
let lookup = this.lookup;
|
||||
|
||||
// retire the texture from the active / searchable queue:
|
||||
|
||||
removeFromArray( txrQ, txr );
|
||||
|
||||
txr.retired = true;
|
||||
|
||||
// remove the refs from the eles to the caches:
|
||||
|
||||
let eleCaches = txr.eleCaches;
|
||||
|
||||
for( let i = 0; i < eleCaches.length; i++ ){
|
||||
let eleCache = eleCaches[i];
|
||||
|
||||
lookup.deleteCache( eleCache.key, eleCache.level );
|
||||
}
|
||||
|
||||
clearArray( eleCaches );
|
||||
|
||||
// add the texture to a retired queue so it can be recycled in future:
|
||||
|
||||
let rtxtrQ = self.getRetiredTextureQueue( txrH );
|
||||
|
||||
rtxtrQ.push( txr );
|
||||
};
|
||||
|
||||
ETCp.addTexture = function( txrH, minW ){
|
||||
let self = this;
|
||||
let txrQ = self.getTextureQueue( txrH );
|
||||
let txr = {};
|
||||
|
||||
txrQ.push( txr );
|
||||
|
||||
txr.eleCaches = [];
|
||||
|
||||
txr.height = txrH;
|
||||
txr.width = Math.max( defTxrWidth, minW );
|
||||
txr.usedWidth = 0;
|
||||
txr.invalidatedWidth = 0;
|
||||
txr.fullnessChecks = 0;
|
||||
|
||||
txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
|
||||
|
||||
txr.context = txr.canvas.getContext('2d');
|
||||
|
||||
return txr;
|
||||
};
|
||||
|
||||
ETCp.recycleTexture = function( txrH, minW ){
|
||||
let self = this;
|
||||
let txrQ = self.getTextureQueue( txrH );
|
||||
let rtxtrQ = self.getRetiredTextureQueue( txrH );
|
||||
|
||||
for( let i = 0; i < rtxtrQ.length; i++ ){
|
||||
let txr = rtxtrQ[i];
|
||||
|
||||
if( txr.width >= minW ){
|
||||
txr.retired = false;
|
||||
|
||||
txr.usedWidth = 0;
|
||||
txr.invalidatedWidth = 0;
|
||||
txr.fullnessChecks = 0;
|
||||
|
||||
clearArray( txr.eleCaches );
|
||||
|
||||
txr.context.setTransform( 1, 0, 0, 1, 0, 0 );
|
||||
txr.context.clearRect( 0, 0, txr.width, txr.height );
|
||||
|
||||
removeFromArray( rtxtrQ, txr );
|
||||
txrQ.push( txr );
|
||||
|
||||
return txr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.queueElement = function( ele, lvl ){
|
||||
let self = this;
|
||||
let q = self.getElementQueue();
|
||||
let k2q = self.getElementKeyToQueue();
|
||||
let key = this.getKey(ele);
|
||||
let existingReq = k2q[key];
|
||||
|
||||
if( existingReq ){
|
||||
// use the max lvl b/c in between lvls are cheap to make
|
||||
existingReq.level = Math.max( existingReq.level, lvl );
|
||||
|
||||
existingReq.eles.merge(ele);
|
||||
|
||||
existingReq.reqs++;
|
||||
|
||||
q.updateItem( existingReq );
|
||||
} else {
|
||||
let req = {
|
||||
eles: ele.spawn().merge(ele),
|
||||
level: lvl,
|
||||
reqs: 1,
|
||||
key
|
||||
};
|
||||
|
||||
q.push( req );
|
||||
|
||||
k2q[key] = req;
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.dequeue = function( pxRatio /*, extent*/ ){
|
||||
let self = this;
|
||||
let q = self.getElementQueue();
|
||||
let k2q = self.getElementKeyToQueue();
|
||||
let dequeued = [];
|
||||
let lookup = self.lookup;
|
||||
|
||||
for( let i = 0; i < maxDeqSize; i++ ){
|
||||
if( q.size() > 0 ){
|
||||
let req = q.pop();
|
||||
let key = req.key;
|
||||
let ele = req.eles[0]; // all eles have the same key
|
||||
let cacheExists = lookup.hasCache(ele, req.level);
|
||||
|
||||
// clear out the key to req lookup
|
||||
k2q[key] = null;
|
||||
|
||||
// dequeueing isn't necessary with an existing cache
|
||||
if( cacheExists ){ continue; }
|
||||
|
||||
dequeued.push( req );
|
||||
|
||||
let bb = self.getBoundingBox( ele );
|
||||
|
||||
self.getElement( ele, bb, pxRatio, req.level, getTxrReasons.dequeue );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dequeued;
|
||||
};
|
||||
|
||||
ETCp.removeFromQueue = function( ele ){
|
||||
let self = this;
|
||||
let q = self.getElementQueue();
|
||||
let k2q = self.getElementKeyToQueue();
|
||||
let key = this.getKey(ele);
|
||||
let req = k2q[key];
|
||||
|
||||
if( req != null ){
|
||||
if( req.eles.length === 1 ){ // remove if last ele in the req
|
||||
// bring to front of queue
|
||||
req.reqs = MAX_INT;
|
||||
q.updateItem(req);
|
||||
|
||||
q.pop(); // remove from queue
|
||||
|
||||
k2q[key] = null; // remove from lookup map
|
||||
} else { // otherwise just remove ele from req
|
||||
req.eles.unmerge(ele);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.onDequeue = function( fn ){ this.onDequeues.push( fn ); };
|
||||
ETCp.offDequeue = function( fn ){ removeFromArray( this.onDequeues, fn ); };
|
||||
|
||||
ETCp.setupDequeueing = defs.setupDequeueing({
|
||||
deqRedrawThreshold: deqRedrawThreshold,
|
||||
deqCost: deqCost,
|
||||
deqAvgCost: deqAvgCost,
|
||||
deqNoDrawCost: deqNoDrawCost,
|
||||
deqFastCost: deqFastCost,
|
||||
deq: function( self, pxRatio, extent ){
|
||||
return self.dequeue( pxRatio, extent );
|
||||
},
|
||||
onDeqd: function( self, deqd ){
|
||||
for( let i = 0; i < self.onDequeues.length; i++ ){
|
||||
let fn = self.onDequeues[i];
|
||||
|
||||
fn( deqd );
|
||||
}
|
||||
},
|
||||
shouldRedraw: function( self, deqd, pxRatio, extent ){
|
||||
for( let i = 0; i < deqd.length; i++ ){
|
||||
let eles = deqd[i].eles;
|
||||
|
||||
for( let j = 0; j < eles.length; j++ ){
|
||||
let bb = eles[j].boundingBox();
|
||||
|
||||
if( math.boundingBoxesIntersect( bb, extent ) ){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
priority: function( self ){
|
||||
return self.renderer.beforeRenderPriorities.eleTxrDeq;
|
||||
}
|
||||
});
|
||||
|
||||
export default ElementTextureCache;
|
||||
390
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/index.mjs
generated
vendored
Normal file
390
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
The canvas renderer was written by Yue Dong.
|
||||
|
||||
Modifications tracked on Github.
|
||||
*/
|
||||
|
||||
/* global OffscreenCanvas */
|
||||
|
||||
import * as util from '../../../util/index.mjs';
|
||||
import * as is from '../../../is.mjs';
|
||||
import { makeBoundingBox } from '../../../math.mjs';
|
||||
import ElementTextureCache from './ele-texture-cache.mjs';
|
||||
import LayeredTextureCache from './layered-texture-cache.mjs';
|
||||
|
||||
import arrowShapes from './arrow-shapes.mjs';
|
||||
import drawingElements from './drawing-elements.mjs';
|
||||
import drawingEdges from './drawing-edges.mjs';
|
||||
import drawingImages from './drawing-images.mjs';
|
||||
import drawingLabelText from './drawing-label-text.mjs';
|
||||
import drawingNodes from './drawing-nodes.mjs';
|
||||
import drawingRedraw from './drawing-redraw.mjs';
|
||||
import drawingRedrawWebGL from './webgl/drawing-redraw-webgl.mjs';
|
||||
import drawingShapes from './drawing-shapes.mjs';
|
||||
import exportImage from './export-image.mjs';
|
||||
import nodeShapes from './node-shapes.mjs';
|
||||
|
||||
var CR = CanvasRenderer;
|
||||
var CRp = CanvasRenderer.prototype;
|
||||
|
||||
CRp.CANVAS_LAYERS = 3;
|
||||
//
|
||||
CRp.SELECT_BOX = 0;
|
||||
CRp.DRAG = 1;
|
||||
CRp.NODE = 2;
|
||||
CRp.WEBGL = 3;
|
||||
|
||||
CRp.CANVAS_TYPES = [ '2d', '2d', '2d', 'webgl2' ];
|
||||
|
||||
CRp.BUFFER_COUNT = 3;
|
||||
//
|
||||
CRp.TEXTURE_BUFFER = 0;
|
||||
CRp.MOTIONBLUR_BUFFER_NODE = 1;
|
||||
CRp.MOTIONBLUR_BUFFER_DRAG = 2;
|
||||
|
||||
function CanvasRenderer( options ){
|
||||
var r = this;
|
||||
|
||||
var containerWindow = r.cy.window();
|
||||
var document = containerWindow.document;
|
||||
|
||||
if( options.webgl ){
|
||||
CRp.CANVAS_LAYERS = r.CANVAS_LAYERS = 4;
|
||||
console.log('webgl rendering enabled');
|
||||
}
|
||||
|
||||
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 ),
|
||||
};
|
||||
|
||||
var tapHlOffAttr = '-webkit-tap-highlight-color';
|
||||
var tapHlOffStyle = 'rgba(0,0,0,0)';
|
||||
r.data.canvasContainer = document.createElement( 'div' ); // eslint-disable-line no-undef
|
||||
var containerStyle = r.data.canvasContainer.style;
|
||||
r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
|
||||
containerStyle.position = 'relative';
|
||||
containerStyle.zIndex = '0';
|
||||
containerStyle.overflow = 'hidden';
|
||||
|
||||
var container = options.cy.container();
|
||||
container.appendChild( r.data.canvasContainer );
|
||||
container.style[tapHlOffAttr] = tapHlOffStyle;
|
||||
|
||||
var styleMap = {
|
||||
'-webkit-user-select': 'none',
|
||||
'-moz-user-select': '-moz-none',
|
||||
'user-select': 'none',
|
||||
'-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
|
||||
'outline-style': 'none',
|
||||
};
|
||||
|
||||
if(is.ms()) {
|
||||
styleMap['-ms-touch-action'] = 'none';
|
||||
styleMap['touch-action'] = 'none';
|
||||
}
|
||||
|
||||
for( var i = 0; i < CRp.CANVAS_LAYERS; i++ ){
|
||||
var canvas = r.data.canvases[ i ] = document.createElement( 'canvas' ); // eslint-disable-line no-undef
|
||||
var type = CRp.CANVAS_TYPES[ i ];
|
||||
r.data.contexts[ i ] = canvas.getContext( type );
|
||||
if( !r.data.contexts[ i ] ) {
|
||||
util.error( 'Could not create canvas of type ' + type );
|
||||
}
|
||||
Object.keys(styleMap).forEach((k) => {
|
||||
canvas.style[k] = styleMap[k];
|
||||
});
|
||||
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' );
|
||||
if( r.data.canvases[ CRp.WEBGL ] ) {
|
||||
r.data.canvases[ CRp.WEBGL ].setAttribute( 'data-id', 'layer' + CRp.WEBGL + '-webgl' );
|
||||
}
|
||||
|
||||
for( var i = 0; i < CRp.BUFFER_COUNT; i++ ){
|
||||
r.data.bufferCanvases[ i ] = document.createElement( 'canvas' ); // eslint-disable-line no-undef
|
||||
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;
|
||||
|
||||
let emptyBb = makeBoundingBox();
|
||||
|
||||
let getBoxCenter = bb => ({ x: (bb.x1 + bb.x2)/2, y: (bb.y1 + bb.y2)/2 });
|
||||
|
||||
let getCenterOffset = bb => ({ x: -bb.w/2, y: -bb.h/2 });
|
||||
|
||||
let backgroundTimestampHasChanged = ele => {
|
||||
let _p = ele[0]._private;
|
||||
let same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
|
||||
|
||||
return !same;
|
||||
};
|
||||
|
||||
let getStyleKey = ele => ele[0]._private.nodeKey;
|
||||
let getLabelKey = ele => ele[0]._private.labelStyleKey;
|
||||
let getSourceLabelKey = ele => ele[0]._private.sourceLabelStyleKey;
|
||||
let getTargetLabelKey = ele => ele[0]._private.targetLabelStyleKey;
|
||||
|
||||
let drawElement = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElement( context, ele, bb, false, false, useEleOpacity );
|
||||
let drawLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'main', useEleOpacity );
|
||||
let drawSourceLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'source', useEleOpacity );
|
||||
let drawTargetLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'target', useEleOpacity );
|
||||
|
||||
let getElementBox = ele => { ele.boundingBox(); return ele[0]._private.bodyBounds; };
|
||||
let getLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.main || emptyBb; };
|
||||
let getSourceLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.source || emptyBb; };
|
||||
let getTargetLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.target || emptyBb; };
|
||||
|
||||
let isLabelVisibleAtScale = (ele, scaledLabelShown) => scaledLabelShown;
|
||||
|
||||
let getElementRotationPoint = ele => getBoxCenter( getElementBox(ele) );
|
||||
|
||||
let addTextMargin = (prefix, pt, ele) => {
|
||||
let pre = prefix ? prefix + '-' : '';
|
||||
|
||||
return {
|
||||
x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
|
||||
y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
|
||||
};
|
||||
};
|
||||
|
||||
let getRsPt = (ele, x, y) => {
|
||||
let rs = ele[0]._private.rscratch;
|
||||
|
||||
return { x: rs[x], y: rs[y] };
|
||||
};
|
||||
|
||||
let getLabelRotationPoint = ele => addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
|
||||
let getSourceLabelRotationPoint = ele => addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
|
||||
let getTargetLabelRotationPoint = ele => addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
|
||||
|
||||
let getElementRotationOffset = ele => getCenterOffset( getElementBox(ele) );
|
||||
let getSourceLabelRotationOffset = ele => getCenterOffset( getSourceLabelBox(ele) );
|
||||
let getTargetLabelRotationOffset = ele => getCenterOffset( getTargetLabelBox(ele) );
|
||||
|
||||
let getLabelRotationOffset = ele => {
|
||||
let bb = getLabelBox(ele);
|
||||
let p = getCenterOffset( getLabelBox(ele) );
|
||||
|
||||
if( ele.isNode() ){
|
||||
switch( ele.pstyle('text-halign').value ){
|
||||
case 'left':
|
||||
p.x = -bb.w - (bb.leftPad || 0);
|
||||
break;
|
||||
case 'right':
|
||||
p.x = -(bb.rightPad || 0);
|
||||
break;
|
||||
}
|
||||
|
||||
switch( ele.pstyle('text-valign').value ){
|
||||
case 'top':
|
||||
p.y = -bb.h - (bb.topPad || 0);
|
||||
break;
|
||||
case 'bottom':
|
||||
p.y = -(bb.botPad || 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
let eleTxrCache = r.data.eleTxrCache = new ElementTextureCache( r, {
|
||||
getKey: getStyleKey,
|
||||
doesEleInvalidateKey: backgroundTimestampHasChanged,
|
||||
drawElement: drawElement,
|
||||
getBoundingBox: getElementBox,
|
||||
getRotationPoint: getElementRotationPoint,
|
||||
getRotationOffset: getElementRotationOffset,
|
||||
allowEdgeTxrCaching: false,
|
||||
allowParentTxrCaching: false
|
||||
} );
|
||||
|
||||
let lblTxrCache = r.data.lblTxrCache = new ElementTextureCache( r, {
|
||||
getKey: getLabelKey,
|
||||
drawElement: drawLabel,
|
||||
getBoundingBox: getLabelBox,
|
||||
getRotationPoint: getLabelRotationPoint,
|
||||
getRotationOffset: getLabelRotationOffset,
|
||||
isVisible: isLabelVisibleAtScale
|
||||
} );
|
||||
|
||||
let slbTxrCache = r.data.slbTxrCache = new ElementTextureCache( r, {
|
||||
getKey: getSourceLabelKey,
|
||||
drawElement: drawSourceLabel,
|
||||
getBoundingBox: getSourceLabelBox,
|
||||
getRotationPoint: getSourceLabelRotationPoint,
|
||||
getRotationOffset: getSourceLabelRotationOffset,
|
||||
isVisible: isLabelVisibleAtScale
|
||||
} );
|
||||
|
||||
let tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache( r, {
|
||||
getKey: getTargetLabelKey,
|
||||
drawElement: drawTargetLabel,
|
||||
getBoundingBox: getTargetLabelBox,
|
||||
getRotationPoint: getTargetLabelRotationPoint,
|
||||
getRotationOffset: getTargetLabelRotationOffset,
|
||||
isVisible: isLabelVisibleAtScale
|
||||
} );
|
||||
|
||||
let lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache( r );
|
||||
|
||||
r.onUpdateEleCalcs(function invalidateTextureCaches( willDraw, eles ){
|
||||
// each cache should check for sub-key diff to see that the update affects that cache particularly
|
||||
eleTxrCache.invalidateElements( eles );
|
||||
lblTxrCache.invalidateElements( eles );
|
||||
slbTxrCache.invalidateElements( eles );
|
||||
tlbTxrCache.invalidateElements( eles );
|
||||
|
||||
// any change invalidates the layers
|
||||
lyrTxrCache.invalidateElements( eles );
|
||||
|
||||
// update the old bg timestamp so diffs can be done in the ele txr caches
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let _p = eles[i]._private;
|
||||
|
||||
_p.oldBackgroundTimestamp = _p.backgroundTimestamp;
|
||||
}
|
||||
});
|
||||
|
||||
let refineInLayers = reqs => {
|
||||
for( var i = 0; i < reqs.length; i++ ){
|
||||
lyrTxrCache.enqueueElementRefinement( reqs[i].ele );
|
||||
}
|
||||
};
|
||||
|
||||
eleTxrCache.onDequeue(refineInLayers);
|
||||
lblTxrCache.onDequeue(refineInLayers);
|
||||
slbTxrCache.onDequeue(refineInLayers);
|
||||
tlbTxrCache.onDequeue(refineInLayers);
|
||||
|
||||
if( options.webgl ) {
|
||||
r.initWebgl( options, {
|
||||
getStyleKey,
|
||||
getLabelKey,
|
||||
getSourceLabelKey,
|
||||
getTargetLabelKey,
|
||||
drawElement,
|
||||
drawLabel,
|
||||
drawSourceLabel,
|
||||
drawTargetLabel,
|
||||
getElementBox,
|
||||
getLabelBox,
|
||||
getSourceLabelBox,
|
||||
getTargetLabelBox,
|
||||
getElementRotationPoint,
|
||||
getElementRotationOffset,
|
||||
getLabelRotationPoint,
|
||||
getSourceLabelRotationPoint,
|
||||
getTargetLabelRotationPoint,
|
||||
getLabelRotationOffset,
|
||||
getSourceLabelRotationOffset,
|
||||
getTargetLabelRotationOffset
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
case 'gc':
|
||||
r.data.gc = true;
|
||||
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;
|
||||
};
|
||||
|
||||
CRp.setImgSmoothing = function( context, bool ){
|
||||
if( context.imageSmoothingEnabled != null ){
|
||||
context.imageSmoothingEnabled = bool;
|
||||
} else {
|
||||
context.webkitImageSmoothingEnabled = bool;
|
||||
context.mozImageSmoothingEnabled = bool;
|
||||
context.msImageSmoothingEnabled = bool;
|
||||
}
|
||||
};
|
||||
|
||||
CRp.getImgSmoothing = function( context ){
|
||||
if( context.imageSmoothingEnabled != null ){
|
||||
return context.imageSmoothingEnabled;
|
||||
} else {
|
||||
return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
|
||||
}
|
||||
};
|
||||
|
||||
CRp.makeOffscreenCanvas = function(width, height){
|
||||
let canvas;
|
||||
|
||||
if( typeof OffscreenCanvas !== typeof undefined ){
|
||||
canvas = new OffscreenCanvas(width, height);
|
||||
} else {
|
||||
var containerWindow = this.cy.window();
|
||||
var document = containerWindow.document;
|
||||
canvas = document.createElement('canvas'); // eslint-disable-line no-undef
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
}
|
||||
|
||||
return canvas;
|
||||
};
|
||||
|
||||
[
|
||||
arrowShapes,
|
||||
drawingElements,
|
||||
drawingEdges,
|
||||
drawingImages,
|
||||
drawingLabelText,
|
||||
drawingNodes,
|
||||
drawingRedraw,
|
||||
drawingRedrawWebGL,
|
||||
drawingShapes,
|
||||
exportImage,
|
||||
nodeShapes
|
||||
].forEach( function( props ){
|
||||
util.extend( CRp, props );
|
||||
} );
|
||||
|
||||
export default CR;
|
||||
1150
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/webgl/drawing-elements-webgl.mjs
generated
vendored
Normal file
1150
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/webgl/drawing-elements-webgl.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
208
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/webgl/misc-upscaler.js
generated
vendored
Normal file
208
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/webgl/misc-upscaler.js
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
// Canvas Upscaling Plugin
|
||||
class MiscUpscaler {
|
||||
constructor(options = {}) {
|
||||
// Plugin Settings
|
||||
this.options = Object.assign({
|
||||
useEdgeDetection: true, // Edge Detection
|
||||
scaleFactor: 2.0, // Upscaling Value
|
||||
}, options);
|
||||
|
||||
this.gl = null;
|
||||
this.program = null;
|
||||
this.frameBuffer = null;
|
||||
this.texture = null;
|
||||
this.outputTexture = null;
|
||||
}
|
||||
|
||||
init(inputCanvas, outputCanvas) {
|
||||
this.inputCanvas = inputCanvas;
|
||||
this.outputCanvas = outputCanvas;
|
||||
|
||||
// Set output canvas size based on scale factor
|
||||
this.outputCanvas.width = this.inputCanvas.width * this.options.scaleFactor;
|
||||
this.outputCanvas.height = this.inputCanvas.height * this.options.scaleFactor;
|
||||
|
||||
// Initialize WebGL context
|
||||
this.gl = this.outputCanvas.getContext('webgl2');
|
||||
if (!this.gl) {
|
||||
console.error('WebGL not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize shaders and buffers
|
||||
this.initShaders();
|
||||
this.initBuffers();
|
||||
this.initTextures();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
initShaders() {
|
||||
// Vertex shader
|
||||
const vertexShaderSource = `
|
||||
attribute vec2 a_position;
|
||||
attribute vec2 a_texCoord;
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = a_texCoord;
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// Fragment shader
|
||||
const fragmentShaderSource = `
|
||||
precision mediump float;
|
||||
uniform sampler2D tDiffuse;
|
||||
uniform vec2 resolution;
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vec2 texelSize = 1.0 / resolution;
|
||||
|
||||
// Get Neighbor Pixels
|
||||
vec4 color = texture2D(tDiffuse, vUv);
|
||||
vec4 colorUp = texture2D(tDiffuse, vUv + vec2(0.0, texelSize.y));
|
||||
vec4 colorDown = texture2D(tDiffuse, vUv - vec2(0.0, texelSize.y));
|
||||
vec4 colorLeft = texture2D(tDiffuse, vUv - vec2(texelSize.x, 0.0));
|
||||
vec4 colorRight = texture2D(tDiffuse, vUv + vec2(texelSize.x, 0.0));
|
||||
|
||||
// Work with edges
|
||||
float edgeStrength = 1.0 - smoothstep(0.1, 0.3, length(color.rgb - colorUp.rgb));
|
||||
edgeStrength += 1.0 - smoothstep(0.1, 0.3, length(color.rgb - colorDown.rgb));
|
||||
edgeStrength += 1.0 - smoothstep(0.1, 0.3, length(color.rgb - colorLeft.rgb));
|
||||
edgeStrength += 1.0 - smoothstep(0.1, 0.3, length(color.rgb - colorRight.rgb));
|
||||
edgeStrength = clamp(edgeStrength, 0.0, 1.0);
|
||||
|
||||
// Applying edges incresing and filtering
|
||||
vec3 enhancedColor = mix(color.rgb, vec3(1.0) - (1.0 - color.rgb) * edgeStrength, 0.5);
|
||||
|
||||
gl_FragColor = vec4(enhancedColor, color.a);
|
||||
}
|
||||
`;
|
||||
|
||||
// Create shader program
|
||||
const vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexShaderSource);
|
||||
const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentShaderSource);
|
||||
this.program = this.createProgram(vertexShader, fragmentShader);
|
||||
|
||||
// Get attribute and uniform locations
|
||||
this.positionLocation = this.gl.getAttribLocation(this.program, 'a_position');
|
||||
this.texCoordLocation = this.gl.getAttribLocation(this.program, 'a_texCoord');
|
||||
this.resolutionLocation = this.gl.getUniformLocation(this.program, 'resolution');
|
||||
this.textureLocation = this.gl.getUniformLocation(this.program, 'tDiffuse');
|
||||
}
|
||||
|
||||
createShader(type, source) {
|
||||
const shader = this.gl.createShader(type);
|
||||
this.gl.shaderSource(shader, source);
|
||||
this.gl.compileShader(shader);
|
||||
|
||||
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
|
||||
console.error('Shader compile error:', this.gl.getShaderInfoLog(shader));
|
||||
this.gl.deleteShader(shader);
|
||||
return null;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
createProgram(vertexShader, fragmentShader) {
|
||||
const program = this.gl.createProgram();
|
||||
this.gl.attachShader(program, vertexShader);
|
||||
this.gl.attachShader(program, fragmentShader);
|
||||
this.gl.linkProgram(program);
|
||||
|
||||
if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
|
||||
console.error('Program link error:', this.gl.getProgramInfoLog(program));
|
||||
return null;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
initBuffers() {
|
||||
// Create a buffer for positions
|
||||
this.positionBuffer = this.gl.createBuffer();
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
||||
|
||||
// Full screen quad (2 triangles)
|
||||
const positions = [
|
||||
-1, -1,
|
||||
1, -1,
|
||||
-1, 1,
|
||||
-1, 1,
|
||||
1, -1,
|
||||
1, 1
|
||||
];
|
||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW);
|
||||
|
||||
// Create a buffer for texture coordinates
|
||||
this.texCoordBuffer = this.gl.createBuffer();
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
|
||||
|
||||
// Texture coordinates for the quad with Y-flipped to match canvas coordinates
|
||||
const texCoords = [
|
||||
0, 1, // top-left (flipped from 0,0)
|
||||
1, 1, // top-right (flipped from 1,0)
|
||||
0, 0, // bottom-left (flipped from 0,1)
|
||||
0, 0, // bottom-left (flipped from 0,1)
|
||||
1, 1, // top-right (flipped from 1,0)
|
||||
1, 0 // bottom-right (flipped from 1,1)
|
||||
];
|
||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(texCoords), this.gl.STATIC_DRAW);
|
||||
}
|
||||
|
||||
initTextures() {
|
||||
// Create texture for input canvas
|
||||
this.texture = this.gl.createTexture();
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
|
||||
|
||||
// Set parameters for texture
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.gl) return;
|
||||
|
||||
// Update texture from input canvas
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
|
||||
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.inputCanvas);
|
||||
|
||||
// Set viewport and clear
|
||||
this.gl.viewport(0, 0, this.outputCanvas.width, this.outputCanvas.height);
|
||||
this.gl.clearColor(0, 0, 0, 0);
|
||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
||||
|
||||
// Use shader program
|
||||
this.gl.useProgram(this.program);
|
||||
|
||||
// Set uniforms
|
||||
this.gl.uniform2f(this.resolutionLocation, this.inputCanvas.width, this.inputCanvas.height);
|
||||
this.gl.uniform1i(this.textureLocation, 0);
|
||||
|
||||
// Set position attribute
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
||||
this.gl.enableVertexAttribArray(this.positionLocation);
|
||||
this.gl.vertexAttribPointer(this.positionLocation, 2, this.gl.FLOAT, false, 0, 0);
|
||||
|
||||
// Set texture coordinate attribute
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
|
||||
this.gl.enableVertexAttribArray(this.texCoordLocation);
|
||||
this.gl.vertexAttribPointer(this.texCoordLocation, 2, this.gl.FLOAT, false, 0, 0);
|
||||
|
||||
// Draw
|
||||
this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
resize() {
|
||||
if (this.inputCanvas && this.outputCanvas) {
|
||||
this.outputCanvas.width = this.inputCanvas.width * this.options.scaleFactor;
|
||||
this.outputCanvas.height = this.inputCanvas.height * this.options.scaleFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { MiscUpscaler as UpscalerPlugin };
|
||||
9
frontend/node_modules/cytoscape/src/extensions/renderer/index.mjs
generated
vendored
Normal file
9
frontend/node_modules/cytoscape/src/extensions/renderer/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import nullRenderer from './null/index.mjs';
|
||||
import baseRenderer from './base/index.mjs';
|
||||
import canvasRenderer from './canvas/index.mjs';
|
||||
|
||||
export default [
|
||||
{ name: 'null', impl: nullRenderer },
|
||||
{ name: 'base', impl: baseRenderer },
|
||||
{ name: 'canvas', impl: canvasRenderer },
|
||||
];
|
||||
145
frontend/node_modules/cytoscape/src/is.mjs
generated
vendored
Normal file
145
frontend/node_modules/cytoscape/src/is.mjs
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
/*global HTMLElement DocumentTouch */
|
||||
|
||||
import window from './window.mjs';
|
||||
|
||||
let navigator = window ? window.navigator : null;
|
||||
let document = window ? window.document : null;
|
||||
|
||||
let typeofstr = typeof '';
|
||||
let typeofobj = typeof {};
|
||||
let typeoffn = typeof function(){};
|
||||
let typeofhtmlele = typeof HTMLElement;
|
||||
|
||||
let instanceStr = function( obj ){
|
||||
return obj && obj.instanceString && fn( obj.instanceString ) ? obj.instanceString() : null;
|
||||
};
|
||||
|
||||
export const defined = obj =>
|
||||
obj != null; // not undefined or null
|
||||
|
||||
export const string = obj =>
|
||||
obj != null && typeof obj == typeofstr;
|
||||
|
||||
export const fn = obj =>
|
||||
obj != null && typeof obj === typeoffn;
|
||||
|
||||
export const array = obj =>
|
||||
!(elementOrCollection(obj)) && (Array.isArray ? Array.isArray( obj ) : obj != null && obj instanceof Array);
|
||||
|
||||
export const plainObject = obj =>
|
||||
obj != null && typeof obj === typeofobj && !array( obj ) && obj.constructor === Object;
|
||||
|
||||
export const object = obj =>
|
||||
obj != null && typeof obj === typeofobj;
|
||||
|
||||
export const number = obj =>
|
||||
obj != null && typeof obj === typeof 1 && !isNaN( obj );
|
||||
|
||||
export const integer = obj =>
|
||||
number( obj ) && Math.floor( obj ) === obj;
|
||||
|
||||
export const bool = obj =>
|
||||
obj != null && typeof obj === typeof true;
|
||||
|
||||
export const htmlElement = obj => {
|
||||
if( 'undefined' === typeofhtmlele ){
|
||||
return undefined;
|
||||
} else {
|
||||
return null != obj && obj instanceof HTMLElement;
|
||||
}
|
||||
};
|
||||
|
||||
export const elementOrCollection = obj =>
|
||||
element( obj ) || collection( obj );
|
||||
|
||||
export const element = obj =>
|
||||
instanceStr( obj ) === 'collection' && obj._private.single;
|
||||
|
||||
export const collection = obj =>
|
||||
instanceStr( obj ) === 'collection' && !obj._private.single;
|
||||
|
||||
export const core = obj =>
|
||||
instanceStr( obj ) === 'core';
|
||||
|
||||
export const style = obj =>
|
||||
instanceStr( obj ) === 'style';
|
||||
|
||||
export const stylesheet = obj =>
|
||||
instanceStr( obj ) === 'stylesheet';
|
||||
|
||||
export const event = obj =>
|
||||
instanceStr( obj ) === 'event';
|
||||
|
||||
export const thread = obj =>
|
||||
instanceStr( obj ) === 'thread';
|
||||
|
||||
export const fabric = obj =>
|
||||
instanceStr( obj ) === 'fabric';
|
||||
|
||||
export const emptyString = obj => {
|
||||
if( obj === undefined || obj === null ){ // null is empty
|
||||
return true;
|
||||
} else if( obj === '' || obj.match( /^\s+$/ ) ){
|
||||
return true; // empty string is empty
|
||||
}
|
||||
|
||||
return false; // otherwise, we don't know what we've got
|
||||
};
|
||||
|
||||
export const nonemptyString = obj => {
|
||||
if( obj && string( obj ) && obj !== '' && !obj.match( /^\s+$/ ) ){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const domElement = obj => {
|
||||
if( typeof HTMLElement === 'undefined' ){
|
||||
return false; // we're not in a browser so it doesn't matter
|
||||
} else {
|
||||
return obj instanceof HTMLElement;
|
||||
}
|
||||
};
|
||||
|
||||
export const boundingBox = obj =>
|
||||
plainObject( obj ) &&
|
||||
number( obj.x1 ) && number( obj.x2 ) &&
|
||||
number( obj.y1 ) && number( obj.y2 )
|
||||
;
|
||||
|
||||
export const promise = obj =>
|
||||
object( obj ) && fn( obj.then );
|
||||
|
||||
export const touch = () =>
|
||||
window && ( ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch );
|
||||
|
||||
export const gecko = () =>
|
||||
window && ( typeof InstallTrigger !== 'undefined' || ('MozAppearance' in document.documentElement.style) );
|
||||
|
||||
export const webkit = () =>
|
||||
window && ( typeof webkitURL !== 'undefined' || ('WebkitAppearance' in document.documentElement.style) );
|
||||
|
||||
export const chromium = () =>
|
||||
window && ( typeof chrome !== 'undefined' );
|
||||
|
||||
export const khtml = () =>
|
||||
navigator && navigator.vendor.match( /kde/i ); // probably a better way to detect this...
|
||||
|
||||
export const khtmlEtc = () =>
|
||||
khtml() || webkit() || chromium();
|
||||
|
||||
export const ms = () =>
|
||||
navigator && navigator.userAgent.match( /msie|trident|edge/i ); // probably a better way to detect this...
|
||||
|
||||
export const windows = () =>
|
||||
navigator && navigator.appVersion.match( /Win/i );
|
||||
|
||||
export const mac = () =>
|
||||
navigator && navigator.appVersion.match( /Mac/i );
|
||||
|
||||
export const linux = () =>
|
||||
navigator && navigator.appVersion.match( /Linux/i );
|
||||
|
||||
export const unix = () =>
|
||||
navigator && navigator.appVersion.match( /X11/i );
|
||||
13
frontend/node_modules/cytoscape/src/selector/new-query.mjs
generated
vendored
Normal file
13
frontend/node_modules/cytoscape/src/selector/new-query.mjs
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Make a new query object
|
||||
*
|
||||
* @prop type {Type} The type enum (int) of the query
|
||||
* @prop checks List of checks to make against an ele to test for a match
|
||||
*/
|
||||
let newQuery = function(){
|
||||
return {
|
||||
checks: []
|
||||
};
|
||||
};
|
||||
|
||||
export default newQuery;
|
||||
254
frontend/node_modules/cytoscape/src/selector/parse.mjs
generated
vendored
Normal file
254
frontend/node_modules/cytoscape/src/selector/parse.mjs
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
import { warn } from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import exprs from './expressions.mjs';
|
||||
import newQuery from './new-query.mjs';
|
||||
import Type from './type.mjs';
|
||||
|
||||
/**
|
||||
* Of all the expressions, find the first match in the remaining text.
|
||||
* @param {string} remaining The remaining text to parse
|
||||
* @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
|
||||
*/
|
||||
const consumeExpr = ( remaining ) => {
|
||||
let expr;
|
||||
let match;
|
||||
let name;
|
||||
|
||||
for( let j = 0; j < exprs.length; j++ ){
|
||||
let e = exprs[ j ];
|
||||
let n = e.name;
|
||||
|
||||
let m = remaining.match( e.regexObj );
|
||||
|
||||
if( m != null ){
|
||||
match = m;
|
||||
expr = e;
|
||||
name = n;
|
||||
|
||||
let 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,
|
||||
remaining: remaining
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Consume all the leading whitespace
|
||||
* @param {string} remaining The text to consume
|
||||
* @returns The text with the leading whitespace removed
|
||||
*/
|
||||
const consumeWhitespace = ( remaining ) => {
|
||||
let match = remaining.match( /^\s+/ );
|
||||
|
||||
if( match ){
|
||||
let consumed = match[0];
|
||||
remaining = remaining.substring( consumed.length );
|
||||
}
|
||||
|
||||
return remaining;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the string and store the parsed representation in the Selector.
|
||||
* @param {string} selector The selector string
|
||||
* @returns `true` if the selector was successfully parsed, `false` otherwise
|
||||
*/
|
||||
const parse = function( selector ){
|
||||
let self = this;
|
||||
|
||||
let remaining = self.inputText = selector;
|
||||
|
||||
let currentQuery = self[0] = newQuery();
|
||||
self.length = 1;
|
||||
|
||||
remaining = consumeWhitespace( remaining ); // get rid of leading whitespace
|
||||
|
||||
for( ;; ){
|
||||
let exprInfo = consumeExpr( remaining );
|
||||
|
||||
if( exprInfo.expr == null ){
|
||||
warn( 'The selector `' + selector + '`is invalid' );
|
||||
return false;
|
||||
} else {
|
||||
let args = exprInfo.match.slice( 1 );
|
||||
|
||||
// let the token populate the selector object in currentQuery
|
||||
let ret = exprInfo.expr.populate( self, currentQuery, args );
|
||||
|
||||
if( ret === false ){
|
||||
return false; // exit if population failed
|
||||
} else if( ret != null ){
|
||||
currentQuery = ret; // change the current query to be filled if the expr specifies
|
||||
}
|
||||
}
|
||||
|
||||
remaining = exprInfo.remaining;
|
||||
|
||||
// we're done when there's nothing left to parse
|
||||
if( remaining.match( /^\s*$/ ) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let lastQ = self[self.length - 1];
|
||||
|
||||
if( self.currentSubject != null ){
|
||||
lastQ.subject = self.currentSubject;
|
||||
}
|
||||
|
||||
lastQ.edgeCount = self.edgeCount;
|
||||
lastQ.compoundCount = self.compoundCount;
|
||||
|
||||
for( let i = 0; i < self.length; i++ ){
|
||||
let q = self[i];
|
||||
|
||||
// in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
|
||||
if( q.compoundCount > 0 && q.edgeCount > 0 ){
|
||||
warn( 'The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector' );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( q.edgeCount > 1 ){
|
||||
warn( 'The selector `' + selector + '` is invalid because it uses multiple edge selectors' );
|
||||
return false;
|
||||
} else if( q.edgeCount === 1 ){
|
||||
warn( 'The selector `' + selector + '` is deprecated. Edge selectors do not take effect on changes to source and target nodes after an edge is added, for performance reasons. Use a class or data selector on edges instead, updating the class or data of an edge when your app detects a change in source or target nodes.' );
|
||||
}
|
||||
}
|
||||
|
||||
return true; // success
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selector represented as a string. This value uses default formatting,
|
||||
* so things like spacing may differ from the input text passed to the constructor.
|
||||
* @returns {string} The selector string
|
||||
*/
|
||||
export const toString = function(){
|
||||
if( this.toStringCache != null ){
|
||||
return this.toStringCache;
|
||||
}
|
||||
|
||||
let clean = function( obj ){
|
||||
if( obj == null ){
|
||||
return '';
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
let cleanVal = function( val ){
|
||||
if( is.string( val ) ){
|
||||
return '"' + val + '"';
|
||||
} else {
|
||||
return clean( val );
|
||||
}
|
||||
};
|
||||
|
||||
let space = ( val ) => {
|
||||
return ' ' + val + ' ';
|
||||
};
|
||||
|
||||
let checkToString = ( check, subject ) => {
|
||||
let { type, value } = check;
|
||||
|
||||
switch( type ){
|
||||
case Type.GROUP: {
|
||||
let group = clean( value );
|
||||
|
||||
return group.substring( 0, group.length - 1 );
|
||||
}
|
||||
|
||||
case Type.DATA_COMPARE: {
|
||||
let { field, operator } = check;
|
||||
|
||||
return '[' + field + space( clean( operator ) ) + cleanVal( value ) + ']';
|
||||
}
|
||||
|
||||
case Type.DATA_BOOL: {
|
||||
let { operator, field } = check;
|
||||
|
||||
return '[' + clean( operator ) + field + ']';
|
||||
}
|
||||
|
||||
case Type.DATA_EXIST: {
|
||||
let { field } = check;
|
||||
|
||||
return '[' + field + ']';
|
||||
}
|
||||
|
||||
case Type.META_COMPARE: {
|
||||
let { operator, field } = check;
|
||||
|
||||
return '[[' + field + space( clean( operator ) ) + cleanVal( value ) + ']]';
|
||||
}
|
||||
|
||||
case Type.STATE: {
|
||||
return value;
|
||||
}
|
||||
|
||||
case Type.ID: {
|
||||
return '#' + value;
|
||||
}
|
||||
|
||||
case Type.CLASS: {
|
||||
return '.' + value;
|
||||
}
|
||||
|
||||
case Type.PARENT:
|
||||
case Type.CHILD: {
|
||||
return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
|
||||
}
|
||||
|
||||
case Type.ANCESTOR:
|
||||
case Type.DESCENDANT: {
|
||||
return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
|
||||
}
|
||||
|
||||
case Type.COMPOUND_SPLIT: {
|
||||
let lhs = queryToString(check.left, subject);
|
||||
let sub = queryToString(check.subject, subject);
|
||||
let rhs = queryToString(check.right, subject);
|
||||
|
||||
return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
|
||||
}
|
||||
|
||||
case Type.TRUE: {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let queryToString = ( query, subject ) => {
|
||||
return query.checks.reduce((str, chk, i) => {
|
||||
return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
|
||||
}, '');
|
||||
};
|
||||
|
||||
let str = '';
|
||||
|
||||
for( let i = 0; i < this.length; i++ ){
|
||||
let query = this[ i ];
|
||||
|
||||
str += queryToString( query, query.subject );
|
||||
|
||||
if( this.length > 1 && i < this.length - 1 ){
|
||||
str += ', ';
|
||||
}
|
||||
}
|
||||
|
||||
this.toStringCache = str;
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
export default { parse, toString };
|
||||
122
frontend/node_modules/cytoscape/src/selector/query-type-match.mjs
generated
vendored
Normal file
122
frontend/node_modules/cytoscape/src/selector/query-type-match.mjs
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
import Type from './type.mjs';
|
||||
import { stateSelectorMatches } from './state.mjs';
|
||||
import { valCmp, boolCmp, existCmp, meta, data } from './data.mjs';
|
||||
|
||||
/** A lookup of `match(check, ele)` functions by `Type` int */
|
||||
export const match = [];
|
||||
|
||||
/**
|
||||
* Returns whether the query matches for the element
|
||||
* @param query The `{ type, value, ... }` query object
|
||||
* @param ele The element to compare against
|
||||
*/
|
||||
export const matches = (query, ele) => {
|
||||
return query.checks.every( chk => match[chk.type](chk, ele) );
|
||||
};
|
||||
|
||||
match[Type.GROUP] = (check, ele) => {
|
||||
let group = check.value;
|
||||
|
||||
return group === '*' || group === ele.group();
|
||||
};
|
||||
|
||||
match[Type.STATE] = (check, ele) => {
|
||||
let stateSelector = check.value;
|
||||
|
||||
return stateSelectorMatches( stateSelector, ele );
|
||||
};
|
||||
|
||||
match[Type.ID] = (check, ele) => {
|
||||
let id = check.value;
|
||||
|
||||
return ele.id() === id;
|
||||
};
|
||||
|
||||
match[Type.CLASS] = (check, ele) => {
|
||||
let cls = check.value;
|
||||
|
||||
return ele.hasClass(cls);
|
||||
};
|
||||
|
||||
match[Type.META_COMPARE] = (check, ele) => {
|
||||
let { field, operator, value } = check;
|
||||
|
||||
return valCmp( meta(ele, field), operator, value );
|
||||
};
|
||||
|
||||
match[Type.DATA_COMPARE] = (check, ele) => {
|
||||
let { field, operator, value } = check;
|
||||
|
||||
return valCmp( data(ele, field), operator, value );
|
||||
};
|
||||
|
||||
match[Type.DATA_BOOL] = (check, ele) => {
|
||||
let { field, operator } = check;
|
||||
|
||||
return boolCmp( data(ele, field), operator );
|
||||
};
|
||||
|
||||
match[Type.DATA_EXIST] = (check, ele) => {
|
||||
let { field, operator } = check;
|
||||
|
||||
return existCmp( data(ele, field), operator );
|
||||
};
|
||||
|
||||
match[Type.UNDIRECTED_EDGE] = (check, ele) => {
|
||||
let qA = check.nodes[0];
|
||||
let qB = check.nodes[1];
|
||||
let src = ele.source();
|
||||
let tgt = ele.target();
|
||||
|
||||
return ( matches(qA, src) && matches(qB, tgt) ) || ( matches(qB, src) && matches(qA, tgt) );
|
||||
};
|
||||
|
||||
match[Type.NODE_NEIGHBOR] = (check, ele) => {
|
||||
return matches(check.node, ele) && ele.neighborhood().some( n => n.isNode() && matches(check.neighbor, n) );
|
||||
};
|
||||
|
||||
match[Type.DIRECTED_EDGE] = (check, ele) => {
|
||||
return matches(check.source, ele.source()) && matches(check.target, ele.target());
|
||||
};
|
||||
|
||||
match[Type.NODE_SOURCE] = (check, ele) => {
|
||||
return matches(check.source, ele) && ele.outgoers().some( n => n.isNode() && matches(check.target, n) );
|
||||
};
|
||||
|
||||
match[Type.NODE_TARGET] = (check, ele) => {
|
||||
return matches(check.target, ele) && ele.incomers().some( n => n.isNode() && matches(check.source, n) );
|
||||
};
|
||||
|
||||
match[Type.CHILD] = (check, ele) => {
|
||||
return matches(check.child, ele) && matches(check.parent, ele.parent());
|
||||
};
|
||||
|
||||
match[Type.PARENT] = (check, ele) => {
|
||||
return matches(check.parent, ele) && ele.children().some( c => matches(check.child, c) );
|
||||
};
|
||||
|
||||
match[Type.DESCENDANT] = (check, ele) => {
|
||||
return matches(check.descendant, ele) && ele.ancestors().some( a => matches(check.ancestor, a) );
|
||||
};
|
||||
|
||||
match[Type.ANCESTOR] = (check, ele) => {
|
||||
return matches(check.ancestor, ele) && ele.descendants().some( d => matches(check.descendant, d) );
|
||||
};
|
||||
|
||||
match[Type.COMPOUND_SPLIT] = (check, ele) => {
|
||||
return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
|
||||
};
|
||||
|
||||
match[Type.TRUE] = () => true;
|
||||
|
||||
match[Type.COLLECTION] = (check, ele) => {
|
||||
let collection = check.value;
|
||||
|
||||
return collection.has(ele);
|
||||
};
|
||||
|
||||
match[Type.FILTER] = (check, ele) => {
|
||||
let filter = check.value;
|
||||
|
||||
return filter(ele);
|
||||
};
|
||||
849
frontend/node_modules/cytoscape/src/style/apply.mjs
generated
vendored
Normal file
849
frontend/node_modules/cytoscape/src/style/apply.mjs
generated
vendored
Normal file
@@ -0,0 +1,849 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import Promise from '../promise.mjs';
|
||||
|
||||
const styfn = {};
|
||||
|
||||
// keys for style blocks, e.g. ttfftt
|
||||
const TRUE = 't';
|
||||
const FALSE = 'f';
|
||||
|
||||
// (potentially expensive calculation)
|
||||
// apply the style to the element based on
|
||||
// - its bypass
|
||||
// - what selectors match it
|
||||
styfn.apply = function( eles ){
|
||||
let self = this;
|
||||
let _p = self._private;
|
||||
let cy = _p.cy;
|
||||
let updatedEles = cy.collection();
|
||||
|
||||
for( let ie = 0; ie < eles.length; ie++ ){
|
||||
let ele = eles[ ie ];
|
||||
let cxtMeta = self.getContextMeta( ele );
|
||||
|
||||
if( cxtMeta.empty ){
|
||||
continue;
|
||||
}
|
||||
|
||||
let cxtStyle = self.getContextStyle( cxtMeta );
|
||||
let app = self.applyContextStyle( cxtMeta, cxtStyle, ele );
|
||||
|
||||
if( ele._private.appliedInitStyle ){
|
||||
self.updateTransitions( ele, app.diffProps );
|
||||
} else {
|
||||
ele._private.appliedInitStyle = true;
|
||||
}
|
||||
|
||||
let hintsDiff = self.updateStyleHints( ele );
|
||||
|
||||
if( hintsDiff ){
|
||||
updatedEles.push( ele );
|
||||
}
|
||||
|
||||
} // for elements
|
||||
|
||||
return updatedEles;
|
||||
};
|
||||
|
||||
styfn.getPropertiesDiff = function( oldCxtKey, newCxtKey ){
|
||||
let self = this;
|
||||
let cache = self._private.propDiffs = self._private.propDiffs || {};
|
||||
let dualCxtKey = oldCxtKey + '-' + newCxtKey;
|
||||
let cachedVal = cache[ dualCxtKey ];
|
||||
|
||||
if( cachedVal ){
|
||||
return cachedVal;
|
||||
}
|
||||
|
||||
let diffProps = [];
|
||||
let addedProp = {};
|
||||
|
||||
for( let i = 0; i < self.length; i++ ){
|
||||
let cxt = self[ i ];
|
||||
let oldHasCxt = oldCxtKey[ i ] === TRUE;
|
||||
let newHasCxt = newCxtKey[ i ] === TRUE;
|
||||
let cxtHasDiffed = oldHasCxt !== newHasCxt;
|
||||
let cxtHasMappedProps = cxt.mappedProperties.length > 0;
|
||||
|
||||
if( cxtHasDiffed || ( newHasCxt && cxtHasMappedProps )){
|
||||
let 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( let j = 0; j < props.length; j++ ){
|
||||
let prop = props[ j ];
|
||||
let 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)
|
||||
let laterCxtOverrides = false;
|
||||
for( let k = i + 1; k < self.length; k++ ){
|
||||
let laterCxt = self[ k ];
|
||||
let hasLaterCxt = newCxtKey[ k ] === TRUE;
|
||||
|
||||
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 ){
|
||||
let self = this;
|
||||
let cxtKey = '';
|
||||
let diffProps;
|
||||
let prevKey = ele._private.styleCxtKey || '';
|
||||
|
||||
// get the cxt key
|
||||
for( let i = 0; i < self.length; i++ ){
|
||||
let context = self[ i ];
|
||||
let contextSelectorMatches = context.selector && context.selector.matches( ele ); // NB: context.selector may be null for 'core'
|
||||
|
||||
if( contextSelectorMatches ){
|
||||
cxtKey += TRUE;
|
||||
} else {
|
||||
cxtKey += FALSE;
|
||||
}
|
||||
} // for context
|
||||
|
||||
diffProps = self.getPropertiesDiff( prevKey, cxtKey );
|
||||
|
||||
ele._private.styleCxtKey = cxtKey;
|
||||
|
||||
return {
|
||||
key: cxtKey,
|
||||
diffPropNames: diffProps,
|
||||
empty: diffProps.length === 0
|
||||
};
|
||||
};
|
||||
|
||||
// gets a computed ele style object based on matched contexts
|
||||
styfn.getContextStyle = function( cxtMeta ){
|
||||
let cxtKey = cxtMeta.key;
|
||||
let self = this;
|
||||
let cxtStyles = this._private.contextStyles = this._private.contextStyles || {};
|
||||
|
||||
// if already computed style, returned cached copy
|
||||
if( cxtStyles[ cxtKey ] ){ return cxtStyles[ cxtKey ]; }
|
||||
|
||||
let style = {
|
||||
_private: {
|
||||
key: cxtKey
|
||||
}
|
||||
};
|
||||
|
||||
for( let i = 0; i < self.length; i++ ){
|
||||
let cxt = self[ i ];
|
||||
let hasCxt = cxtKey[ i ] === TRUE;
|
||||
|
||||
if( !hasCxt ){ continue; }
|
||||
|
||||
for( let j = 0; j < cxt.properties.length; j++ ){
|
||||
let prop = cxt.properties[ j ];
|
||||
|
||||
style[ prop.name ] = prop;
|
||||
}
|
||||
}
|
||||
|
||||
cxtStyles[ cxtKey ] = style;
|
||||
return style;
|
||||
};
|
||||
|
||||
styfn.applyContextStyle = function( cxtMeta, cxtStyle, ele ){
|
||||
let self = this;
|
||||
let diffProps = cxtMeta.diffPropNames;
|
||||
let retDiffProps = {};
|
||||
let types = self.types;
|
||||
|
||||
for( let i = 0; i < diffProps.length; i++ ){
|
||||
let diffPropName = diffProps[ i ];
|
||||
let cxtProp = cxtStyle[ diffPropName ];
|
||||
let eleProp = ele.pstyle( diffPropName );
|
||||
|
||||
if( !cxtProp ){ // no context prop means delete
|
||||
if( !eleProp ){
|
||||
continue; // no existing prop means nothing needs to be removed
|
||||
// nb affects initial application on mapped values like control-point-distances
|
||||
} else if( eleProp.bypass ){
|
||||
cxtProp = { name: diffPropName, deleteBypassed: true };
|
||||
} else {
|
||||
cxtProp = { name: diffPropName, delete: true };
|
||||
}
|
||||
}
|
||||
|
||||
// save cycles when the context prop doesn't need to be applied
|
||||
if( eleProp === cxtProp ){ continue; }
|
||||
|
||||
// save cycles when a mapped context prop doesn't need to be applied
|
||||
if(
|
||||
cxtProp.mapped === types.fn // context prop is function mapper
|
||||
&& eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
|
||||
&& eleProp.mapping != null // ele prop is a concrete value from from a mapper
|
||||
&& eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
|
||||
){ // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
|
||||
let mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
|
||||
let fnValue = mapping.fnValue = cxtProp.value( ele ); // temporarily cache the value in case of a miss
|
||||
|
||||
if( fnValue === mapping.prevFnValue ){ continue; }
|
||||
}
|
||||
|
||||
let retDiffProp = retDiffProps[ diffPropName ] = {
|
||||
prev: eleProp
|
||||
};
|
||||
|
||||
self.applyParsedProperty( ele, cxtProp );
|
||||
|
||||
retDiffProp.next = ele.pstyle( diffPropName );
|
||||
|
||||
if( retDiffProp.next && retDiffProp.next.bypass ){
|
||||
retDiffProp.next = retDiffProp.next.bypassed;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
diffProps: retDiffProps
|
||||
};
|
||||
};
|
||||
|
||||
styfn.updateStyleHints = function(ele){
|
||||
let _p = ele._private;
|
||||
let self = this;
|
||||
let propNames = self.propertyGroupNames;
|
||||
let propGrKeys = self.propertyGroupKeys;
|
||||
let propHash = ( ele, propNames, seedKey ) => self.getPropertiesHash( ele, propNames, seedKey );
|
||||
let oldStyleKey = _p.styleKey;
|
||||
|
||||
if( ele.removed() ){ return false; }
|
||||
|
||||
let isNode = _p.group === 'nodes';
|
||||
|
||||
// get the style key hashes per prop group
|
||||
// but lazily -- only use non-default prop values to reduce the number of hashes
|
||||
//
|
||||
|
||||
let overriddenStyles = ele._private.style;
|
||||
|
||||
propNames = Object.keys( overriddenStyles );
|
||||
|
||||
for( let i = 0; i < propGrKeys.length; i++ ){
|
||||
let grKey = propGrKeys[i];
|
||||
|
||||
_p.styleKeys[ grKey ] = [ util.DEFAULT_HASH_SEED, util.DEFAULT_HASH_SEED_ALT ];
|
||||
}
|
||||
|
||||
let updateGrKey1 = (val, grKey) => _p.styleKeys[ grKey ][0] = util.hashInt( val, _p.styleKeys[ grKey ][0] );
|
||||
let updateGrKey2 = (val, grKey) => _p.styleKeys[ grKey ][1] = util.hashIntAlt( val, _p.styleKeys[ grKey ][1] );
|
||||
|
||||
let updateGrKey = (val, grKey) => {
|
||||
updateGrKey1(val, grKey);
|
||||
updateGrKey2(val, grKey);
|
||||
};
|
||||
|
||||
let updateGrKeyWStr = (strVal, grKey) => {
|
||||
for( let j = 0; j < strVal.length; j++ ){
|
||||
let ch = strVal.charCodeAt(j);
|
||||
|
||||
updateGrKey1(ch, grKey);
|
||||
updateGrKey2(ch, grKey);
|
||||
}
|
||||
};
|
||||
|
||||
// - hashing works on 32 bit ints b/c we use bitwise ops
|
||||
// - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
|
||||
// - raise up small numbers so more significant digits are seen by hashing
|
||||
// - make small numbers larger than a normal value to avoid collisions
|
||||
// - works in practice and it's relatively cheap
|
||||
let N = 2000000000;
|
||||
let cleanNum = val => (-128 < val && val < 128) && Math.floor(val) !== val ? N - ((val * 1024) | 0) : val;
|
||||
|
||||
for( let i = 0; i < propNames.length; i++ ){
|
||||
let name = propNames[i];
|
||||
let parsedProp = overriddenStyles[ name ];
|
||||
|
||||
if( parsedProp == null ){ continue; }
|
||||
|
||||
let propInfo = this.properties[name];
|
||||
let type = propInfo.type;
|
||||
let grKey = propInfo.groupKey;
|
||||
let normalizedNumberVal;
|
||||
|
||||
if( propInfo.hashOverride != null ){
|
||||
normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
|
||||
} else if( parsedProp.pfValue != null ){
|
||||
normalizedNumberVal = parsedProp.pfValue;
|
||||
}
|
||||
|
||||
// might not be a number if it allows enums
|
||||
let numberVal = propInfo.enums == null ? parsedProp.value : null;
|
||||
let haveNormNum = normalizedNumberVal != null;
|
||||
let haveUnitedNum = numberVal != null;
|
||||
let haveNum = haveNormNum || haveUnitedNum;
|
||||
let units = parsedProp.units;
|
||||
|
||||
// numbers are cheaper to hash than strings
|
||||
// 1 hash op vs n hash ops (for length n string)
|
||||
if( type.number && haveNum && !type.multiple ){
|
||||
let v = haveNormNum ? normalizedNumberVal : numberVal;
|
||||
|
||||
updateGrKey(cleanNum(v), grKey);
|
||||
|
||||
if( !haveNormNum && units != null ){
|
||||
updateGrKeyWStr(units, grKey);
|
||||
}
|
||||
} else {
|
||||
updateGrKeyWStr(parsedProp.strValue, grKey);
|
||||
}
|
||||
}
|
||||
|
||||
// overall style key
|
||||
//
|
||||
|
||||
let hash = [ util.DEFAULT_HASH_SEED, util.DEFAULT_HASH_SEED_ALT ];
|
||||
|
||||
for( let i = 0; i < propGrKeys.length; i++ ){
|
||||
let grKey = propGrKeys[i];
|
||||
let grHash = _p.styleKeys[ grKey ];
|
||||
|
||||
hash[0] = util.hashInt( grHash[0], hash[0] );
|
||||
hash[1] = util.hashIntAlt( grHash[1], hash[1] );
|
||||
}
|
||||
|
||||
_p.styleKey = util.combineHashes(hash[0], hash[1]);
|
||||
|
||||
// label dims
|
||||
//
|
||||
|
||||
let sk = _p.styleKeys;
|
||||
|
||||
_p.labelDimsKey = util.combineHashesArray(sk.labelDimensions);
|
||||
|
||||
let labelKeys = propHash( ele, ['label'], sk.labelDimensions );
|
||||
|
||||
_p.labelKey = util.combineHashesArray(labelKeys);
|
||||
_p.labelStyleKey = util.combineHashesArray(util.hashArrays(sk.commonLabel, labelKeys));
|
||||
|
||||
if( !isNode ){
|
||||
let sourceLabelKeys = propHash( ele, ['source-label'], sk.labelDimensions );
|
||||
_p.sourceLabelKey = util.combineHashesArray(sourceLabelKeys);
|
||||
_p.sourceLabelStyleKey = util.combineHashesArray(util.hashArrays(sk.commonLabel, sourceLabelKeys));
|
||||
|
||||
let targetLabelKeys = propHash( ele, ['target-label'], sk.labelDimensions );
|
||||
_p.targetLabelKey = util.combineHashesArray(targetLabelKeys);
|
||||
_p.targetLabelStyleKey = util.combineHashesArray(util.hashArrays(sk.commonLabel, targetLabelKeys));
|
||||
}
|
||||
|
||||
// node
|
||||
//
|
||||
|
||||
if( isNode ){
|
||||
let { nodeBody, nodeBorder, nodeOutline, backgroundImage, compound, pie, stripe } = _p.styleKeys;
|
||||
|
||||
let nodeKeys = [ nodeBody, nodeBorder, nodeOutline, backgroundImage, compound, pie, stripe ].filter(k => k != null).reduce(util.hashArrays, [
|
||||
util.DEFAULT_HASH_SEED,
|
||||
util.DEFAULT_HASH_SEED_ALT
|
||||
]);
|
||||
_p.nodeKey = util.combineHashesArray(nodeKeys);
|
||||
|
||||
_p.hasPie = pie != null && pie[0] !== util.DEFAULT_HASH_SEED && pie[1] !== util.DEFAULT_HASH_SEED_ALT;
|
||||
|
||||
_p.hasStripe = stripe != null && stripe[0] !== util.DEFAULT_HASH_SEED && stripe[1] !== util.DEFAULT_HASH_SEED_ALT;
|
||||
}
|
||||
|
||||
return oldStyleKey !== _p.styleKey;
|
||||
};
|
||||
|
||||
styfn.clearStyleHints = function(ele){
|
||||
let _p = ele._private;
|
||||
|
||||
_p.styleCxtKey = '';
|
||||
_p.styleKeys = {};
|
||||
_p.styleKey = null;
|
||||
_p.labelKey = null;
|
||||
_p.labelStyleKey = null;
|
||||
_p.sourceLabelKey = null;
|
||||
_p.sourceLabelStyleKey = null;
|
||||
_p.targetLabelKey = null;
|
||||
_p.targetLabelStyleKey = null;
|
||||
_p.nodeKey = null;
|
||||
_p.hasPie = null;
|
||||
_p.hasStripe = null;
|
||||
};
|
||||
|
||||
// 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 ){
|
||||
let self = this;
|
||||
let prop = parsedProp;
|
||||
let style = ele._private.style;
|
||||
let flatProp;
|
||||
let types = self.types;
|
||||
let type = self.properties[ prop.name ].type;
|
||||
let propIsBypass = prop.bypass;
|
||||
let origProp = style[ prop.name ];
|
||||
let origPropIsBypass = origProp && origProp.bypass;
|
||||
let _p = ele._private;
|
||||
let flatPropMapping = 'mapping';
|
||||
|
||||
let getVal = p => {
|
||||
if( p == null ){
|
||||
return null;
|
||||
} else if( p.pfValue != null ){
|
||||
return p.pfValue;
|
||||
} else {
|
||||
return p.value;
|
||||
}
|
||||
};
|
||||
|
||||
let checkTriggers = () => {
|
||||
let fromVal = getVal(origProp);
|
||||
let toVal = getVal(prop);
|
||||
|
||||
self.checkTriggers( ele, prop.name, fromVal, toVal );
|
||||
};
|
||||
|
||||
// edge sanity checks to prevent the client from making serious mistakes
|
||||
if(
|
||||
parsedProp.name === 'curve-style'
|
||||
&& ele.isEdge()
|
||||
&& (
|
||||
( // loops must be bundled beziers
|
||||
parsedProp.value !== 'bezier'
|
||||
&& ele.isLoop()
|
||||
) || ( // edges connected to compound nodes can not be haystacks
|
||||
parsedProp.value === 'haystack'
|
||||
&& ( ele.source().isParent() || ele.target().isParent() )
|
||||
)
|
||||
)
|
||||
){
|
||||
prop = parsedProp = this.parse( parsedProp.name, 'bezier', propIsBypass );
|
||||
}
|
||||
|
||||
if( prop.delete ){ // delete the property and use the default value on falsey value
|
||||
style[ prop.name ] = undefined;
|
||||
|
||||
checkTriggers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( prop.deleteBypassed ){ // delete the property that the
|
||||
if( !origProp ){
|
||||
checkTriggers();
|
||||
|
||||
return true; // can't delete if no prop
|
||||
|
||||
} else if( origProp.bypass ){ // delete bypassed
|
||||
origProp.bypassed = undefined;
|
||||
|
||||
checkTriggers();
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false; // we're unsuccessful deleting the bypassed
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to delete the current bypass
|
||||
if( prop.deleteBypass ){ // then this property is just here to indicate we need to delete
|
||||
if( !origProp ){
|
||||
checkTriggers();
|
||||
|
||||
return true; // property is already not defined
|
||||
|
||||
} else if( origProp.bypass ){ // 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 ] = origProp.bypassed;
|
||||
|
||||
checkTriggers();
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false; // we're unsuccessful deleting the bypass
|
||||
}
|
||||
}
|
||||
|
||||
let printMappingErr = function(){
|
||||
util.warn( 'Do not assign mappings to elements without corresponding data (i.e. ele `' + ele.id() + '` has no mapping 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: {
|
||||
// flatten the field (e.g. data.foo.bar)
|
||||
let fields = prop.field.split( '.' );
|
||||
let fieldVal = _p.data;
|
||||
|
||||
for( let i = 0; i < fields.length && fieldVal; i++ ){
|
||||
let field = fields[ i ];
|
||||
fieldVal = fieldVal[ field ];
|
||||
}
|
||||
|
||||
if( fieldVal == null ){
|
||||
printMappingErr();
|
||||
return false;
|
||||
}
|
||||
|
||||
let percent;
|
||||
if( !is.number( fieldVal ) ){ // then don't apply and fall back on the existing style
|
||||
util.warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
|
||||
return false;
|
||||
} else {
|
||||
let fieldWidth = prop.fieldMax - prop.fieldMin;
|
||||
|
||||
if( fieldWidth === 0 ){ // safety check -- not strictly necessary as no props of zero range should be passed here
|
||||
percent = 0;
|
||||
} else {
|
||||
percent = (fieldVal - prop.fieldMin) / fieldWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure to bound percent value
|
||||
if( percent < 0 ){
|
||||
percent = 0;
|
||||
} else if( percent > 1 ){
|
||||
percent = 1;
|
||||
}
|
||||
|
||||
if( type.color ){
|
||||
let r1 = prop.valueMin[0];
|
||||
let r2 = prop.valueMax[0];
|
||||
let g1 = prop.valueMin[1];
|
||||
let g2 = prop.valueMax[1];
|
||||
let b1 = prop.valueMin[2];
|
||||
let b2 = prop.valueMax[2];
|
||||
let a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
|
||||
let a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
|
||||
|
||||
let 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 ){
|
||||
let calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
|
||||
flatProp = this.parse( prop.name, calcValue, prop.bypass, flatPropMapping );
|
||||
|
||||
} else {
|
||||
return false; // can only map to colours and numbers
|
||||
}
|
||||
|
||||
if( !flatProp ){ // if we can't flatten the property, then don't apply the property and fall back on the existing style
|
||||
printMappingErr();
|
||||
return false;
|
||||
}
|
||||
|
||||
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: {
|
||||
// flatten the field (e.g. data.foo.bar)
|
||||
let fields = prop.field.split( '.' );
|
||||
let fieldVal = _p.data;
|
||||
|
||||
for( let i = 0; i < fields.length && fieldVal; i++ ){
|
||||
let field = fields[ i ];
|
||||
fieldVal = fieldVal[ field ];
|
||||
}
|
||||
|
||||
if( fieldVal != null ){
|
||||
flatProp = this.parse( prop.name, fieldVal, prop.bypass, flatPropMapping );
|
||||
}
|
||||
|
||||
if( !flatProp ){ // if we can't flatten the property, then don't apply and fall back on the existing style
|
||||
printMappingErr();
|
||||
return false;
|
||||
}
|
||||
|
||||
flatProp.mapping = prop; // keep a reference to the mapping
|
||||
prop = flatProp; // the flattened (mapped) property is the one we want
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case types.fn: {
|
||||
let fn = prop.value;
|
||||
let fnRetVal = prop.fnValue != null ? prop.fnValue : fn( ele ); // check for cached value before calling function
|
||||
|
||||
prop.prevFnValue = fnRetVal;
|
||||
|
||||
if( fnRetVal == null ){
|
||||
util.warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
|
||||
return false;
|
||||
}
|
||||
|
||||
flatProp = this.parse( prop.name, fnRetVal, prop.bypass, flatPropMapping );
|
||||
|
||||
if( !flatProp ){
|
||||
util.warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
|
||||
return false;
|
||||
}
|
||||
|
||||
flatProp.mapping = util.copy( 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;
|
||||
}
|
||||
}
|
||||
|
||||
checkTriggers();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
styfn.cleanElements = function( eles, keepBypasses ){
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[i];
|
||||
|
||||
this.clearStyleHints(ele);
|
||||
|
||||
ele.dirtyCompoundBoundsCache();
|
||||
ele.dirtyBoundingBoxCache();
|
||||
|
||||
if( !keepBypasses ){
|
||||
ele._private.style = {};
|
||||
} else {
|
||||
let style = ele._private.style;
|
||||
let propNames = Object.keys(style);
|
||||
|
||||
for( let j = 0; j < propNames.length; j++ ){
|
||||
let propName = propNames[j];
|
||||
let eleProp = style[ propName ];
|
||||
|
||||
if( eleProp != null ){
|
||||
if( eleProp.bypass ){
|
||||
eleProp.bypassed = null;
|
||||
} else {
|
||||
style[ propName ] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// updates the visual style for all elements (useful for manual style modification after init)
|
||||
styfn.update = function(){
|
||||
let cy = this._private.cy;
|
||||
let eles = cy.mutableElements();
|
||||
|
||||
eles.updateStyle();
|
||||
};
|
||||
|
||||
// diffProps : { name => { prev, next } }
|
||||
styfn.updateTransitions = function( ele, diffProps ){
|
||||
let self = this;
|
||||
let _p = ele._private;
|
||||
let props = ele.pstyle( 'transition-property' ).value;
|
||||
let duration = ele.pstyle( 'transition-duration' ).pfValue;
|
||||
let delay = ele.pstyle( 'transition-delay' ).pfValue;
|
||||
|
||||
if( props.length > 0 && duration > 0 ){
|
||||
|
||||
let style = {};
|
||||
|
||||
// build up the style to animate towards
|
||||
let anyPrev = false;
|
||||
for( let i = 0; i < props.length; i++ ){
|
||||
let prop = props[ i ];
|
||||
let styProp = ele.pstyle( prop );
|
||||
let diffProp = diffProps[ prop ];
|
||||
|
||||
if( !diffProp ){ continue; }
|
||||
|
||||
let prevProp = diffProp.prev;
|
||||
let fromProp = prevProp;
|
||||
let toProp = diffProp.next != null ? diffProp.next : styProp;
|
||||
let diff = false;
|
||||
let initVal;
|
||||
let 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 ){
|
||||
style[ 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;
|
||||
|
||||
( new Promise(function( resolve ){
|
||||
if( delay > 0 ){
|
||||
ele.delayAnimation( delay ).play().promise().then( resolve );
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}) ).then(function(){
|
||||
return ele.animation( {
|
||||
style: style,
|
||||
duration: duration,
|
||||
easing: ele.pstyle( 'transition-timing-function' ).value,
|
||||
queue: false
|
||||
} ).play().promise();
|
||||
}).then(function(){
|
||||
// if( !isBypass ){
|
||||
self.removeBypasses( ele, props );
|
||||
ele.emitAndNotify('style');
|
||||
// }
|
||||
|
||||
_p.transitioning = false;
|
||||
});
|
||||
|
||||
} else if( _p.transitioning ){
|
||||
this.removeBypasses( ele, props );
|
||||
ele.emitAndNotify('style');
|
||||
|
||||
_p.transitioning = false;
|
||||
}
|
||||
};
|
||||
|
||||
styfn.checkTrigger = function( ele, name, fromValue, toValue, getTrigger, onTrigger ){
|
||||
let prop = this.properties[ name ];
|
||||
let triggerCheck = getTrigger( prop );
|
||||
|
||||
if (ele.removed()) { return; }
|
||||
|
||||
if( triggerCheck != null && triggerCheck( fromValue, toValue, ele ) ){
|
||||
onTrigger(prop);
|
||||
}
|
||||
};
|
||||
|
||||
styfn.checkZOrderTrigger = function( ele, name, fromValue, toValue ){
|
||||
this.checkTrigger( ele, name, fromValue, toValue, prop => prop.triggersZOrder, () => {
|
||||
this._private.cy.notify('zorder', ele);
|
||||
});
|
||||
};
|
||||
|
||||
styfn.checkBoundsTrigger = function( ele, name, fromValue, toValue ){
|
||||
this.checkTrigger( ele, name, fromValue, toValue, prop => prop.triggersBounds, prop => {
|
||||
ele.dirtyCompoundBoundsCache();
|
||||
ele.dirtyBoundingBoxCache();
|
||||
});
|
||||
};
|
||||
|
||||
styfn.checkConnectedEdgesBoundsTrigger = function( ele, name, fromValue, toValue ){
|
||||
this.checkTrigger( ele, name, fromValue, toValue, prop => prop.triggersBoundsOfConnectedEdges, prop => {
|
||||
ele.connectedEdges().forEach(edge => {
|
||||
edge.dirtyBoundingBoxCache();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
styfn.checkParallelEdgesBoundsTrigger = function( ele, name, fromValue, toValue ){
|
||||
this.checkTrigger( ele, name, fromValue, toValue, prop => prop.triggersBoundsOfParallelEdges, prop => {
|
||||
ele.parallelEdges().forEach(pllEdge => {
|
||||
pllEdge.dirtyBoundingBoxCache();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
styfn.checkTriggers = function( ele, name, fromValue, toValue ){
|
||||
ele.dirtyStyleCache();
|
||||
|
||||
this.checkZOrderTrigger( ele, name, fromValue, toValue );
|
||||
this.checkBoundsTrigger( ele, name, fromValue, toValue );
|
||||
this.checkConnectedEdgesBoundsTrigger( ele, name, fromValue, toValue );
|
||||
this.checkParallelEdgesBoundsTrigger( ele, name, fromValue, toValue );
|
||||
};
|
||||
|
||||
export default styfn;
|
||||
204
frontend/node_modules/cytoscape/src/style/index.mjs
generated
vendored
Normal file
204
frontend/node_modules/cytoscape/src/style/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
import Selector from '../selector/index.mjs';
|
||||
|
||||
import apply from './apply.mjs';
|
||||
import bypass from './bypass.mjs';
|
||||
import container from './container.mjs';
|
||||
import getForEle from './get-for-ele.mjs';
|
||||
import json from './json.mjs';
|
||||
import stringSheet from './string-sheet.mjs';
|
||||
import properties from './properties.mjs';
|
||||
import parse from './parse.mjs';
|
||||
|
||||
let 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: {}
|
||||
};
|
||||
|
||||
this.length = 0;
|
||||
|
||||
this.resetToDefault();
|
||||
};
|
||||
|
||||
let styfn = Style.prototype;
|
||||
|
||||
styfn.instanceString = function(){
|
||||
return 'style';
|
||||
};
|
||||
|
||||
// remove all contexts
|
||||
styfn.clear = function(){
|
||||
let _p = this._private;
|
||||
let cy = _p.cy;
|
||||
let eles = cy.elements();
|
||||
|
||||
for( let i = 0; i < this.length; i++ ){
|
||||
this[ i ] = undefined;
|
||||
}
|
||||
this.length = 0;
|
||||
|
||||
_p.contextStyles = {};
|
||||
_p.propDiffs = {};
|
||||
|
||||
this.cleanElements( eles, true );
|
||||
|
||||
eles.forEach(ele => {
|
||||
let ele_p = ele[0]._private;
|
||||
|
||||
ele_p.styleDirty = true;
|
||||
ele_p.appliedInitStyle = false;
|
||||
});
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
styfn.resetToDefault = function(){
|
||||
this.clear();
|
||||
this.addDefaultStylesheet();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// builds a style object for the 'core' selector
|
||||
styfn.core = function( propName ){
|
||||
return this._private.coreStyle[ propName ] || this.getDefaultProperty( propName );
|
||||
};
|
||||
|
||||
// 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
|
||||
let selector = selectorStr === 'core' ? null : new Selector( selectorStr );
|
||||
|
||||
let 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(){
|
||||
let self = this;
|
||||
let args = arguments;
|
||||
|
||||
if( args.length === 1 ){
|
||||
let map = args[0];
|
||||
|
||||
for( let i = 0; i < self.properties.length; i++ ){
|
||||
let prop = self.properties[ i ];
|
||||
let mapVal = map[ prop.name ];
|
||||
|
||||
if( mapVal === undefined ){
|
||||
mapVal = map[ util.dash2camel( prop.name ) ];
|
||||
}
|
||||
|
||||
if( mapVal !== undefined ){
|
||||
this.cssRule( prop.name, mapVal );
|
||||
}
|
||||
}
|
||||
|
||||
} else if( args.length === 2 ){
|
||||
this.cssRule( args[0], args[1] );
|
||||
}
|
||||
|
||||
// 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
|
||||
let property = this.parse( name, value );
|
||||
|
||||
// add property to current context if valid
|
||||
if( property ){
|
||||
let 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.name.match( /stripe-(\d+)-background-size/ ) && property.value ){
|
||||
this._private.hasStripe = true;
|
||||
}
|
||||
|
||||
if( property.mapped ){
|
||||
this[ i ].mappedProperties.push( property );
|
||||
}
|
||||
|
||||
// add to core style if necessary
|
||||
let currentSelectorIsCore = !this[ i ].selector;
|
||||
if( currentSelectorIsCore ){
|
||||
this._private.coreStyle[ property.name ] = property;
|
||||
}
|
||||
}
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
styfn.append = function( style ){
|
||||
if( is.stylesheet( style ) ){
|
||||
style.appendToStyle( this );
|
||||
} else if( is.array( style ) ){
|
||||
this.appendFromJson( style );
|
||||
} else if( is.string( style ) ){
|
||||
this.appendFromString( style );
|
||||
} // you probably wouldn't want to append a Style, since you'd duplicate the default parts
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// static function
|
||||
Style.fromJson = function( cy, json ){
|
||||
let style = new Style( cy );
|
||||
|
||||
style.fromJson( json );
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
Style.fromString = function( cy, string ){
|
||||
return new Style( cy ).fromString( string );
|
||||
};
|
||||
|
||||
[
|
||||
apply,
|
||||
bypass,
|
||||
container,
|
||||
getForEle,
|
||||
json,
|
||||
stringSheet,
|
||||
properties,
|
||||
parse
|
||||
].forEach( function( props ){
|
||||
util.extend( styfn, props );
|
||||
} );
|
||||
|
||||
|
||||
Style.types = styfn.types;
|
||||
Style.properties = styfn.properties;
|
||||
Style.propertyGroups = styfn.propertyGroups;
|
||||
Style.propertyGroupNames = styfn.propertyGroupNames;
|
||||
Style.propertyGroupKeys = styfn.propertyGroupKeys;
|
||||
|
||||
export default Style;
|
||||
59
frontend/node_modules/cytoscape/src/style/json.mjs
generated
vendored
Normal file
59
frontend/node_modules/cytoscape/src/style/json.mjs
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
let styfn = {};
|
||||
|
||||
styfn.appendFromJson = function( json ){
|
||||
let style = this;
|
||||
|
||||
for( let i = 0; i < json.length; i++ ){
|
||||
let context = json[ i ];
|
||||
let selector = context.selector;
|
||||
let props = context.style || context.css;
|
||||
let names = Object.keys( props );
|
||||
|
||||
style.selector( selector ); // apply selector
|
||||
|
||||
for( let j = 0; j < names.length; j++ ){
|
||||
let name = names[j];
|
||||
let value = props[ name ];
|
||||
|
||||
style.css( name, value ); // apply property
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
// accessible cy.style() function
|
||||
styfn.fromJson = function( json ){
|
||||
let style = this;
|
||||
|
||||
style.resetToDefault();
|
||||
style.appendFromJson( json );
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
// get json from cy.style() api
|
||||
styfn.json = function(){
|
||||
let json = [];
|
||||
|
||||
for( let i = this.defaultLength; i < this.length; i++ ){
|
||||
let cxt = this[ i ];
|
||||
let selector = cxt.selector;
|
||||
let props = cxt.properties;
|
||||
let css = {};
|
||||
|
||||
for( let j = 0; j < props.length; j++ ){
|
||||
let prop = props[ j ];
|
||||
css[ prop.name ] = prop.strValue;
|
||||
}
|
||||
|
||||
json.push( {
|
||||
selector: !selector ? 'core' : selector.toString(),
|
||||
style: css
|
||||
} );
|
||||
}
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
export default styfn;
|
||||
435
frontend/node_modules/cytoscape/src/style/parse.mjs
generated
vendored
Normal file
435
frontend/node_modules/cytoscape/src/style/parse.mjs
generated
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import * as math from '../math.mjs';
|
||||
|
||||
let styfn = {};
|
||||
|
||||
// a caching layer for property parsing
|
||||
styfn.parse = function( name, value, propIsBypass, propIsFlat ){
|
||||
let self = this;
|
||||
|
||||
// function values can't be cached in all cases, and there isn't much benefit of caching them anyway
|
||||
if( is.fn( value ) ){
|
||||
return self.parseImplWarn( name, value, propIsBypass, propIsFlat );
|
||||
}
|
||||
|
||||
let flatKey = ( propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ) ? 'dontcare' : propIsFlat;
|
||||
let bypassKey = propIsBypass ? 't' : 'f';
|
||||
let valueKey = '' + value;
|
||||
let argHash = util.hashStrings( name, valueKey, bypassKey, flatKey );
|
||||
let propCache = self.propCache = self.propCache || [];
|
||||
let ret;
|
||||
|
||||
if( !(ret = propCache[ argHash ]) ){
|
||||
ret = propCache[ argHash ] = self.parseImplWarn( name, value, propIsBypass, propIsFlat );
|
||||
}
|
||||
|
||||
// - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
|
||||
// - mappings can't be shared b/c mappings are per-element
|
||||
if( propIsBypass || propIsFlat === 'mapping' ){
|
||||
// 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;
|
||||
};
|
||||
|
||||
styfn.parseImplWarn = function( name, value, propIsBypass, propIsFlat ){
|
||||
let prop = this.parseImpl( name, value, propIsBypass, propIsFlat );
|
||||
|
||||
if( !prop && value != null ){
|
||||
util.warn(`The style property \`${name}: ${value}\` is invalid`);
|
||||
}
|
||||
|
||||
if( prop && (prop.name === 'width' || prop.name === 'height') && value === 'label' ){
|
||||
util.warn('The style value of `label` is deprecated for `' + prop.name + '`');
|
||||
}
|
||||
|
||||
return prop;
|
||||
};
|
||||
|
||||
// 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
|
||||
styfn.parseImpl = function( name, value, propIsBypass, propIsFlat ){
|
||||
let self = this;
|
||||
|
||||
name = util.camel2dash( name ); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
|
||||
|
||||
let property = self.properties[ name ];
|
||||
let passedValue = value;
|
||||
let types = self.types;
|
||||
|
||||
if( !property ){ return null; } // return null on property of unknown name
|
||||
if( value === undefined ){ return null; } // can't assign undefined
|
||||
|
||||
// the property may be an alias
|
||||
if( property.alias ){
|
||||
property = property.pointsTo;
|
||||
name = property.name;
|
||||
}
|
||||
|
||||
let valueIsString = is.string( value );
|
||||
if( valueIsString ){ // trim the value to make parsing easier
|
||||
value = value.trim();
|
||||
}
|
||||
|
||||
let 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
|
||||
let data, mapData;
|
||||
if( !valueIsString || propIsFlat || value.length < 7 || value[1] !== 'a' ){
|
||||
// then don't bother to do the expensive regex checks
|
||||
|
||||
} else if(value.length >= 7 && value[0] === 'd' && ( data = new RegExp( types.data.regex ).exec( value ) )){
|
||||
if( propIsBypass ){ return false; } // mappers not allowed in bypass
|
||||
|
||||
let mapped = types.data;
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: data,
|
||||
strValue: '' + value,
|
||||
mapped: mapped,
|
||||
field: data[1],
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
} else if(value.length >= 10 && value[0] === 'm' && ( mapData = new RegExp( types.mapData.regex ).exec( value ) )){
|
||||
if( propIsBypass ){ return false; } // mappers not allowed in bypass
|
||||
if( type.multiple ){ return false; } // impossible to map to num
|
||||
|
||||
let mapped = types.mapData;
|
||||
|
||||
// we can map only if the type is a colour or a number
|
||||
if( !(type.color || type.number) ){ return false; }
|
||||
|
||||
let valueMin = this.parse( name, mapData[4] ); // parse to validate
|
||||
if( !valueMin || valueMin.mapped ){ return false; } // can't be invalid or mapped
|
||||
|
||||
let 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.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue ){
|
||||
util.warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
|
||||
|
||||
return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
|
||||
|
||||
} else if( type.color ){
|
||||
let c1 = valueMin.value;
|
||||
let c2 = valueMax.value;
|
||||
|
||||
let 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' ){
|
||||
let 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; }
|
||||
|
||||
let valArr = [];
|
||||
let unitsArr = [];
|
||||
let pfValArr = [];
|
||||
let strVal = '';
|
||||
let hasEnum = false;
|
||||
|
||||
for( let i = 0; i < vals.length; i++ ){
|
||||
let p = self.parse( name, vals[i], propIsBypass, 'multiple' );
|
||||
|
||||
hasEnum = hasEnum || is.string( p.value );
|
||||
|
||||
valArr.push( p.value );
|
||||
pfValArr.push( p.pfValue != null ? p.pfValue : p.value );
|
||||
unitsArr.push( p.units );
|
||||
strVal += (i > 0 ? ' ' : '') + p.strValue;
|
||||
}
|
||||
|
||||
if( type.validate && !type.validate( valArr, unitsArr ) ){
|
||||
return null;
|
||||
}
|
||||
|
||||
if( type.singleEnum && hasEnum ){
|
||||
if( valArr.length === 1 && is.string( valArr[0] ) ){
|
||||
return {
|
||||
name: name,
|
||||
value: valArr[0],
|
||||
strValue: valArr[0],
|
||||
bypass: propIsBypass
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: valArr,
|
||||
pfValue: pfValArr,
|
||||
strValue: strVal,
|
||||
bypass: propIsBypass,
|
||||
units: unitsArr
|
||||
};
|
||||
}
|
||||
|
||||
// several types also allow enums
|
||||
let checkEnums = function(){
|
||||
for( let i = 0; i < type.enums.length; i++ ){
|
||||
let 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 ){
|
||||
let units;
|
||||
let 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 ){
|
||||
let unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
|
||||
if( units ){ unitsRegex = units; } // only allow explicit units if so set
|
||||
let 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.strictMin && value === type.min) ) )
|
||||
|| ( type.max !== undefined && ( value > type.max || (type.strictMax && value === type.max) ) )
|
||||
){
|
||||
return null;
|
||||
}
|
||||
|
||||
let 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 : math.deg2rad( value );
|
||||
}
|
||||
|
||||
// normalize value in %
|
||||
if( units === '%' ){
|
||||
ret.pfValue = value / 100;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
} else if( type.propList ){
|
||||
|
||||
let props = [];
|
||||
let propsStr = '' + value;
|
||||
|
||||
if( propsStr === 'none' ){
|
||||
// leave empty
|
||||
|
||||
} else { // go over each prop
|
||||
|
||||
let propsSplit = propsStr.split( /\s*,\s*|\s+/ );
|
||||
for( let i = 0; i < propsSplit.length; i++ ){
|
||||
let propName = propsSplit[ i ].trim();
|
||||
|
||||
if( self.properties[ propName ] ){
|
||||
props.push( propName );
|
||||
} else {
|
||||
util.warn('`' + propName + '` is not a valid property name');
|
||||
}
|
||||
}
|
||||
|
||||
if( props.length === 0 ){ return null; }
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: props,
|
||||
strValue: props.length === 0 ? 'none' : props.join(' '),
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
} else if( type.color ){
|
||||
let tuple = util.color2tuple( value );
|
||||
|
||||
if( !tuple ){ return null; }
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: tuple,
|
||||
pfValue: tuple,
|
||||
strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')', // n.b. no spaces b/c of multiple support
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
} else if( type.regex || type.regexes ){
|
||||
|
||||
// first check enums
|
||||
if( type.enums ){
|
||||
let enumProp = checkEnums();
|
||||
|
||||
if( enumProp ){ return enumProp; }
|
||||
}
|
||||
|
||||
let regexes = type.regexes ? type.regexes : [ type.regex ];
|
||||
|
||||
for( let i = 0; i < regexes.length; i++ ){
|
||||
let regex = new RegExp( regexes[ i ] ); // make a regex from the type string
|
||||
let m = regex.exec( value );
|
||||
|
||||
if( m ){ // regex matches
|
||||
return {
|
||||
name: name,
|
||||
value: type.singleRegexMatchValue ? m[1] : 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
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default styfn;
|
||||
97
frontend/node_modules/cytoscape/src/stylesheet.mjs
generated
vendored
Normal file
97
frontend/node_modules/cytoscape/src/stylesheet.mjs
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
import * as is from './is.mjs';
|
||||
import Style from './style/index.mjs';
|
||||
import { dash2camel } from './util/index.mjs';
|
||||
|
||||
// a dummy stylesheet object that doesn't need a reference to the core
|
||||
// (useful for init)
|
||||
let Stylesheet = function(){
|
||||
if( !(this instanceof Stylesheet) ){
|
||||
return new Stylesheet();
|
||||
}
|
||||
|
||||
this.length = 0;
|
||||
};
|
||||
|
||||
let sheetfn = Stylesheet.prototype;
|
||||
|
||||
sheetfn.instanceString = function(){
|
||||
return 'stylesheet';
|
||||
};
|
||||
|
||||
// just store the selector to be parsed later
|
||||
sheetfn.selector = function( selector ){
|
||||
let i = this.length++;
|
||||
|
||||
this[ i ] = {
|
||||
selector: selector,
|
||||
properties: []
|
||||
};
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
// just store the property to be parsed later
|
||||
sheetfn.css = function( name, value ){
|
||||
let i = this.length - 1;
|
||||
|
||||
if( is.string( name ) ){
|
||||
this[ i ].properties.push( {
|
||||
name: name,
|
||||
value: value
|
||||
} );
|
||||
} else if( is.plainObject( name ) ){
|
||||
let map = name;
|
||||
let propNames = Object.keys( map );
|
||||
|
||||
for( let j = 0; j < propNames.length; j++ ){
|
||||
let key = propNames[ j ];
|
||||
let mapVal = map[ key ];
|
||||
|
||||
if( mapVal == null ){ continue; }
|
||||
|
||||
let prop = Style.properties[key] || Style.properties[dash2camel(key)];
|
||||
|
||||
if( prop == null ){ continue; }
|
||||
|
||||
let name = prop.name;
|
||||
let 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 ){
|
||||
let style = new Style( cy );
|
||||
|
||||
return this.appendToStyle( style );
|
||||
};
|
||||
|
||||
// append a dummy stylesheet object on a real style object
|
||||
sheetfn.appendToStyle = function( style ){
|
||||
for( let i = 0; i < this.length; i++ ){
|
||||
let context = this[ i ];
|
||||
let selector = context.selector;
|
||||
let props = context.properties;
|
||||
|
||||
style.selector( selector ); // apply selector
|
||||
|
||||
for( let j = 0; j < props.length; j++ ){
|
||||
let prop = props[ j ];
|
||||
|
||||
style.css( prop.name, prop.value ); // apply property
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
export default Stylesheet;
|
||||
18
frontend/node_modules/cytoscape/src/test.mjs
generated
vendored
Normal file
18
frontend/node_modules/cytoscape/src/test.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
This file tells the Mocha tests what build of Cytoscape to use.
|
||||
*/
|
||||
|
||||
// For manual build tests, use the ESM build
|
||||
// NB : Must do `npm run build` before `npm test`
|
||||
let cytoscape;
|
||||
|
||||
if (process.env.TEST_BUILD) {
|
||||
// Dynamically import the ESM build
|
||||
cytoscape = await import('../build/cytoscape.esm.mjs'); // Assuming the ESM build uses `.esm.js`
|
||||
} else {
|
||||
// Dynamically import the unbundled, unbabelified raw source
|
||||
cytoscape = await import('./index.mjs');
|
||||
}
|
||||
|
||||
// Export the module (adjust based on whether you need default or named exports)
|
||||
export default cytoscape.default || cytoscape;
|
||||
290
frontend/node_modules/cytoscape/src/util/colors.mjs
generated
vendored
Normal file
290
frontend/node_modules/cytoscape/src/util/colors.mjs
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as regex from './regex.mjs';
|
||||
|
||||
// get [r, g, b] from #abc or #aabbcc
|
||||
export const hex2tuple = hex => {
|
||||
if( !(hex.length === 4 || hex.length === 7) || hex[0] !== '#' ){ return; }
|
||||
|
||||
let shortHex = hex.length === 4;
|
||||
let r, g, b;
|
||||
let 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)
|
||||
export const hsl2tuple = hsl => {
|
||||
let ret;
|
||||
let 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;
|
||||
}
|
||||
|
||||
let m = new RegExp( '^' + 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 {
|
||||
let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
let 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)
|
||||
export const rgb2tuple = rgb => {
|
||||
let ret;
|
||||
|
||||
let m = new RegExp( '^' + regex.rgba + '$' ).exec( rgb );
|
||||
if( m ){
|
||||
ret = [];
|
||||
|
||||
let isPct = [];
|
||||
for( let i = 1; i <= 3; i++ ){
|
||||
let 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 ) );
|
||||
}
|
||||
|
||||
let atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
|
||||
let allArePct = isPct[1] && isPct[2] && isPct[3];
|
||||
if( atLeastOneIsPct && !allArePct ){ return; } // must all be percent values if one is
|
||||
|
||||
let alpha = m[4];
|
||||
if( alpha !== undefined ){
|
||||
alpha = parseFloat( alpha );
|
||||
|
||||
if( alpha < 0 || alpha > 1 ){ return; } // invalid alpha value
|
||||
|
||||
ret.push( alpha );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
export const colorname2tuple = color => {
|
||||
return colors[ color.toLowerCase() ];
|
||||
};
|
||||
|
||||
export const color2tuple = color => {
|
||||
return ( is.array( color ) ? color : null )
|
||||
|| colorname2tuple( color )
|
||||
|| hex2tuple( color )
|
||||
|| rgb2tuple( color )
|
||||
|| hsl2tuple( color );
|
||||
};
|
||||
|
||||
export const 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 ]
|
||||
};
|
||||
Reference in New Issue
Block a user