const NODE_LABEL = "name"; const NODE_ID = "id"; const NODE_GROUP = "group"; const NODE_DESCRIPTION = "description"; const NODE_IMAGE = "image"; const LINK_SOURCE = "source"; const LINK_TARGET = "target"; const LINK_TYPE = "type"; const LINK_PARTICLE_COUNT = 4; const GRAPH_NODES = "nodes"; const GRAPH_LINKS = "links"; const IMAGE_SIZE = 12; const IMAGE_SRC = PLUGIN_PATH + "datasets/images/"; const LINK_PARAMS = [LINK_TYPE]; const NODE_PARAMS = [NODE_ID, NODE_LABEL, NODE_IMAGE, NODE_DESCRIPTION]; const JSON_CONFIG = PLUGIN_PATH + "datasets/aud1.json"; const STOP_PHYSICS_DELAY = 5000; // ms const graph = { data: undefined, externUpdate: [], // Register callbacks in this list update() { graph.externUpdate.forEach((fn) => fn()); }, deleteNode(nodeId) { // Delete node from nodes graph.data[GRAPH_NODES] = graph.data[GRAPH_NODES].filter( (n) => n[NODE_ID] !== nodeId ); // Delete links with node graph.data[GRAPH_LINKS] = graph.data[GRAPH_LINKS].filter( (l) => l[LINK_SOURCE][NODE_ID] !== nodeId && l[LINK_TARGET][NODE_ID] !== nodeId ); }, stopPhysics() { graph.data[GRAPH_NODES].forEach((n) => { n.fx = n.x; n.fy = n.y; }); }, addIdentifiers() { graph.data[GRAPH_NODES].forEach((n) => { n.node = true; n.link = false; }); graph.data[GRAPH_LINKS].forEach((l) => { l.node = false; l.link = true; }); }, deleteLink(sourceId, targetId) { // Only keep links, of one of the nodes is different graph.data[GRAPH_LINKS] = graph.data[GRAPH_LINKS].filter( (l) => l[LINK_SOURCE][NODE_ID] !== sourceId || l[LINK_TARGET][NODE_ID] !== targetId ); }, isLinkOnNode(link, node) { if (link === undefined || node === undefined) { return false; } if (link.link !== true || node.node !== true) { return false; } return ( link[LINK_SOURCE][NODE_ID] === node[NODE_ID] || link[LINK_TARGET][NODE_ID] === node[NODE_ID] ); }, existsLink(sourceId, targetId) { const links = graph.data[GRAPH_LINKS]; for (var i = 0; i < links.length; i++) { var link = links[i]; if ( link[LINK_SOURCE][NODE_ID] === sourceId && link[LINK_TARGET][NODE_ID] === targetId ) { return true; } } return false; }, connectNodes(sourceId, targetIds) { targetIds.forEach((targetId) => { if ( graph.existsLink(sourceId, targetId) || graph.existsLink(targetId, sourceId) ) { return; } var link = {}; link[LINK_SOURCE] = sourceId; link[LINK_TARGET] = targetId; graph.data[GRAPH_LINKS].push(link); }); }, getCleanData() { var cleanData = {}; cleanData[GRAPH_LINKS] = []; cleanData[GRAPH_NODES] = []; graph.data[GRAPH_LINKS].forEach((link) => cleanData[GRAPH_LINKS].push(graph.getCleanLink(link)) ); graph.data[GRAPH_NODES].forEach((node) => cleanData[GRAPH_NODES].push(graph.getCleanNode(node)) ); console.log(cleanData); return cleanData; }, getCleanNode(node) { var cleanNode = {}; NODE_PARAMS.forEach((param) => { cleanNode[param] = node[param]; }); return cleanNode; }, getCleanLink(link) { var cleanLink = {}; // Source and target nodes // Node ids will be converted to complete node objects on running graphs, gotta convert back cleanLink[LINK_SOURCE] = link[LINK_SOURCE][NODE_ID]; cleanLink[LINK_TARGET] = link[LINK_TARGET][NODE_ID]; // Other parameters LINK_PARAMS.forEach((param) => { cleanLink[param] = link[param]; }); return cleanLink; }, existsNodeId(nodeId) { var nodes = graph.data[GRAPH_NODES]; for (var i = 0; i < nodes.length; i++) { if (nodes[i][NODE_ID] === nodeId) { return true; } } return false; }, getUnusedNodeId() { var id; do { id = graph.getRandomString(); } while (graph.existsNodeId(id)); return id; }, getRandomString(length = 8) { // Based on: https://stackoverflow.com/a/1349426/7376120 var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var charactersLength = characters.length; var result = ""; for (var i = 0; i < length; i++) { result += characters.charAt( Math.floor(Math.random() * charactersLength) ); } return result; }, addLink(sourceId, targetId, linkDetails = {}) { // Copy params var newLink = linkDetails; // Make sure the IDs exist if ( sourceId === undefined || targetId === undefined || graph.existsNodeId(sourceId) === false || graph.existsNodeId(targetId) === false ) { return; } // Make sure the link is unique if (graph.existsLink(sourceId, targetId)) { return; } newLink[LINK_SOURCE] = sourceId; newLink[LINK_TARGET] = targetId; // Basic node properties newLink.link = true; newLink.node = false; // Add node graph.data[GRAPH_LINKS].push(newLink); graph.update(); return newLink; }, addNode(nodeDetails) { // Copy params var newNode = nodeDetails; // Make sure the ID is set and unique if (newNode[NODE_ID] === undefined) { newNode[NODE_ID] = graph.getUnusedNodeId(); } else if (graph.existsNodeId(newNode[NODE_ID])) { return; } // Basic node properties newNode.node = true; newNode.link = false; // Add node graph.data[GRAPH_NODES].push(newNode); graph.update(); return newNode; }, };