dotgraph.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. // Generated by CoffeeScript 1.4.0
  2. /*
  3. # Objects for dealing with the Graphviz dot/xdot format.
  4. # After obtaining an ast using DotParser.parser(source),
  5. # you may find the following useful:
  6. #
  7. # astToStr: Turn an ast back into a string
  8. #
  9. # new DotGraph(ast): Get a dotgraph object. Calling .walk on this
  10. # object will walk the ast and populate the .notes, .edges, and .graphs
  11. # properties.
  12. #
  13. # new XDotGraph(ast): Subclass of DotGraph. Calling .walk will populate
  14. # .nodes, .edges, and .graphs and will parse any of the known attributes
  15. # to javascript objects and convert their values to pixels if necessary.
  16. */
  17. var DotGraph, XDotGraph, astToStr,
  18. __hasProp = {}.hasOwnProperty,
  19. __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
  20. astToStr = function(ast, indentLevel, indentChar) {
  21. var attrListToStr, attrs, escape, indentStr, n, ret, _ref;
  22. if (indentLevel == null) {
  23. indentLevel = 0;
  24. }
  25. if (indentChar == null) {
  26. indentChar = '\t';
  27. }
  28. escape = function(s) {
  29. if (!(s != null)) {
  30. return "\"\"";
  31. }
  32. if (/^[a-zA-Z0-9]+$/.test(s) && !/^(graph|digraph|subgraph|node|edge|strict)$/.test(s)) {
  33. return s;
  34. } else {
  35. return "\"" + (('' + s).replace('"', '\\"')) + "\"";
  36. }
  37. };
  38. attrListToStr = function(l) {
  39. var attrStrings, e, s, _i, _len, _ref;
  40. if (!l || l.length === 0) {
  41. return "";
  42. }
  43. attrStrings = [];
  44. for (_i = 0, _len = l.length; _i < _len; _i++) {
  45. e = l[_i];
  46. s = e.id + "=";
  47. if ((_ref = e.eq) != null ? _ref.html : void 0) {
  48. s += "<" + e.eq.value + ">";
  49. } else {
  50. s += escape(e.eq);
  51. }
  52. attrStrings.push(s);
  53. }
  54. return "[" + (attrStrings.join(", ")) + "]";
  55. };
  56. ret = '';
  57. indentStr = new Array(indentLevel + 1).join(indentChar);
  58. if (ast instanceof Array) {
  59. ret = ((function() {
  60. var _i, _len, _results;
  61. _results = [];
  62. for (_i = 0, _len = ast.length; _i < _len; _i++) {
  63. n = ast[_i];
  64. _results.push(astToStr(n, indentLevel));
  65. }
  66. return _results;
  67. })()).join('\n');
  68. }
  69. switch (ast.type) {
  70. case 'digraph':
  71. case 'graph':
  72. case 'subgraph':
  73. if (ast.strict) {
  74. ret += indentStr + " strict " + ast.type;
  75. } else {
  76. ret += indentStr + ast.type;
  77. }
  78. if (ast.id) {
  79. ret += " " + ast.id;
  80. }
  81. ret += " {";
  82. if (ast.children.length === 0) {
  83. ret += " }\n";
  84. } else {
  85. ret += "\n";
  86. ret += astToStr(ast.children, indentLevel + 1);
  87. ret += "\n" + indentStr + "}";
  88. }
  89. break;
  90. case 'attr_stmt':
  91. ret += indentStr + ast.target;
  92. attrs = attrListToStr(ast.attr_list);
  93. if (attrs) {
  94. ret += " " + attrs;
  95. }
  96. break;
  97. case 'node_stmt':
  98. ret += indentStr + escape(ast.node_id.id);
  99. if (ast.node_id.port) {
  100. ret += ":" + (escape(ast.node_id.port.id));
  101. }
  102. if ((_ref = ast.node_id.port) != null ? _ref.compass_pt : void 0) {
  103. ret += ":" + ast.node_id.port.compass_pt;
  104. }
  105. attrs = attrListToStr(ast.attr_list);
  106. if (attrs) {
  107. ret += " " + attrs;
  108. }
  109. break;
  110. case 'edge_stmt':
  111. ret += indentStr + ((function() {
  112. var _i, _len, _ref1, _results;
  113. _ref1 = ast.edge_list;
  114. _results = [];
  115. for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
  116. n = _ref1[_i];
  117. _results.push(astToStr(n, 0));
  118. }
  119. return _results;
  120. })()).join(' -> ');
  121. attrs = attrListToStr(ast.attr_list);
  122. if (attrs) {
  123. ret += " " + attrs;
  124. }
  125. break;
  126. case 'node_id':
  127. ret += indentStr + escape(ast.id);
  128. }
  129. return ret;
  130. };
  131. /*
  132. # Takes in an AST of the dot/xdot file format
  133. # and produces a graph object where nodes/edges/subgraphs
  134. # may be queried for attributes
  135. */
  136. DotGraph = (function() {
  137. var DotSubgraph, attrListToObj, copy, doubleCopy, giveRandomKey, mergeLeftNoOverried, mergeLeftOverried;
  138. giveRandomKey = function() {
  139. return Math.random().toFixed(8).slice(2);
  140. };
  141. mergeLeftNoOverried = function(obj1, obj2) {
  142. var k, v;
  143. for (k in obj2) {
  144. v = obj2[k];
  145. if (!(obj1[k] != null)) {
  146. obj1[k] = v;
  147. }
  148. }
  149. return obj1;
  150. };
  151. mergeLeftOverried = function(obj1, obj2) {
  152. var k, v;
  153. for (k in obj2) {
  154. v = obj2[k];
  155. obj1[k] = v;
  156. }
  157. return obj1;
  158. };
  159. copy = function(obj) {
  160. var k, ret, v;
  161. ret = {};
  162. for (k in obj) {
  163. v = obj[k];
  164. ret[k] = v;
  165. }
  166. return ret;
  167. };
  168. doubleCopy = function(obj) {
  169. var k, ret, v;
  170. ret = {};
  171. for (k in obj) {
  172. v = obj[k];
  173. ret[k] = copy(v);
  174. }
  175. return ret;
  176. };
  177. attrListToObj = function(list) {
  178. var attr, ret, _i, _len;
  179. ret = {};
  180. for (_i = 0, _len = list.length; _i < _len; _i++) {
  181. attr = list[_i];
  182. ret[attr.id] = attr.eq;
  183. }
  184. return ret;
  185. };
  186. /*
  187. # Light object to hold nodes and attributes of subgraphs.
  188. # This is really just a container and doesn't have any processing capabilities
  189. */
  190. DotSubgraph = (function() {
  191. function DotSubgraph(id, type, parent) {
  192. this.id = id;
  193. this.type = type != null ? type : 'subgraph';
  194. this.parent = parent != null ? parent : null;
  195. if (!this.id) {
  196. this.id = giveRandomKey();
  197. this.autogeneratedId = true;
  198. }
  199. this.nodes = {};
  200. this.attrs = {};
  201. this.graphs = {};
  202. }
  203. DotSubgraph.prototype.toString = function() {
  204. return this.id;
  205. };
  206. return DotSubgraph;
  207. })();
  208. /****************************************
  209. # Here is where the DotGraph methods start
  210. */
  211. function DotGraph(ast) {
  212. this.ast = ast;
  213. this.nodes = {};
  214. this.edges = {};
  215. this.graphs = {};
  216. this.rootGraph = new DotSubgraph();
  217. }
  218. DotGraph.prototype.walk = function(ast) {
  219. var getAllNodes, walk,
  220. _this = this;
  221. if (ast == null) {
  222. ast = this.ast;
  223. }
  224. walk = function(tree, state, currentParentGraph) {
  225. var attrs, edge, elm, h, heads, id, node, oldParentGraph, t, tails, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2;
  226. if (state == null) {
  227. state = {
  228. node: {},
  229. edge: {},
  230. graph: {}
  231. };
  232. }
  233. if (tree instanceof Array) {
  234. for (_i = 0, _len = tree.length; _i < _len; _i++) {
  235. elm = tree[_i];
  236. walk(elm, state, currentParentGraph);
  237. }
  238. }
  239. switch (tree.type) {
  240. case 'graph':
  241. case 'digraph':
  242. case 'subgraph':
  243. oldParentGraph = currentParentGraph;
  244. currentParentGraph = new DotSubgraph(tree.id || null, tree.type, currentParentGraph);
  245. if (_this.graphs[currentParentGraph] != null) {
  246. currentParentGraph = _this.graphs[currentParentGraph];
  247. }
  248. if (oldParentGraph) {
  249. oldParentGraph.graphs[currentParentGraph] = currentParentGraph;
  250. }
  251. _this.graphs[currentParentGraph] = currentParentGraph;
  252. if ((_ref = tree.type) === 'graph' || _ref === 'digraph') {
  253. _this.rootGraph = currentParentGraph;
  254. _this.rootGraph.strict = tree.strict;
  255. }
  256. state = doubleCopy(state);
  257. walk(tree.children, state, currentParentGraph);
  258. break;
  259. case 'node_stmt':
  260. id = tree.node_id.id;
  261. _this.nodes[id] = _this.nodes[id] || {
  262. attrs: {}
  263. };
  264. mergeLeftOverried(_this.nodes[id].attrs, attrListToObj(tree.attr_list));
  265. mergeLeftNoOverried(_this.nodes[id].attrs, state.node);
  266. currentParentGraph.nodes[id] = true;
  267. break;
  268. case 'attr_stmt':
  269. mergeLeftOverried(state[tree.target], attrListToObj(tree.attr_list));
  270. break;
  271. case 'edge_stmt':
  272. _ref1 = tree.edge_list;
  273. for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
  274. node = _ref1[_j];
  275. if (node.type === 'node_id' && !(_this.nodes[node.id] != null)) {
  276. walk({
  277. type: 'node_stmt',
  278. node_id: node,
  279. attr_list: []
  280. }, state, currentParentGraph);
  281. } else if (node.type === 'subgraph') {
  282. walk(node, state, currentParentGraph);
  283. }
  284. }
  285. heads = getAllNodes(tree.edge_list[0]);
  286. _ref2 = tree.edge_list.slice(1);
  287. for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
  288. node = _ref2[_k];
  289. tails = getAllNodes(node);
  290. for (_l = 0, _len3 = heads.length; _l < _len3; _l++) {
  291. h = heads[_l];
  292. for (_m = 0, _len4 = tails.length; _m < _len4; _m++) {
  293. t = tails[_m];
  294. edge = [h, t];
  295. attrs = mergeLeftNoOverried(attrListToObj(tree.attr_list), state.edge);
  296. _this.edges[edge] = _this.edges[edge] || [];
  297. _this.edges[edge].push({
  298. edge: edge,
  299. attrs: attrs
  300. });
  301. }
  302. }
  303. heads = tails;
  304. }
  305. }
  306. currentParentGraph.attrs = state.graph;
  307. };
  308. getAllNodes = function(tree) {
  309. var n, ret, _i, _len;
  310. ret = [];
  311. if (tree instanceof Array) {
  312. for (_i = 0, _len = tree.length; _i < _len; _i++) {
  313. n = tree[_i];
  314. ret = ret.concat(getAllNodes(n));
  315. }
  316. return ret;
  317. }
  318. switch (tree.type) {
  319. case 'node_id':
  320. ret.push(tree.id);
  321. break;
  322. case 'node_stmt':
  323. ret.push(tree.node_id.id);
  324. break;
  325. case 'edge_stmt':
  326. ret = ret.concat(getAllNodes(tree.edge_list));
  327. break;
  328. case 'graph':
  329. case 'digraph':
  330. case 'subgraph':
  331. ret = ret.concat(getAllNodes(tree.children));
  332. }
  333. return ret;
  334. };
  335. walk(ast);
  336. this.id = this.rootGraph.id;
  337. this.type = this.rootGraph.type;
  338. this.strict = this.rootGraph.strict;
  339. return this;
  340. };
  341. DotGraph.prototype.generateAst = function() {
  342. var e, genAttrsAst, genEdgesAst, genNodeAst, genSubgraphAst, k, root, v, _i, _len, _ref, _ref1;
  343. genAttrsAst = function(attrs) {
  344. var k, ret, v;
  345. if (!attrs || !attrs instanceof Object) {
  346. return null;
  347. }
  348. ret = [];
  349. for (k in attrs) {
  350. v = attrs[k];
  351. ret.push({
  352. type: 'attr',
  353. id: k,
  354. eq: v
  355. });
  356. }
  357. return ret;
  358. };
  359. genEdgesAst = function(edge) {
  360. var attrList, ret;
  361. ret = {
  362. type: 'edge_stmt',
  363. edge_list: [
  364. {
  365. type: 'node_id',
  366. id: edge.edge[0]
  367. }, {
  368. type: 'node_id',
  369. id: edge.edge[1]
  370. }
  371. ]
  372. };
  373. attrList = genAttrsAst(edge.attrs);
  374. if (attrList) {
  375. ret.attr_list = attrList;
  376. }
  377. return ret;
  378. };
  379. genNodeAst = function(id, attrs, html) {
  380. var attrList, ret;
  381. ret = {
  382. type: 'node_stmt',
  383. node_id: {
  384. type: 'node_id',
  385. id: id
  386. }
  387. };
  388. attrList = genAttrsAst(attrs.attrs);
  389. if (attrList) {
  390. ret.attr_list = attrList;
  391. }
  392. return ret;
  393. };
  394. genSubgraphAst = function(graph) {
  395. var k, ret, v, _ref, _ref1, _ref2;
  396. ret = {
  397. type: graph.type,
  398. id: graph.autogeneratedId ? null : graph.id,
  399. children: []
  400. };
  401. _ref = graph.graphs;
  402. for (k in _ref) {
  403. v = _ref[k];
  404. ret.children.push(genSubgraphAst(v));
  405. }
  406. _ref1 = graph.nodes;
  407. for (k in _ref1) {
  408. v = _ref1[k];
  409. ret.children.push(genNodeAst(k, v));
  410. }
  411. _ref2 = graph.edges;
  412. for (k in _ref2) {
  413. v = _ref2[k];
  414. ret.children.push(genEdgesAst(v));
  415. }
  416. if (Object.keys(graph.attrs).length > 0) {
  417. ret.children.push({
  418. type: 'attr_stmt',
  419. target: 'graph',
  420. attr_list: genAttrsAst(graph.attrs)
  421. });
  422. }
  423. return ret;
  424. };
  425. root = genSubgraphAst(this.rootGraph);
  426. if (this.strict) {
  427. root.strict = this.strict;
  428. }
  429. root.children = root.children || [];
  430. _ref = this.nodes;
  431. for (k in _ref) {
  432. v = _ref[k];
  433. root.children.push(genNodeAst(k, v));
  434. }
  435. _ref1 = this.edges;
  436. for (k in _ref1) {
  437. v = _ref1[k];
  438. for (_i = 0, _len = v.length; _i < _len; _i++) {
  439. e = v[_i];
  440. root.children.push(genEdgesAst(e));
  441. }
  442. }
  443. return root;
  444. };
  445. return DotGraph;
  446. })();
  447. /*
  448. # Extension of the DotGraph object that will parse node/edge/graph
  449. # attributes like pos, width, height, etc. into the appropriate javascript types.
  450. #
  451. # All attributes are normalized to pixels for easier drawing.
  452. */
  453. XDotGraph = (function(_super) {
  454. var Edge, toFloatList;
  455. __extends(XDotGraph, _super);
  456. function XDotGraph() {
  457. return XDotGraph.__super__.constructor.apply(this, arguments);
  458. }
  459. toFloatList = function(list) {
  460. var v;
  461. if (typeof list === 'string') {
  462. list = list.split(/[, ]/);
  463. }
  464. return (function() {
  465. var _i, _len, _results;
  466. _results = [];
  467. for (_i = 0, _len = list.length; _i < _len; _i++) {
  468. v = list[_i];
  469. _results.push(parseFloat(v));
  470. }
  471. return _results;
  472. })();
  473. };
  474. Edge = (function() {
  475. function Edge(val) {
  476. var controlPoints, i;
  477. val = toFloatList(val);
  478. controlPoints = [];
  479. i = 3;
  480. while (i + 6 < val.length) {
  481. controlPoints.push(val.slice(i, i + 6));
  482. i += 6;
  483. }
  484. this.type = 'edge';
  485. this.origin = val.slice(1, 3);
  486. this.controlPoints = controlPoints;
  487. this.arrow = val.slice(-4);
  488. }
  489. Edge.prototype.toString = function() {
  490. var i, l, points, _i, _len, _ref;
  491. points = [this.origin[0], this.origin[1]];
  492. _ref = this.controlPoints;
  493. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  494. l = _ref[_i];
  495. points = points.concat(l);
  496. }
  497. points = points.concat(this.arrow.slice(-2));
  498. return "e," + (((function() {
  499. var _j, _ref1, _results;
  500. _results = [];
  501. for (i = _j = 0, _ref1 = points.length; _j < _ref1; i = _j += 2) {
  502. _results.push(points[i] + ',' + points[i + 1]);
  503. }
  504. return _results;
  505. })()).join(' '));
  506. };
  507. return Edge;
  508. })();
  509. XDotGraph.prototype.dpi = 36;
  510. XDotGraph.prototype.walk = function() {
  511. var processAttrs,
  512. _this = this;
  513. XDotGraph.__super__.walk.call(this);
  514. processAttrs = function(graph) {
  515. var attr, e, edge, g, h, n, val, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
  516. if (!(graph != null)) {
  517. return;
  518. }
  519. _ref = (graph != null ? graph.nodes : void 0) || {};
  520. for (h in _ref) {
  521. n = _ref[h];
  522. _ref1 = (n != null ? n.attrs : void 0) || {};
  523. for (attr in _ref1) {
  524. val = _ref1[attr];
  525. n.attrs[attr] = _this.parseAttr(attr, val);
  526. }
  527. }
  528. _ref2 = (graph != null ? graph.edges : void 0) || {};
  529. for (h in _ref2) {
  530. e = _ref2[h];
  531. for (_i = 0, _len = e.length; _i < _len; _i++) {
  532. edge = e[_i];
  533. _ref3 = (edge != null ? edge.attrs : void 0) || {};
  534. for (attr in _ref3) {
  535. val = _ref3[attr];
  536. edge.attrs[attr] = _this.parseAttr(attr, val);
  537. }
  538. }
  539. }
  540. _ref4 = (graph != null ? graph.attrs : void 0) || {};
  541. for (attr in _ref4) {
  542. val = _ref4[attr];
  543. graph.attrs[attr] = _this.parseAttr(attr, val);
  544. }
  545. _ref5 = (graph != null ? graph.graphs : void 0) || {};
  546. for (h in _ref5) {
  547. g = _ref5[h];
  548. processAttrs(g);
  549. }
  550. };
  551. return processAttrs(this);
  552. };
  553. XDotGraph.prototype.parseAttr = function(attr, val) {
  554. if (!val) {
  555. return null;
  556. }
  557. switch (attr) {
  558. case 'width':
  559. case 'height':
  560. return parseFloat(val) * this.dpi;
  561. case 'bb':
  562. case 'lp':
  563. return toFloatList(val);
  564. case 'pos':
  565. if (val.charAt(0) === 'e') {
  566. /*
  567. val = toFloatList(val)
  568. controlPoints = []
  569. # arrow pos are of the form "'e',startx,starty, <triplets of bzCurve xy-coords>, arrowTargetx, arrowTargety"
  570. i = 2
  571. while i + 6 < val.length
  572. controlPoints.push val.slice(i,i+6)
  573. i += 6
  574. return {type: 'edge', origin: val[1..2], controlPoints: controlPoints, arrow: val.slice(-4)}
  575. */
  576. return new Edge(val);
  577. } else {
  578. return toFloatList(val);
  579. }
  580. }
  581. return val;
  582. };
  583. return XDotGraph;
  584. })(DotGraph);