vis-rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/.project +11 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +202 -0
  7. data/README.md +29 -0
  8. data/Rakefile +1 -0
  9. data/lib/vis/rails/engine.rb +6 -0
  10. data/lib/vis/rails/version.rb +5 -0
  11. data/lib/vis/rails.rb +7 -0
  12. data/vendor/assets/javascripts/vis.js +1 -0
  13. data/vendor/assets/stylesheets/vis.css +3 -0
  14. data/vendor/assets/vis/DataSet.js +936 -0
  15. data/vendor/assets/vis/DataView.js +281 -0
  16. data/vendor/assets/vis/EventBus.js +89 -0
  17. data/vendor/assets/vis/events.js +116 -0
  18. data/vendor/assets/vis/graph/ClusterMixin.js +1019 -0
  19. data/vendor/assets/vis/graph/Edge.js +620 -0
  20. data/vendor/assets/vis/graph/Graph.js +2111 -0
  21. data/vendor/assets/vis/graph/Groups.js +80 -0
  22. data/vendor/assets/vis/graph/Images.js +41 -0
  23. data/vendor/assets/vis/graph/NavigationMixin.js +245 -0
  24. data/vendor/assets/vis/graph/Node.js +978 -0
  25. data/vendor/assets/vis/graph/Popup.js +105 -0
  26. data/vendor/assets/vis/graph/SectorsMixin.js +547 -0
  27. data/vendor/assets/vis/graph/SelectionMixin.js +515 -0
  28. data/vendor/assets/vis/graph/dotparser.js +829 -0
  29. data/vendor/assets/vis/graph/img/downarrow.png +0 -0
  30. data/vendor/assets/vis/graph/img/leftarrow.png +0 -0
  31. data/vendor/assets/vis/graph/img/minus.png +0 -0
  32. data/vendor/assets/vis/graph/img/plus.png +0 -0
  33. data/vendor/assets/vis/graph/img/rightarrow.png +0 -0
  34. data/vendor/assets/vis/graph/img/uparrow.png +0 -0
  35. data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
  36. data/vendor/assets/vis/graph/shapes.js +225 -0
  37. data/vendor/assets/vis/module/exports.js +68 -0
  38. data/vendor/assets/vis/module/header.js +24 -0
  39. data/vendor/assets/vis/module/imports.js +32 -0
  40. data/vendor/assets/vis/shim.js +252 -0
  41. data/vendor/assets/vis/timeline/Controller.js +172 -0
  42. data/vendor/assets/vis/timeline/Range.js +553 -0
  43. data/vendor/assets/vis/timeline/Stack.js +192 -0
  44. data/vendor/assets/vis/timeline/TimeStep.js +449 -0
  45. data/vendor/assets/vis/timeline/Timeline.js +476 -0
  46. data/vendor/assets/vis/timeline/component/Component.js +148 -0
  47. data/vendor/assets/vis/timeline/component/ContentPanel.js +113 -0
  48. data/vendor/assets/vis/timeline/component/CurrentTime.js +101 -0
  49. data/vendor/assets/vis/timeline/component/CustomTime.js +255 -0
  50. data/vendor/assets/vis/timeline/component/Group.js +129 -0
  51. data/vendor/assets/vis/timeline/component/GroupSet.js +546 -0
  52. data/vendor/assets/vis/timeline/component/ItemSet.js +612 -0
  53. data/vendor/assets/vis/timeline/component/Panel.js +112 -0
  54. data/vendor/assets/vis/timeline/component/RootPanel.js +215 -0
  55. data/vendor/assets/vis/timeline/component/TimeAxis.js +522 -0
  56. data/vendor/assets/vis/timeline/component/css/currenttime.css +5 -0
  57. data/vendor/assets/vis/timeline/component/css/customtime.css +6 -0
  58. data/vendor/assets/vis/timeline/component/css/groupset.css +59 -0
  59. data/vendor/assets/vis/timeline/component/css/item.css +93 -0
  60. data/vendor/assets/vis/timeline/component/css/itemset.css +17 -0
  61. data/vendor/assets/vis/timeline/component/css/panel.css +14 -0
  62. data/vendor/assets/vis/timeline/component/css/timeaxis.css +41 -0
  63. data/vendor/assets/vis/timeline/component/css/timeline.css +2 -0
  64. data/vendor/assets/vis/timeline/component/item/Item.js +81 -0
  65. data/vendor/assets/vis/timeline/component/item/ItemBox.js +302 -0
  66. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +237 -0
  67. data/vendor/assets/vis/timeline/component/item/ItemRange.js +251 -0
  68. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +91 -0
  69. data/vendor/assets/vis/util.js +673 -0
  70. data/vis-rails.gemspec +47 -0
  71. metadata +142 -0
