|
@ -6810,6 +6810,9 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
/** |
|
|
/** |
|
|
* Parse a text source containing data in DOT language into a JSON object. |
|
|
* Parse a text source containing data in DOT language into a JSON object. |
|
|
* The object contains two lists: one with nodes and one with edges. |
|
|
* The object contains two lists: one with nodes and one with edges. |
|
|
|
|
|
* |
|
|
|
|
|
* DOT language reference: http://www.graphviz.org/doc/info/lang.html
|
|
|
|
|
|
* |
|
|
* @param {String} data Text containing a graph in DOT-notation |
|
|
* @param {String} data Text containing a graph in DOT-notation |
|
|
* @return {Object} graph An object containing two parameters: |
|
|
* @return {Object} graph An object containing two parameters: |
|
|
* {Object[]} nodes |
|
|
* {Object[]} nodes |
|
@ -6848,10 +6851,6 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
var token = ''; // current token
|
|
|
var token = ''; // current token
|
|
|
var tokenType = TOKENTYPE.NULL; // type of the token
|
|
|
var tokenType = TOKENTYPE.NULL; // type of the token
|
|
|
|
|
|
|
|
|
var graph = null; // object with the graph to be build
|
|
|
|
|
|
var nodeAttr = null; // global node attributes
|
|
|
|
|
|
var edgeAttr = null; // global edge attributes
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get the first character from the dot file. |
|
|
* Get the first character from the dot file. |
|
|
* The character is stored into the char c. If the end of the dot file is |
|
|
* The character is stored into the char c. If the end of the dot file is |
|
@ -6943,11 +6942,12 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Add a node to the current graph object. If there is already a node with |
|
|
|
|
|
|
|
|
* Add a node to a graph object. If there is already a node with |
|
|
* the same id, their attributes will be merged. |
|
|
* the same id, their attributes will be merged. |
|
|
|
|
|
* @param {Object} graph |
|
|
* @param {Object} node |
|
|
* @param {Object} node |
|
|
*/ |
|
|
*/ |
|
|
function addNode(node) { |
|
|
|
|
|
|
|
|
function addNode(graph, node) { |
|
|
var nodes = graph.nodes; |
|
|
var nodes = graph.nodes; |
|
|
if (!nodes) { |
|
|
if (!nodes) { |
|
|
nodes = []; |
|
|
nodes = []; |
|
@ -6972,24 +6972,25 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
else { |
|
|
else { |
|
|
// add
|
|
|
// add
|
|
|
graph.nodes.push(node); |
|
|
graph.nodes.push(node); |
|
|
if (nodeAttr) { |
|
|
|
|
|
var attr = merge({}, nodeAttr); // clone global attributes
|
|
|
|
|
|
|
|
|
if (graph.node) { |
|
|
|
|
|
var attr = merge({}, graph.node); // clone global attributes
|
|
|
node.attr = merge(attr, node.attr); // merge attributes
|
|
|
node.attr = merge(attr, node.attr); // merge attributes
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Add an edge to the current graph object |
|
|
|
|
|
|
|
|
* Add an edge to a graph object |
|
|
|
|
|
* @param {Object} graph |
|
|
* @param {Object} edge |
|
|
* @param {Object} edge |
|
|
*/ |
|
|
*/ |
|
|
function addEdge(edge) { |
|
|
|
|
|
|
|
|
function addEdge(graph, edge) { |
|
|
if (!graph.edges) { |
|
|
if (!graph.edges) { |
|
|
graph.edges = []; |
|
|
graph.edges = []; |
|
|
} |
|
|
} |
|
|
graph.edges.push(edge); |
|
|
graph.edges.push(edge); |
|
|
if (edgeAttr) { |
|
|
|
|
|
var attr = merge({}, edgeAttr); // clone global attributes
|
|
|
|
|
|
|
|
|
if (graph.edge) { |
|
|
|
|
|
var attr = merge({}, graph.edge); // clone global attributes
|
|
|
edge.attr = merge(attr, edge.attr); // merge attributes
|
|
|
edge.attr = merge(attr, edge.attr); // merge attributes
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -7135,9 +7136,7 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
* @returns {Object} graph |
|
|
* @returns {Object} graph |
|
|
*/ |
|
|
*/ |
|
|
function parseGraph() { |
|
|
function parseGraph() { |
|
|
graph = {}; |
|
|
|
|
|
nodeAttr = null; |
|
|
|
|
|
edgeAttr = null; |
|
|
|
|
|
|
|
|
var graph = {}; |
|
|
|
|
|
|
|
|
first(); |
|
|
first(); |
|
|
getToken(); |
|
|
getToken(); |
|
@ -7167,7 +7166,7 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
getToken(); |
|
|
getToken(); |
|
|
|
|
|
|
|
|
// statements
|
|
|
// statements
|
|
|
parseStatements(); |
|
|
|
|
|
|
|
|
parseStatements(graph); |
|
|
|
|
|
|
|
|
// close angle bracket
|
|
|
// close angle bracket
|
|
|
if (token != '}') { |
|
|
if (token != '}') { |
|
@ -7181,19 +7180,24 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
} |
|
|
} |
|
|
getToken(); |
|
|
getToken(); |
|
|
|
|
|
|
|
|
|
|
|
// remove temporary global properties
|
|
|
|
|
|
delete graph.node; |
|
|
|
|
|
delete graph.edge; |
|
|
|
|
|
|
|
|
return graph; |
|
|
return graph; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Parse a list with statements. |
|
|
* Parse a list with statements. |
|
|
|
|
|
* @param {Object} graph |
|
|
*/ |
|
|
*/ |
|
|
function parseStatements () { |
|
|
|
|
|
|
|
|
function parseStatements (graph) { |
|
|
while (token !== '' && token != '}') { |
|
|
while (token !== '' && token != '}') { |
|
|
if (tokenType != TOKENTYPE.IDENTIFIER) { |
|
|
if (tokenType != TOKENTYPE.IDENTIFIER) { |
|
|
throw newSyntaxError('Identifier expected'); |
|
|
throw newSyntaxError('Identifier expected'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
parseStatement(); |
|
|
|
|
|
|
|
|
parseStatement(graph); |
|
|
if (token == ';') { |
|
|
if (token == ';') { |
|
|
getToken(); |
|
|
getToken(); |
|
|
} |
|
|
} |
|
@ -7204,47 +7208,39 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
* Parse a single statement. Can be a an attribute statement, node |
|
|
* Parse a single statement. Can be a an attribute statement, node |
|
|
* statement, a series of node statements and edge statements, or a |
|
|
* statement, a series of node statements and edge statements, or a |
|
|
* parameter. |
|
|
* parameter. |
|
|
|
|
|
* @param {Object} graph |
|
|
*/ |
|
|
*/ |
|
|
function parseStatement() { |
|
|
|
|
|
var attr; |
|
|
|
|
|
|
|
|
function parseStatement(graph) { |
|
|
|
|
|
// TODO: parse subgraph
|
|
|
|
|
|
|
|
|
attr = parseAttributeStatement(); |
|
|
|
|
|
if (!attr) { |
|
|
|
|
|
var id = token; // can be a string or a number
|
|
|
|
|
|
getToken(); |
|
|
|
|
|
|
|
|
// parse an attribute statement
|
|
|
|
|
|
var attr = parseAttributeStatement(graph); |
|
|
|
|
|
if (attr) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (token == '=') { |
|
|
|
|
|
// id statement
|
|
|
|
|
|
getToken(); |
|
|
|
|
|
if (!graph.attr) { |
|
|
|
|
|
graph.attr = {}; |
|
|
|
|
|
} |
|
|
|
|
|
graph.attr[id] = token; |
|
|
|
|
|
getToken(); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
// node statement
|
|
|
|
|
|
var node = { |
|
|
|
|
|
id: id |
|
|
|
|
|
}; |
|
|
|
|
|
attr = parseAttributes(); |
|
|
|
|
|
if (attr) { |
|
|
|
|
|
node.attr = attr; |
|
|
|
|
|
} |
|
|
|
|
|
addNode(node); |
|
|
|
|
|
|
|
|
// parse node
|
|
|
|
|
|
var id = token; // id can be a string or a number
|
|
|
|
|
|
getToken(); |
|
|
|
|
|
|
|
|
// edge statements
|
|
|
|
|
|
parseEdge(id); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (token == '=') { |
|
|
|
|
|
// id statement
|
|
|
|
|
|
getToken(); |
|
|
|
|
|
graph[id] = token; |
|
|
|
|
|
getToken(); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
parseNodeStatement(graph, id); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* parse an attribute statement like "node [shape=circle fontSize=16]". |
|
|
* parse an attribute statement like "node [shape=circle fontSize=16]". |
|
|
* Available keywords are 'node', 'edge', 'graph' |
|
|
* Available keywords are 'node', 'edge', 'graph' |
|
|
|
|
|
* @param {Object} graph |
|
|
* @returns {Object | null} attr |
|
|
* @returns {Object | null} attr |
|
|
*/ |
|
|
*/ |
|
|
function parseAttributeStatement () { |
|
|
|
|
|
|
|
|
function parseAttributeStatement (graph) { |
|
|
var attr = null; |
|
|
var attr = null; |
|
|
|
|
|
|
|
|
// attribute statements
|
|
|
// attribute statements
|
|
@ -7252,25 +7248,25 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
getToken(); |
|
|
getToken(); |
|
|
|
|
|
|
|
|
// node attributes
|
|
|
// node attributes
|
|
|
attr = parseAttributes(); |
|
|
|
|
|
|
|
|
attr = parseAttributeList(); |
|
|
if (attr) { |
|
|
if (attr) { |
|
|
nodeAttr = merge(nodeAttr, attr); |
|
|
|
|
|
|
|
|
graph.node = merge(graph.node, attr); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else if (token == 'edge') { |
|
|
else if (token == 'edge') { |
|
|
getToken(); |
|
|
getToken(); |
|
|
|
|
|
|
|
|
// edge attributes
|
|
|
// edge attributes
|
|
|
attr = parseAttributes(); |
|
|
|
|
|
|
|
|
attr = parseAttributeList(); |
|
|
if (attr) { |
|
|
if (attr) { |
|
|
edgeAttr = merge(edgeAttr, attr); |
|
|
|
|
|
|
|
|
graph.edge = merge(graph.edge, attr); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else if (token == 'graph') { |
|
|
else if (token == 'graph') { |
|
|
getToken(); |
|
|
getToken(); |
|
|
|
|
|
|
|
|
// graph attributes
|
|
|
// graph attributes
|
|
|
attr = parseAttributes(); |
|
|
|
|
|
|
|
|
attr = parseAttributeList(); |
|
|
if (attr) { |
|
|
if (attr) { |
|
|
graph.attr = merge(graph.attr, attr); |
|
|
graph.attr = merge(graph.attr, attr); |
|
|
} |
|
|
} |
|
@ -7279,28 +7275,49 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
return attr; |
|
|
return attr; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* parse a node statement |
|
|
|
|
|
* @param {Object} graph |
|
|
|
|
|
* @param {String | Number} id |
|
|
|
|
|
*/ |
|
|
|
|
|
function parseNodeStatement(graph, id) { |
|
|
|
|
|
// node statement
|
|
|
|
|
|
var node = { |
|
|
|
|
|
id: id |
|
|
|
|
|
}; |
|
|
|
|
|
var attr = parseAttributeList(); |
|
|
|
|
|
if (attr) { |
|
|
|
|
|
node.attr = attr; |
|
|
|
|
|
} |
|
|
|
|
|
addNode(graph, node); |
|
|
|
|
|
|
|
|
|
|
|
// edge statements
|
|
|
|
|
|
parseEdge(graph, id); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Parse an edge or a series of edges |
|
|
* Parse an edge or a series of edges |
|
|
|
|
|
* @param {Object} graph |
|
|
* @param {String | Number} from Id of the from node |
|
|
* @param {String | Number} from Id of the from node |
|
|
*/ |
|
|
*/ |
|
|
function parseEdge(from) { |
|
|
|
|
|
|
|
|
function parseEdge(graph, from) { |
|
|
while (token == '->' || token == '--') { |
|
|
while (token == '->' || token == '--') { |
|
|
var type = token; |
|
|
var type = token; |
|
|
getToken(); |
|
|
getToken(); |
|
|
|
|
|
|
|
|
if (token == '{') { |
|
|
if (token == '{') { |
|
|
// parse a set of nodes, like "node1 -> {node2, node3}"
|
|
|
// parse a set of nodes, like "node1 -> {node2, node3}"
|
|
|
parseEdgeSet(from, type); |
|
|
|
|
|
|
|
|
parseEdgeSet(graph, from, type); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
// parse a single edge, like "node1 -> node2 -> node3"
|
|
|
// parse a single edge, like "node1 -> node2 -> node3"
|
|
|
var to = token; |
|
|
var to = token; |
|
|
addNode({ |
|
|
|
|
|
|
|
|
addNode(graph, { |
|
|
id: to |
|
|
id: to |
|
|
}); |
|
|
}); |
|
|
getToken(); |
|
|
getToken(); |
|
|
var attr = parseAttributes(); |
|
|
|
|
|
|
|
|
var attr = parseAttributeList(); |
|
|
|
|
|
|
|
|
// create edge
|
|
|
// create edge
|
|
|
var edge = { |
|
|
var edge = { |
|
@ -7311,7 +7328,7 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
if (attr) { |
|
|
if (attr) { |
|
|
edge.attr = attr; |
|
|
edge.attr = attr; |
|
|
} |
|
|
} |
|
|
addEdge(edge); |
|
|
|
|
|
|
|
|
addEdge(graph, edge); |
|
|
|
|
|
|
|
|
from = to; |
|
|
from = to; |
|
|
} |
|
|
} |
|
@ -7320,11 +7337,12 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Parse a set of nodes, like "{node1; node2; node3}" |
|
|
* Parse a set of nodes, like "{node1; node2; node3}" |
|
|
|
|
|
* @param {Object} graph |
|
|
* @param {String | Number} from Id of the from node |
|
|
* @param {String | Number} from Id of the from node |
|
|
* @param {String} type Edge type, '--' or '->' |
|
|
* @param {String} type Edge type, '--' or '->' |
|
|
* @return {Node[] | null} nodes |
|
|
* @return {Node[] | null} nodes |
|
|
*/ |
|
|
*/ |
|
|
function parseEdgeSet(from, type) { |
|
|
|
|
|
|
|
|
function parseEdgeSet(graph, from, type) { |
|
|
var nodes = null; |
|
|
var nodes = null; |
|
|
|
|
|
|
|
|
if (token == '{') { |
|
|
if (token == '{') { |
|
@ -7336,7 +7354,7 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
throw newSyntaxError('Identifier expected'); |
|
|
throw newSyntaxError('Identifier expected'); |
|
|
} |
|
|
} |
|
|
var to = token; |
|
|
var to = token; |
|
|
addNode({ |
|
|
|
|
|
|
|
|
addNode(graph, { |
|
|
id: to |
|
|
id: to |
|
|
}); |
|
|
}); |
|
|
getToken(); |
|
|
getToken(); |
|
@ -7347,11 +7365,11 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
to: to, |
|
|
to: to, |
|
|
type: type |
|
|
type: type |
|
|
}; |
|
|
}; |
|
|
var attr = parseAttributes(); |
|
|
|
|
|
|
|
|
var attr = parseAttributeList(); |
|
|
if (attr) { |
|
|
if (attr) { |
|
|
edge.attr = attr; |
|
|
edge.attr = attr; |
|
|
} |
|
|
} |
|
|
addEdge(edge); |
|
|
|
|
|
|
|
|
addEdge(graph, edge); |
|
|
|
|
|
|
|
|
// separator
|
|
|
// separator
|
|
|
if (token == ';') { |
|
|
if (token == ';') { |
|
@ -7372,12 +7390,14 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
/** |
|
|
/** |
|
|
* Parse a set with attributes, |
|
|
* Parse a set with attributes, |
|
|
* for example [label="1.000", shape=solid] |
|
|
* for example [label="1.000", shape=solid] |
|
|
* @return {Object | undefined} attr |
|
|
|
|
|
|
|
|
* @return {Object | null} attr |
|
|
*/ |
|
|
*/ |
|
|
function parseAttributes() { |
|
|
|
|
|
if (token == '[') { |
|
|
|
|
|
|
|
|
function parseAttributeList() { |
|
|
|
|
|
var attr = null; |
|
|
|
|
|
|
|
|
|
|
|
while (token == '[') { |
|
|
getToken(); |
|
|
getToken(); |
|
|
var attr = {}; |
|
|
|
|
|
|
|
|
attr = {}; |
|
|
while (token !== '' && token != ']') { |
|
|
while (token !== '' && token != ']') { |
|
|
if (tokenType != TOKENTYPE.IDENTIFIER) { |
|
|
if (tokenType != TOKENTYPE.IDENTIFIER) { |
|
|
throw newSyntaxError('Attribute name expected'); |
|
|
throw newSyntaxError('Attribute name expected'); |
|
@ -7406,12 +7426,9 @@ Timeline.prototype.getItemRange = function getItemRange() { |
|
|
throw newSyntaxError('Bracket ] expected'); |
|
|
throw newSyntaxError('Bracket ] expected'); |
|
|
} |
|
|
} |
|
|
getToken(); |
|
|
getToken(); |
|
|
|
|
|
|
|
|
return attr; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
return undefined; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return attr; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|