vis-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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);