@@ -0,0 +1,829 @@
1
+ (function(exports) {
2
+ /**
3
+ * Parse a text source containing data in DOT language into a JSON object.
4
+ * The object contains two lists: one with nodes and one with edges.
5
+ *
6
+ * DOT language reference: http://www.graphviz.org/doc/info/lang.html
7
+ *
8
+ * @param {String} data Text containing a graph in DOT-notation
9
+ * @return {Object} graph An object containing two parameters:
10
+ * {Object[]} nodes
11
+ * {Object[]} edges
12
+ */
13
+ function parseDOT (data) {
14
+ dot = data;
15
+ return parseGraph();
16
+ }
17
+
18
+ // token types enumeration
19
+ var TOKENTYPE = {
20
+ NULL : 0,
21
+ DELIMITER : 1,
22
+ IDENTIFIER: 2,
23
+ UNKNOWN : 3
24
+ };
25
+
26
+ // map with all delimiters
27
+ var DELIMITERS = {
28
+ '{': true,
29
+ '}': true,
30
+ '[': true,
31
+ ']': true,
32
+ ';': true,
33
+ '=': true,
34
+ ',': true,
35
+
36
+ '->': true,
37
+ '--': true
38
+ };
39
+
40
+ var dot = ''; // current dot file
41
+ var index = 0; // current index in dot file
42
+ var c = ''; // current token character in expr
43
+ var token = ''; // current token
44
+ var tokenType = TOKENTYPE.NULL; // type of the token
45
+
46
+ /**
47
+ * Get the first character from the dot file.
48
+ * The character is stored into the char c. If the end of the dot file is
49
+ * reached, the function puts an empty string in c.
50
+ */
51
+ function first() {
52
+ index = 0;
53
+ c = dot.charAt(0);
54
+ }
55
+
56
+ /**
57
+ * Get the next character from the dot file.
58
+ * The character is stored into the char c. If the end of the dot file is
59
+ * reached, the function puts an empty string in c.
60
+ */
61
+ function next() {
62
+ index++;
63
+ c = dot.charAt(index);
64
+ }
65
+
66
+ /**
67
+ * Preview the next character from the dot file.
68
+ * @return {String} cNext
69
+ */
70
+ function nextPreview() {
71
+ return dot.charAt(index + 1);
72
+ }
73
+
74
+ /**
75
+ * Test whether given character is alphabetic or numeric
76
+ * @param {String} c
77
+ * @return {Boolean} isAlphaNumeric
78
+ */
79
+ var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/;
80
+ function isAlphaNumeric(c) {
81
+ return regexAlphaNumeric.test(c);
82
+ }
83
+
84
+ /**
85
+ * Merge all properties of object b into object b
86
+ * @param {Object} a
87
+ * @param {Object} b
88
+ * @return {Object} a
89
+ */
90
+ function merge (a, b) {
91
+ if (!a) {
92
+ a = {};
93
+ }
94
+
95
+ if (b) {
96
+ for (var name in b) {
97
+ if (b.hasOwnProperty(name)) {
98
+ a[name] = b[name];
99
+ }
100
+ }
101
+ }
102
+ return a;
103
+ }
104
+
105
+ /**
106
+ * Set a value in an object, where the provided parameter name can be a
107
+ * path with nested parameters. For example:
108
+ *
109
+ * var obj = {a: 2};
110
+ * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}}
111
+ *
112
+ * @param {Object} obj
113
+ * @param {String} path A parameter name or dot-separated parameter path,
114
+ * like "color.highlight.border".
115
+ * @param {*} value
116
+ */
117
+ function setValue(obj, path, value) {
118
+ var keys = path.split('.');
119
+ var o = obj;
120
+ while (keys.length) {
121
+ var key = keys.shift();
122
+ if (keys.length) {
123
+ // this isn't the end point
124
+ if (!o[key]) {
125
+ o[key] = {};
126
+ }
127
+ o = o[key];
128
+ }
129
+ else {
130
+ // this is the end point
131
+ o[key] = value;
132
+ }
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Add a node to a graph object. If there is already a node with
138
+ * the same id, their attributes will be merged.
139
+ * @param {Object} graph
140
+ * @param {Object} node
141
+ */
142
+ function addNode(graph, node) {
143
+ var i, len;
144
+ var current = null;
145
+
146
+ // find root graph (in case of subgraph)
147
+ var graphs = [graph]; // list with all graphs from current graph to root graph
148
+ var root = graph;
149
+ while (root.parent) {
150
+ graphs.push(root.parent);
151
+ root = root.parent;
152
+ }
153
+
154
+ // find existing node (at root level) by its id
155
+ if (root.nodes) {
156
+ for (i = 0, len = root.nodes.length; i < len; i++) {
157
+ if (node.id === root.nodes[i].id) {
158
+ current = root.nodes[i];
159
+ break;
160
+ }
161
+ }
162
+ }
163
+
164
+ if (!current) {
165
+ // this is a new node
166
+ current = {
167
+ id: node.id
168
+ };
169
+ if (graph.node) {
170
+ // clone default attributes
171
+ current.attr = merge(current.attr, graph.node);
172
+ }
173
+ }
174
+
175
+ // add node to this (sub)graph and all its parent graphs
176
+ for (i = graphs.length - 1; i >= 0; i--) {
177
+ var g = graphs[i];
178
+
179
+ if (!g.nodes) {
180
+ g.nodes = [];
181
+ }
182
+ if (g.nodes.indexOf(current) == -1) {
183
+ g.nodes.push(current);
184
+ }
185
+ }
186
+
187
+ // merge attributes
188
+ if (node.attr) {
189
+ current.attr = merge(current.attr, node.attr);
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Add an edge to a graph object
195
+ * @param {Object} graph
196
+ * @param {Object} edge
197
+ */
198
+ function addEdge(graph, edge) {
199
+ if (!graph.edges) {
200
+ graph.edges = [];
201
+ }
202
+ graph.edges.push(edge);
203
+ if (graph.edge) {
204
+ var attr = merge({}, graph.edge); // clone default attributes
205
+ edge.attr = merge(attr, edge.attr); // merge attributes
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Create an edge to a graph object
211
+ * @param {Object} graph
212
+ * @param {String | Number | Object} from
213
+ * @param {String | Number | Object} to
214
+ * @param {String} type
215
+ * @param {Object | null} attr
216
+ * @return {Object} edge
217
+ */
218
+ function createEdge(graph, from, to, type, attr) {
219
+ var edge = {
220
+ from: from,
221
+ to: to,
222
+ type: type
223
+ };
224
+
225
+ if (graph.edge) {
226
+ edge.attr = merge({}, graph.edge); // clone default attributes
227
+ }
228
+ edge.attr = merge(edge.attr || {}, attr); // merge attributes
229
+
230
+ return edge;
231
+ }
232
+
233
+ /**
234
+ * Get next token in the current dot file.
235
+ * The token and token type are available as token and tokenType
236
+ */
237
+ function getToken() {
238
+ tokenType = TOKENTYPE.NULL;
239
+ token = '';
240
+
241
+ // skip over whitespaces
242
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
243
+ next();
244
+ }
245
+
246
+ do {
247
+ var isComment = false;
248
+
249
+ // skip comment
250
+ if (c == '#') {
251
+ // find the previous non-space character
252
+ var i = index - 1;
253
+ while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') {
254
+ i--;
255
+ }
256
+ if (dot.charAt(i) == '\n' || dot.charAt(i) == '') {
257
+ // the # is at the start of a line, this is indeed a line comment
258
+ while (c != '' && c != '\n') {
259
+ next();
260
+ }
261
+ isComment = true;
262
+ }
263
+ }
264
+ if (c == '/' && nextPreview() == '/') {
265
+ // skip line comment
266
+ while (c != '' && c != '\n') {
267
+ next();
268
+ }
269
+ isComment = true;
270
+ }
271
+ if (c == '/' && nextPreview() == '*') {
272
+ // skip block comment
273
+ while (c != '') {
274
+ if (c == '*' && nextPreview() == '/') {
275
+ // end of block comment found. skip these last two characters
276
+ next();
277
+ next();
278
+ break;
279
+ }
280
+ else {
281
+ next();
282
+ }
283
+ }
284
+ isComment = true;
285
+ }
286
+
287
+ // skip over whitespaces
288
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
289
+ next();
290
+ }
291
+ }
292
+ while (isComment);
293
+
294
+ // check for end of dot file
295
+ if (c == '') {
296
+ // token is still empty
297
+ tokenType = TOKENTYPE.DELIMITER;
298
+ return;
299
+ }
300
+
301
+ // check for delimiters consisting of 2 characters
302
+ var c2 = c + nextPreview();
303
+ if (DELIMITERS[c2]) {
304
+ tokenType = TOKENTYPE.DELIMITER;
305
+ token = c2;
306
+ next();
307
+ next();
308
+ return;
309
+ }
310
+
311
+ // check for delimiters consisting of 1 character
312
+ if (DELIMITERS[c]) {
313
+ tokenType = TOKENTYPE.DELIMITER;
314
+ token = c;
315
+ next();
316
+ return;
317
+ }
318
+
319
+ // check for an identifier (number or string)
320
+ // TODO: more precise parsing of numbers/strings (and the port separator ':')
321
+ if (isAlphaNumeric(c) || c == '-') {
322
+ token += c;
323
+ next();
324
+
325
+ while (isAlphaNumeric(c)) {
326
+ token += c;
327
+ next();
328
+ }
329
+ if (token == 'false') {
330
+ token = false; // convert to boolean
331
+ }
332
+ else if (token == 'true') {
333
+ token = true; // convert to boolean
334
+ }
335
+ else if (!isNaN(Number(token))) {
336
+ token = Number(token); // convert to number
337
+ }
338
+ tokenType = TOKENTYPE.IDENTIFIER;
339
+ return;
340
+ }
341
+
342
+ // check for a string enclosed by double quotes
343
+ if (c == '"') {
344
+ next();
345
+ while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) {
346
+ token += c;
347
+ if (c == '"') { // skip the escape character
348
+ next();
349
+ }
350
+ next();
351
+ }
352
+ if (c != '"') {
353
+ throw newSyntaxError('End of string " expected');
354
+ }
355
+ next();
356
+ tokenType = TOKENTYPE.IDENTIFIER;
357
+ return;
358
+ }
359
+
360
+ // something unknown is found, wrong characters, a syntax error
361
+ tokenType = TOKENTYPE.UNKNOWN;
362
+ while (c != '') {
363
+ token += c;
364
+ next();
365
+ }
366
+ throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"');
367
+ }
368
+
369
+ /**
370
+ * Parse a graph.
371
+ * @returns {Object} graph
372
+ */
373
+ function parseGraph() {
374
+ var graph = {};
375
+
376
+ first();
377
+ getToken();
378
+
379
+ // optional strict keyword
380
+ if (token == 'strict') {
381
+ graph.strict = true;
382
+ getToken();
383
+ }
384
+
385
+ // graph or digraph keyword
386
+ if (token == 'graph' || token == 'digraph') {
387
+ graph.type = token;
388
+ getToken();
389
+ }
390
+
391
+ // optional graph id
392
+ if (tokenType == TOKENTYPE.IDENTIFIER) {
393
+ graph.id = token;
394
+ getToken();
395
+ }
396
+
397
+ // open angle bracket
398
+ if (token != '{') {
399
+ throw newSyntaxError('Angle bracket { expected');
400
+ }
401
+ getToken();
402
+
403
+ // statements
404
+ parseStatements(graph);
405
+
406
+ // close angle bracket
407
+ if (token != '}') {
408
+ throw newSyntaxError('Angle bracket } expected');
409
+ }
410
+ getToken();
411
+
412
+ // end of file
413
+ if (token !== '') {
414
+ throw newSyntaxError('End of file expected');
415
+ }
416
+ getToken();
417
+
418
+ // remove temporary default properties
419
+ delete graph.node;
420
+ delete graph.edge;
421
+ delete graph.graph;
422
+
423
+ return graph;
424
+ }
425
+
426
+ /**
427
+ * Parse a list with statements.
428
+ * @param {Object} graph
429
+ */
430
+ function parseStatements (graph) {
431
+ while (token !== '' && token != '}') {
432
+ parseStatement(graph);
433
+ if (token == ';') {
434
+ getToken();
435
+ }
436
+ }
437
+ }
438
+
439
+ /**
440
+ * Parse a single statement. Can be a an attribute statement, node
441
+ * statement, a series of node statements and edge statements, or a
442
+ * parameter.
443
+ * @param {Object} graph
444
+ */
445
+ function parseStatement(graph) {
446
+ // parse subgraph
447
+ var subgraph = parseSubgraph(graph);
448
+ if (subgraph) {
449
+ // edge statements
450
+ parseEdge(graph, subgraph);
451
+
452
+ return;
453
+ }
454
+
455
+ // parse an attribute statement
456
+ var attr = parseAttributeStatement(graph);
457
+ if (attr) {
458
+ return;
459
+ }
460
+
461
+ // parse node
462
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
463
+ throw newSyntaxError('Identifier expected');
464
+ }
465
+ var id = token; // id can be a string or a number
466
+ getToken();
467
+
468
+ if (token == '=') {
469
+ // id statement
470
+ getToken();
471
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
472
+ throw newSyntaxError('Identifier expected');
473
+ }
474
+ graph[id] = token;
475
+ getToken();
476
+ // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] "
477
+ }
478
+ else {
479
+ parseNodeStatement(graph, id);
480
+ }
481
+ }
482
+
483
+ /**
484
+ * Parse a subgraph
485
+ * @param {Object} graph parent graph object
486
+ * @return {Object | null} subgraph
487
+ */
488
+ function parseSubgraph (graph) {
489
+ var subgraph = null;
490
+
491
+ // optional subgraph keyword
492
+ if (token == 'subgraph') {
493
+ subgraph = {};
494
+ subgraph.type = 'subgraph';
495
+ getToken();
496
+
497
+ // optional graph id
498
+ if (tokenType == TOKENTYPE.IDENTIFIER) {
499
+ subgraph.id = token;
500
+ getToken();
501
+ }
502
+ }
503
+
504
+ // open angle bracket
505
+ if (token == '{') {
506
+ getToken();
507
+
508
+ if (!subgraph) {
509
+ subgraph = {};
510
+ }
511
+ subgraph.parent = graph;
512
+ subgraph.node = graph.node;
513
+ subgraph.edge = graph.edge;
514
+ subgraph.graph = graph.graph;
515
+
516
+ // statements
517
+ parseStatements(subgraph);
518
+
519
+ // close angle bracket
520
+ if (token != '}') {
521
+ throw newSyntaxError('Angle bracket } expected');
522
+ }
523
+ getToken();
524
+
525
+ // remove temporary default properties
526
+ delete subgraph.node;
527
+ delete subgraph.edge;
528
+ delete subgraph.graph;
529
+ delete subgraph.parent;
530
+
531
+ // register at the parent graph
532
+ if (!graph.subgraphs) {
533
+ graph.subgraphs = [];
534
+ }
535
+ graph.subgraphs.push(subgraph);
536
+ }
537
+
538
+ return subgraph;
539
+ }
540
+
541
+ /**
542
+ * parse an attribute statement like "node [shape=circle fontSize=16]".
543
+ * Available keywords are 'node', 'edge', 'graph'.
544
+ * The previous list with default attributes will be replaced
545
+ * @param {Object} graph
546
+ * @returns {String | null} keyword Returns the name of the parsed attribute
547
+ * (node, edge, graph), or null if nothing
548
+ * is parsed.
549
+ */
550
+ function parseAttributeStatement (graph) {
551
+ // attribute statements
552
+ if (token == 'node') {
553
+ getToken();
554
+
555
+ // node attributes
556
+ graph.node = parseAttributeList();
557
+ return 'node';
558
+ }
559
+ else if (token == 'edge') {
560
+ getToken();
561
+
562
+ // edge attributes
563
+ graph.edge = parseAttributeList();
564
+ return 'edge';
565
+ }
566
+ else if (token == 'graph') {
567
+ getToken();
568
+
569
+ // graph attributes
570
+ graph.graph = parseAttributeList();
571
+ return 'graph';
572
+ }
573
+
574
+ return null;
575
+ }
576
+
577
+ /**
578
+ * parse a node statement
579
+ * @param {Object} graph
580
+ * @param {String | Number} id
581
+ */
582
+ function parseNodeStatement(graph, id) {
583
+ // node statement
584
+ var node = {
585
+ id: id
586
+ };
587
+ var attr = parseAttributeList();
588
+ if (attr) {
589
+ node.attr = attr;
590
+ }
591
+ addNode(graph, node);
592
+
593
+ // edge statements
594
+ parseEdge(graph, id);
595
+ }
596
+
597
+ /**
598
+ * Parse an edge or a series of edges
599
+ * @param {Object} graph
600
+ * @param {String | Number} from Id of the from node
601
+ */
602
+ function parseEdge(graph, from) {
603
+ while (token == '->' || token == '--') {
604
+ var to;
605
+ var type = token;
606
+ getToken();
607
+
608
+ var subgraph = parseSubgraph(graph);
609
+ if (subgraph) {
610
+ to = subgraph;
611
+ }
612
+ else {
613
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
614
+ throw newSyntaxError('Identifier or subgraph expected');
615
+ }
616
+ to = token;
617
+ addNode(graph, {
618
+ id: to
619
+ });
620
+ getToken();
621
+ }
622
+
623
+ // parse edge attributes
624
+ var attr = parseAttributeList();
625
+
626
+ // create edge
627
+ var edge = createEdge(graph, from, to, type, attr);
628
+ addEdge(graph, edge);
629
+
630
+ from = to;
631
+ }
632
+ }
633
+
634
+ /**
635
+ * Parse a set with attributes,
636
+ * for example [label="1.000", shape=solid]
637
+ * @return {Object | null} attr
638
+ */
639
+ function parseAttributeList() {
640
+ var attr = null;
641
+
642
+ while (token == '[') {
643
+ getToken();
644
+ attr = {};
645
+ while (token !== '' && token != ']') {
646
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
647
+ throw newSyntaxError('Attribute name expected');
648
+ }
649
+ var name = token;
650
+
651
+ getToken();
652
+ if (token != '=') {
653
+ throw newSyntaxError('Equal sign = expected');
654
+ }
655
+ getToken();
656
+
657
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
658
+ throw newSyntaxError('Attribute value expected');
659
+ }
660
+ var value = token;
661
+ setValue(attr, name, value); // name can be a path
662
+
663
+ getToken();
664
+ if (token ==',') {
665
+ getToken();
666
+ }
667
+ }
668
+
669
+ if (token != ']') {
670
+ throw newSyntaxError('Bracket ] expected');
671
+ }
672
+ getToken();
673
+ }
674
+
675
+ return attr;
676
+ }
677
+
678
+ /**
679
+ * Create a syntax error with extra information on current token and index.
680
+ * @param {String} message
681
+ * @returns {SyntaxError} err
682
+ */
683
+ function newSyntaxError(message) {
684
+ return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')');
685
+ }
686
+
687
+ /**
688
+ * Chop off text after a maximum length
689
+ * @param {String} text
690
+ * @param {Number} maxLength
691
+ * @returns {String}
692
+ */
693
+ function chop (text, maxLength) {
694
+ return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...');
695
+ }
696
+
697
+ /**
698
+ * Execute a function fn for each pair of elements in two arrays
699
+ * @param {Array | *} array1
700
+ * @param {Array | *} array2
701
+ * @param {function} fn
702
+ */
703
+ function forEach2(array1, array2, fn) {
704
+ if (array1 instanceof Array) {
705
+ array1.forEach(function (elem1) {
706
+ if (array2 instanceof Array) {
707
+ array2.forEach(function (elem2) {
708
+ fn(elem1, elem2);
709
+ });
710
+ }
711
+ else {
712
+ fn(elem1, array2);
713
+ }
714
+ });
715
+ }
716
+ else {
717
+ if (array2 instanceof Array) {
718
+ array2.forEach(function (elem2) {
719
+ fn(array1, elem2);
720
+ });
721
+ }
722
+ else {
723
+ fn(array1, array2);
724
+ }
725
+ }
726
+ }
727
+
728
+ /**
729
+ * Convert a string containing a graph in DOT language into a map containing
730
+ * with nodes and edges in the format of graph.
731
+ * @param {String} data Text containing a graph in DOT-notation
732
+ * @return {Object} graphData
733
+ */
734
+ function DOTToGraph (data) {
735
+ // parse the DOT file
736
+ var dotData = parseDOT(data);
737
+ var graphData = {
738
+ nodes: [],
739
+ edges: [],
740
+ options: {}
741
+ };
742
+
743
+ // copy the nodes
744
+ if (dotData.nodes) {
745
+ dotData.nodes.forEach(function (dotNode) {
746
+ var graphNode = {
747
+ id: dotNode.id,
748
+ label: String(dotNode.label || dotNode.id)
749
+ };
750
+ merge(graphNode, dotNode.attr);
751
+ if (graphNode.image) {
752
+ graphNode.shape = 'image';
753
+ }
754
+ graphData.nodes.push(graphNode);
755
+ });
756
+ }
757
+
758
+ // copy the edges
759
+ if (dotData.edges) {
760
+ /**
761
+ * Convert an edge in DOT format to an edge with VisGraph format
762
+ * @param {Object} dotEdge
763
+ * @returns {Object} graphEdge
764
+ */
765
+ function convertEdge(dotEdge) {
766
+ var graphEdge = {
767
+ from: dotEdge.from,
768
+ to: dotEdge.to
769
+ };
770
+ merge(graphEdge, dotEdge.attr);
771
+ graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line';
772
+ return graphEdge;
773
+ }
774
+
775
+ dotData.edges.forEach(function (dotEdge) {
776
+ var from, to;
777
+ if (dotEdge.from instanceof Object) {
778
+ from = dotEdge.from.nodes;
779
+ }
780
+ else {
781
+ from = {
782
+ id: dotEdge.from
783
+ }
784
+ }
785
+
786
+ if (dotEdge.to instanceof Object) {
787
+ to = dotEdge.to.nodes;
788
+ }
789
+ else {
790
+ to = {
791
+ id: dotEdge.to
792
+ }
793
+ }
794
+
795
+ if (dotEdge.from instanceof Object && dotEdge.from.edges) {
796
+ dotEdge.from.edges.forEach(function (subEdge) {
797
+ var graphEdge = convertEdge(subEdge);
798
+ graphData.edges.push(graphEdge);
799
+ });
800
+ }
801
+
802
+ forEach2(from, to, function (from, to) {
803
+ var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr);
804
+ var graphEdge = convertEdge(subEdge);
805
+ graphData.edges.push(graphEdge);
806
+ });
807
+
808
+ if (dotEdge.to instanceof Object && dotEdge.to.edges) {
809
+ dotEdge.to.edges.forEach(function (subEdge) {
810
+ var graphEdge = convertEdge(subEdge);
811
+ graphData.edges.push(graphEdge);
812
+ });
813
+ }
814
+ });
815
+ }
816
+
817
+ // copy the options
818
+ if (dotData.attr) {
819
+ graphData.options = dotData.attr;
820
+ }
821
+
822
+ return graphData;
823
+ }
824
+
825
+ // exports
826
+ exports.parseDOT = parseDOT;
827
+ exports.DOTToGraph = DOTToGraph;
828
+
829
+ })(typeof util !== 'undefined' ? util : exports);