jsmetric 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 (45) hide show
  1. data/.gitignore +6 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +2 -0
  4. data/README +36 -0
  5. data/Rakefile +37 -0
  6. data/bin/jsmetric +16 -0
  7. data/boot.rb +5 -0
  8. data/build +1 -0
  9. data/features/cyclometric_complexity/boolean_complexity_counting.feature +46 -0
  10. data/features/cyclometric_complexity/case_complexity_counting.feature +117 -0
  11. data/features/cyclometric_complexity/exception_complexity_counting.feature +81 -0
  12. data/features/cyclometric_complexity/function_detection.feature +128 -0
  13. data/features/cyclometric_complexity/if_else_complexity_counting.feature +178 -0
  14. data/features/cyclometric_complexity/loop_complexity_counting.feature +81 -0
  15. data/features/graphing/draw_basic_graph.feature +14 -0
  16. data/features/reporting/report.feature +13 -0
  17. data/features/sample_js_files_for_test/foobar.js +30 -0
  18. data/features/step_definitions/cyclometric_complexity_steps.rb +31 -0
  19. data/features/step_definitions/graph_steps.rb +10 -0
  20. data/features/step_definitions/reporting_steps.rb +14 -0
  21. data/features/support/env.rb +1 -0
  22. data/jsgraphlib/Curry-1.0.1.js +29 -0
  23. data/jsgraphlib/dracula_algorithms.js +599 -0
  24. data/jsgraphlib/dracula_graffle.js +106 -0
  25. data/jsgraphlib/dracula_graph.js +534 -0
  26. data/jsgraphlib/graphtest.html +57 -0
  27. data/jsgraphlib/jquery-1.4.2.min.js +154 -0
  28. data/jsgraphlib/jsgraphsource.js +12 -0
  29. data/jsgraphlib/raphael-min.js +7 -0
  30. data/jsgraphlib/seedrandom.js +266 -0
  31. data/jsmetric.gemspec +26 -0
  32. data/lib/cc_report.rb +20 -0
  33. data/lib/complexity_analyser.rb +90 -0
  34. data/lib/fulljslint.js +6100 -0
  35. data/lib/graphing/graph_analyser.rb +19 -0
  36. data/lib/js_lint.rb +28 -0
  37. data/lib/json2.js +480 -0
  38. data/lib/options.js +24 -0
  39. data/lib/report.rb +9 -0
  40. data/lib/utils.rb +18 -0
  41. data/lib/version.rb +3 -0
  42. data/spec/spec_helper.rb +1 -0
  43. data/tasks/dev.rb +4 -0
  44. data/tasks/run.rb +55 -0
  45. metadata +175 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Originally grabbed from the official RaphaelJS Documentation
3
+ * http://raphaeljs.com/graffle.html
4
+ * Adopted (arrows) and commented by Philipp Strathausen http://blog.ameisenbar.de
5
+ * Licenced under the MIT licence.
6
+ */
7
+
8
+ /**
9
+ * Usage:
10
+ * connect two shapes
11
+ * parameters:
12
+ * source shape [or connection for redrawing],
13
+ * target shape,
14
+ * style with { fg : linecolor, bg : background color, directed: boolean }
15
+ * returns:
16
+ * connection { draw = function() }
17
+ */
18
+ Raphael.fn.connection = function (obj1, obj2, style) {
19
+ var selfRef = this;
20
+ /* create and return new connection */
21
+ var edge = {/*
22
+ from : obj1,
23
+ to : obj2,
24
+ style : style,*/
25
+ draw : function() {
26
+ /* get bounding boxes of target and source */
27
+ var bb1 = obj1.getBBox();
28
+ var bb2 = obj2.getBBox();
29
+ var off1 = 0;
30
+ var off2 = 0;
31
+ /* coordinates for potential connection coordinates from/to the objects */
32
+ var p = [
33
+ {x: bb1.x + bb1.width / 2, y: bb1.y - off1}, /* NORTH 1 */
34
+ {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + off1}, /* SOUTH 1 */
35
+ {x: bb1.x - off1, y: bb1.y + bb1.height / 2}, /* WEST 1 */
36
+ {x: bb1.x + bb1.width + off1, y: bb1.y + bb1.height / 2}, /* EAST 1 */
37
+ {x: bb2.x + bb2.width / 2, y: bb2.y - off2}, /* NORTH 2 */
38
+ {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + off2}, /* SOUTH 2 */
39
+ {x: bb2.x - off2, y: bb2.y + bb2.height / 2}, /* WEST 2 */
40
+ {x: bb2.x + bb2.width + off2, y: bb2.y + bb2.height / 2} /* EAST 2 */
41
+ ];
42
+
43
+ /* distances between objects and according coordinates connection */
44
+ var d = {}, dis = [];
45
+
46
+ /*
47
+ * find out the best connection coordinates by trying all possible ways
48
+ */
49
+ /* loop the first object's connection coordinates */
50
+ for (var i = 0; i < 4; i++) {
51
+ /* loop the seond object's connection coordinates */
52
+ for (var j = 4; j < 8; j++) {
53
+ var dx = Math.abs(p[i].x - p[j].x),
54
+ dy = Math.abs(p[i].y - p[j].y);
55
+ if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
56
+ dis.push(dx + dy);
57
+ d[dis[dis.length - 1].toFixed(3)] = [i, j];
58
+ }
59
+ }
60
+ }
61
+ var res = dis.length == 0 ? [0, 4] : d[Math.min.apply(Math, dis).toFixed(3)];
62
+ /* bezier path */
63
+ var x1 = p[res[0]].x,
64
+ y1 = p[res[0]].y,
65
+ x4 = p[res[1]].x,
66
+ y4 = p[res[1]].y,
67
+ dx = Math.max(Math.abs(x1 - x4) / 2, 10),
68
+ dy = Math.max(Math.abs(y1 - y4) / 2, 10),
69
+ x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3),
70
+ y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3),
71
+ x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3),
72
+ y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3);
73
+ /* assemble path and arrow */
74
+ var path = ["M", x1.toFixed(3), y1.toFixed(3), "C", x2, y2, x3, y3, x4.toFixed(3), y4.toFixed(3)].join(",");
75
+ /* arrow */
76
+ if(style && style.directed) {
77
+ /* magnitude, length of the last path vector */
78
+ var mag = Math.sqrt((y4 - y3) * (y4 - y3) + (x4 - x3) * (x4 - x3));
79
+ /* vector normalisation to specified length */
80
+ var norm = function(x,l){return (-x*(l||5)/mag);};
81
+ /* calculate array coordinates (two lines orthogonal to the path vector) */
82
+ var arr = [
83
+ {x:(norm(x4-x3)+norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)+norm(x4-x3)+y4).toFixed(3)},
84
+ {x:(norm(x4-x3)-norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)-norm(x4-x3)+y4).toFixed(3)}
85
+ ];
86
+ path = path + ",M"+arr[0].x+","+arr[0].y+",L"+x4+","+y4+",L"+arr[1].x+","+arr[1].y;
87
+ }
88
+ /* function to be used for moving existent path(s), e.g. animate() or attr() */
89
+ var move = "attr";
90
+ /* applying path(s) */
91
+ edge.fg && edge.fg[move]({path:path})
92
+ || (edge.fg = selfRef.path(path).attr({stroke: style && style.stroke || "#000", fill: "none"}).toBack());
93
+ edge.bg && edge.bg[move]({path:path})
94
+ || style && style.fill && (edge.bg = style.fill.split && selfRef.path(path).attr({stroke: style.fill.split("|")[0], fill: "none", "stroke-width": style.fill.split("|")[1] || 3}).toBack());
95
+ /* setting label */
96
+ style && style.label
97
+ && (edge.label && edge.label.attr({x:(x1+x4)/2, y:(y1+y4)/2})
98
+ || (edge.label = selfRef.text((x1+x4)/2, (y1+y4)/2, style.label).attr({fill: "#000", "font-size": style["font-size"] || "12px"})));
99
+ // && selfRef.text(x4, y4, style.label).attr({stroke: style && style.stroke || "#fff", "font-weight":"bold", "font-size":"20px"})
100
+ // style && style.callback && style.callback(edge);
101
+ }
102
+ }
103
+ edge.draw();
104
+ return edge;
105
+ };
106
+ //Raphael.prototype.set.prototype.dodo=function(){console.log("works");};
@@ -0,0 +1,534 @@
1
+ /*
2
+ * Dracula Graph Layout and Drawing Framework 0.0.3alpha
3
+ * (c) 2010 Philipp Strathausen <strathausen@gmail.com>, http://strathausen.eu
4
+ *
5
+ * based on the Graph JavaScript framework, version 0.0.1
6
+ * (c) 2006 Aslak Hellesoy <aslak.hellesoy@gmail.com>
7
+ * (c) 2006 Dave Hoover <dave.hoover@gmail.com>
8
+ *
9
+ * Ported from Graph::Layouter::Spring in
10
+ * http://search.cpan.org/~pasky/Graph-Layderer-0.02/
11
+ * The algorithm is based on a spring-style layouter of a Java-based social
12
+ * network tracker PieSpy written by Paul Mutton E<lt>paul@jibble.orgE<gt>.
13
+ *
14
+ * This code is freely distributable under the terms of an MIT-style license.
15
+ * For details, see the Graph web site: http://dev.buildpatternd.com/trac
16
+ *
17
+ * Links:
18
+ *
19
+ * Graph Dracula JavaScript Framework:
20
+ * http://graphdracula.net
21
+ *
22
+ * Demo of the original applet:
23
+ * http://redsquirrel.com/dave/work/webdep/
24
+ *
25
+ * Mirrored original source code at snipplr:
26
+ * http://snipplr.com/view/1950/graph-javascript-framework-version-001/
27
+ *
28
+ * Original usage example:
29
+ * http://ajaxian.com/archives/new-javascriptcanvas-graph-library
30
+ *
31
+ /*--------------------------------------------------------------------------*/
32
+
33
+ // Branched by Jake Stothard <stothardj@gmail.com>.
34
+
35
+ /*
36
+ * Edge Factory
37
+ */
38
+ var AbstractEdge = function() {
39
+ }
40
+ AbstractEdge.prototype = {
41
+ hide: function() {
42
+ this.connection.fg.hide();
43
+ this.connection.bg && this.bg.connection.hide();
44
+ }
45
+ };
46
+ var EdgeFactory = function() {
47
+ this.template = new AbstractEdge();
48
+ this.template.style = new Object();
49
+ this.template.style.directed = false;
50
+ this.template.weight = 1;
51
+ };
52
+ EdgeFactory.prototype = {
53
+ build: function(source, target) {
54
+ var e = jQuery.extend(true, {}, this.template);
55
+ e.source = source;
56
+ e.target = target;
57
+ return e;
58
+ }
59
+ };
60
+
61
+ /*
62
+ * Graph
63
+ */
64
+ var Graph = function() {
65
+ this.nodes = new Object();
66
+ this.edges = [];
67
+ this.snapshots = []; // previous graph states TODO to be implemented
68
+ this.edgeFactory = new EdgeFactory();
69
+ };
70
+ Graph.prototype = {
71
+ /*
72
+ * add a node
73
+ * @id the node's ID (string or number)
74
+ * @content (optional, dictionary) can contain any information that is
75
+ * being interpreted by the layout algorithm or the graph
76
+ * representation
77
+ */
78
+ addNode: function(id, content) {
79
+ /* testing if node is already existing in the graph */
80
+ if(this.nodes[id] == undefined) {
81
+ this.nodes[id] = new Graph.Node(id, content);
82
+ }
83
+ return this.nodes[id];
84
+ },
85
+
86
+ addEdge: function(source, target, style) {
87
+ var s = this.addNode(source);
88
+ var t = this.addNode(target);
89
+ var edge = this.edgeFactory.build(s, t);
90
+ jQuery.extend(edge.style,style);
91
+ s.edges.push(edge);
92
+ this.edges.push(edge);
93
+ // NOTE: Even directed edges are added to both nodes.
94
+ t.edges.push(edge);
95
+ },
96
+
97
+ /* TODO to be implemented
98
+ * Preserve a copy of the graph state (nodes, positions, ...)
99
+ * @comment a comment describing the state
100
+ */
101
+ snapShot: function(comment) {
102
+ /* FIXME
103
+ var graph = new Graph();
104
+ graph.nodes = jQuery.extend(true, {}, this.nodes);
105
+ graph.edges = jQuery.extend(true, {}, this.edges);
106
+ this.snapshots.push({comment: comment, graph: graph});
107
+ */
108
+ },
109
+ removeNode: function(id) {
110
+ delete this.nodes[id];
111
+ for(var i = 0; i < this.edges.length; i++) {
112
+ if (this.edges[i].source.id == id || this.edges[i].target.id == id) {
113
+ this.edges.splice(i, 1);
114
+ i--;
115
+ }
116
+ }
117
+ }
118
+ };
119
+
120
+ /*
121
+ * Node
122
+ */
123
+ Graph.Node = function(id, node){
124
+ node = node || {};
125
+ node.id = id;
126
+ node.edges = [];
127
+ node.hide = function() {
128
+ this.hidden = true;
129
+ this.shape && this.shape.hide(); /* FIXME this is representation specific code and should be elsewhere */
130
+ for(i in this.edges)
131
+ (this.edges[i].source.id == id || this.edges[i].target == id) && this.edges[i].hide && this.edges[i].hide();
132
+ };
133
+ node.show = function() {
134
+ this.hidden = false;
135
+ this.shape && this.shape.show();
136
+ for(i in this.edges)
137
+ (this.edges[i].source.id == id || this.edges[i].target == id) && this.edges[i].show && this.edges[i].show();
138
+ };
139
+ return node;
140
+ };
141
+ Graph.Node.prototype = {
142
+ };
143
+
144
+ /*
145
+ * Renderer base class
146
+ */
147
+ Graph.Renderer = {};
148
+
149
+ /*
150
+ * Renderer implementation using RaphaelJS
151
+ */
152
+ Graph.Renderer.Raphael = function(element, graph, width, height) {
153
+ this.width = width || 400;
154
+ this.height = height || 400;
155
+ var selfRef = this;
156
+ this.r = Raphael(element, this.width, this.height);
157
+ this.radius = 40; /* max dimension of a node */
158
+ this.graph = graph;
159
+ this.mouse_in = false;
160
+
161
+ /* TODO default node rendering function */
162
+ if(!this.graph.render) {
163
+ this.graph.render = function() {
164
+ return;
165
+ }
166
+ }
167
+
168
+ /*
169
+ * Dragging
170
+ */
171
+ this.isDrag = false;
172
+ this.dragger = function (e) {
173
+ this.dx = e.clientX;
174
+ this.dy = e.clientY;
175
+ selfRef.isDrag = this;
176
+ this.set && this.set.animate({"fill-opacity": .1}, 200) && this.set.toFront();
177
+ e.preventDefault && e.preventDefault();
178
+ };
179
+
180
+ document.onmousemove = function (e) {
181
+ e = e || window.event;
182
+ if (selfRef.isDrag) {
183
+ var bBox = selfRef.isDrag.set.getBBox();
184
+ // TODO round the coordinates here (eg. for proper image representation)
185
+ var newX = e.clientX - selfRef.isDrag.dx + (bBox.x + bBox.width / 2);
186
+ var newY = e.clientY - selfRef.isDrag.dy + (bBox.y + bBox.height / 2);
187
+ /* prevent shapes from being dragged out of the canvas */
188
+ var clientX = e.clientX - (newX < 20 ? newX - 20 : newX > selfRef.width - 20 ? newX - selfRef.width + 20 : 0);
189
+ var clientY = e.clientY - (newY < 20 ? newY - 20 : newY > selfRef.height - 20 ? newY - selfRef.height + 20 : 0);
190
+ selfRef.isDrag.set.translate(clientX - Math.round(selfRef.isDrag.dx), clientY - Math.round(selfRef.isDrag.dy));
191
+ // console.log(clientX - Math.round(selfRef.isDrag.dx), clientY - Math.round(selfRef.isDrag.dy));
192
+ for (var i in selfRef.graph.edges) {
193
+ selfRef.graph.edges[i].connection && selfRef.graph.edges[i].connection.draw();
194
+ }
195
+ //selfRef.r.safari();
196
+ selfRef.isDrag.dx = clientX;
197
+ selfRef.isDrag.dy = clientY;
198
+ }
199
+ };
200
+ document.onmouseup = function () {
201
+ selfRef.isDrag && selfRef.isDrag.set.animate({"fill-opacity": .6}, 500);
202
+ selfRef.isDrag = false;
203
+ };
204
+ this.draw();
205
+ };
206
+ Graph.Renderer.Raphael.prototype = {
207
+ translate: function(point) {
208
+ return [
209
+ (point[0] - this.graph.layoutMinX) * this.factorX + this.radius,
210
+ (point[1] - this.graph.layoutMinY) * this.factorY + this.radius
211
+ ];
212
+ },
213
+
214
+ rotate: function(point, length, angle) {
215
+ var dx = length * Math.cos(angle);
216
+ var dy = length * Math.sin(angle);
217
+ return [point[0]+dx, point[1]+dy];
218
+ },
219
+
220
+ draw: function() {
221
+ this.factorX = (this.width - 2 * this.radius) / (this.graph.layoutMaxX - this.graph.layoutMinX);
222
+ this.factorY = (this.height - 2 * this.radius) / (this.graph.layoutMaxY - this.graph.layoutMinY);
223
+ for (i in this.graph.nodes) {
224
+ this.drawNode(this.graph.nodes[i]);
225
+ }
226
+ for (var i = 0; i < this.graph.edges.length; i++) {
227
+ this.drawEdge(this.graph.edges[i]);
228
+ }
229
+ },
230
+
231
+ drawNode: function(node) {
232
+ var point = this.translate([node.layoutPosX, node.layoutPosY]);
233
+ node.point = point;
234
+
235
+ /* if node has already been drawn, move the nodes */
236
+ if(node.shape) {
237
+ var oBBox = node.shape.getBBox();
238
+ var opoint = { x: oBBox.x + oBBox.width / 2, y: oBBox.y + oBBox.height / 2};
239
+ node.shape.translate(Math.round(point[0] - opoint.x), Math.round(point[1] - opoint.y));
240
+ this.r.safari();
241
+ return node;
242
+ }/* else, draw new nodes */
243
+
244
+ var shape;
245
+
246
+ /* if a node renderer function is provided by the user, then use it
247
+ or the default render function instead */
248
+ if(!node.render) {
249
+ node.render = function(r, node) {
250
+ /* the default node drawing */
251
+ var color = Raphael.getColor();
252
+ var ellipse = r.ellipse(0, 0, 30, 20).attr({fill: color, stroke: color, "stroke-width": 2});
253
+ /* set DOM node ID */
254
+ ellipse.node.id = node.label || node.id;
255
+ shape = r.set().
256
+ push(ellipse).
257
+ push(r.text(0, 30, node.label || node.id));
258
+ return shape;
259
+ }
260
+ }
261
+ /* or check for an ajax representation of the nodes */
262
+ if(node.shapes) {
263
+ // TODO ajax representation evaluation
264
+ }
265
+
266
+ shape = node.render(this.r, node).hide();
267
+
268
+ shape.attr({"fill-opacity": .6});
269
+ /* re-reference to the node an element belongs to, needed for dragging all elements of a node */
270
+ shape.items.forEach(function(item){ item.set = shape; item.node.style.cursor = "move"; });
271
+ shape.mousedown(this.dragger);
272
+
273
+ var box = shape.getBBox();
274
+ shape.translate(Math.round(point[0]-(box.x+box.width/2)),Math.round(point[1]-(box.y+box.height/2)))
275
+ //console.log(box,point);
276
+ node.hidden || shape.show();
277
+ node.shape = shape;
278
+ },
279
+ drawEdge: function(edge) {
280
+ /* if this edge already exists the other way around and is undirected */
281
+ if(edge.backedge)
282
+ return;
283
+ if(edge.source.hidden || edge.target.hidden) {
284
+ edge.connection && edge.connection.fg.hide() | edge.connection.bg && edge.connection.bg.hide();
285
+ return;
286
+ }
287
+ /* if edge already has been drawn, only refresh the edge */
288
+ if(!edge.connection) {
289
+ edge.style && edge.style.callback && edge.style.callback(edge); // TODO move this somewhere else
290
+ edge.connection = this.r.connection(edge.source.shape, edge.target.shape, edge.style);
291
+ return;
292
+ }
293
+ //FIXME showing doesn't work well
294
+ edge.connection.fg.show();
295
+ edge.connection.bg && edge.connection.bg.show();
296
+ edge.connection.draw();
297
+ }
298
+ };
299
+ Graph.Layout = {};
300
+ Graph.Layout.Spring = function(graph) {
301
+ this.graph = graph;
302
+ this.iterations = 500;
303
+ this.maxRepulsiveForceDistance = 6;
304
+ this.k = 2;
305
+ this.c = 0.01;
306
+ this.maxVertexMovement = 0.5;
307
+ this.layout();
308
+ };
309
+ Graph.Layout.Spring.prototype = {
310
+ layout: function() {
311
+ this.layoutPrepare();
312
+ for (var i = 0; i < this.iterations; i++) {
313
+ this.layoutIteration();
314
+ }
315
+ this.layoutCalcBounds();
316
+ },
317
+
318
+ layoutPrepare: function() {
319
+ for (i in this.graph.nodes) {
320
+ var node = this.graph.nodes[i];
321
+ node.layoutPosX = 0;
322
+ node.layoutPosY = 0;
323
+ node.layoutForceX = 0;
324
+ node.layoutForceY = 0;
325
+ }
326
+
327
+ },
328
+
329
+ layoutCalcBounds: function() {
330
+ var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity;
331
+
332
+ for (i in this.graph.nodes) {
333
+ var x = this.graph.nodes[i].layoutPosX;
334
+ var y = this.graph.nodes[i].layoutPosY;
335
+
336
+ if(x > maxx) maxx = x;
337
+ if(x < minx) minx = x;
338
+ if(y > maxy) maxy = y;
339
+ if(y < miny) miny = y;
340
+ }
341
+
342
+ this.graph.layoutMinX = minx;
343
+ this.graph.layoutMaxX = maxx;
344
+ this.graph.layoutMinY = miny;
345
+ this.graph.layoutMaxY = maxy;
346
+ },
347
+
348
+ layoutIteration: function() {
349
+ // Forces on nodes due to node-node repulsions
350
+
351
+ var prev = new Array();
352
+ for(var c in this.graph.nodes) {
353
+ var node1 = this.graph.nodes[c];
354
+ for (var d in prev) {
355
+ var node2 = this.graph.nodes[prev[d]];
356
+ this.layoutRepulsive(node1, node2);
357
+
358
+ }
359
+ prev.push(c);
360
+ }
361
+
362
+ // Forces on nodes due to edge attractions
363
+ for (var i = 0; i < this.graph.edges.length; i++) {
364
+ var edge = this.graph.edges[i];
365
+ this.layoutAttractive(edge);
366
+ }
367
+
368
+ // Move by the given force
369
+ for (i in this.graph.nodes) {
370
+ var node = this.graph.nodes[i];
371
+ var xmove = this.c * node.layoutForceX;
372
+ var ymove = this.c * node.layoutForceY;
373
+
374
+ var max = this.maxVertexMovement;
375
+ if(xmove > max) xmove = max;
376
+ if(xmove < -max) xmove = -max;
377
+ if(ymove > max) ymove = max;
378
+ if(ymove < -max) ymove = -max;
379
+
380
+ node.layoutPosX += xmove;
381
+ node.layoutPosY += ymove;
382
+ node.layoutForceX = 0;
383
+ node.layoutForceY = 0;
384
+ }
385
+ },
386
+
387
+ layoutRepulsive: function(node1, node2) {
388
+ var dx = node2.layoutPosX - node1.layoutPosX;
389
+ var dy = node2.layoutPosY - node1.layoutPosY;
390
+ var d2 = dx * dx + dy * dy;
391
+ if(d2 < 0.01) {
392
+ dx = 0.1 * Math.random() + 0.1;
393
+ dy = 0.1 * Math.random() + 0.1;
394
+ var d2 = dx * dx + dy * dy;
395
+ }
396
+ var d = Math.sqrt(d2);
397
+ if(d < this.maxRepulsiveForceDistance) {
398
+ var repulsiveForce = this.k * this.k / d;
399
+ node2.layoutForceX += repulsiveForce * dx / d;
400
+ node2.layoutForceY += repulsiveForce * dy / d;
401
+ node1.layoutForceX -= repulsiveForce * dx / d;
402
+ node1.layoutForceY -= repulsiveForce * dy / d;
403
+ }
404
+ },
405
+
406
+ layoutAttractive: function(edge) {
407
+ var node1 = edge.source;
408
+ var node2 = edge.target;
409
+
410
+ var dx = node2.layoutPosX - node1.layoutPosX;
411
+ var dy = node2.layoutPosY - node1.layoutPosY;
412
+ var d2 = dx * dx + dy * dy;
413
+ if(d2 < 0.01) {
414
+ dx = 0.1 * Math.random() + 0.1;
415
+ dy = 0.1 * Math.random() + 0.1;
416
+ var d2 = dx * dx + dy * dy;
417
+ }
418
+ var d = Math.sqrt(d2);
419
+ if(d > this.maxRepulsiveForceDistance) {
420
+ d = this.maxRepulsiveForceDistance;
421
+ d2 = d * d;
422
+ }
423
+ var attractiveForce = (d2 - this.k * this.k) / this.k;
424
+ if(edge.attraction == undefined) edge.attraction = 1;
425
+ attractiveForce *= Math.log(edge.attraction) * 0.5 + 1;
426
+
427
+ node2.layoutForceX -= attractiveForce * dx / d;
428
+ node2.layoutForceY -= attractiveForce * dy / d;
429
+ node1.layoutForceX += attractiveForce * dx / d;
430
+ node1.layoutForceY += attractiveForce * dy / d;
431
+ }
432
+ };
433
+
434
+ Graph.Layout.Ordered = function(graph, order) {
435
+ this.graph = graph;
436
+ this.order = order;
437
+ this.layout();
438
+ };
439
+ Graph.Layout.Ordered.prototype = {
440
+ layout: function() {
441
+ this.layoutPrepare();
442
+ this.layoutCalcBounds();
443
+ },
444
+
445
+ layoutPrepare: function(order) {
446
+ for (i in this.graph.nodes) {
447
+ var node = this.graph.nodes[i];
448
+ node.layoutPosX = 0;
449
+ node.layoutPosY = 0;
450
+ }
451
+ var counter = 0;
452
+ for (i in this.order) {
453
+ var node = this.order[i];
454
+ node.layoutPosX = counter;
455
+ node.layoutPosY = Math.random();
456
+ counter++;
457
+ }
458
+ },
459
+
460
+ layoutCalcBounds: function() {
461
+ var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity;
462
+
463
+ for (i in this.graph.nodes) {
464
+ var x = this.graph.nodes[i].layoutPosX;
465
+ var y = this.graph.nodes[i].layoutPosY;
466
+
467
+ if(x > maxx) maxx = x;
468
+ if(x < minx) minx = x;
469
+ if(y > maxy) maxy = y;
470
+ if(y < miny) miny = y;
471
+ }
472
+
473
+ this.graph.layoutMinX = minx;
474
+ this.graph.layoutMaxX = maxx;
475
+
476
+ this.graph.layoutMinY = miny;
477
+ this.graph.layoutMaxY = maxy;
478
+ },
479
+ };
480
+
481
+ /*
482
+ * usefull JavaScript extensions,
483
+ */
484
+
485
+ function log(a) {console.log&&console.log(a);}
486
+
487
+ /*
488
+ * Raphael Tooltip Plugin
489
+ * - attaches an element as a tooltip to another element
490
+ *
491
+ * Usage example, adding a rectangle as a tooltip to a circle:
492
+ *
493
+ * paper.circle(100,100,10).tooltip(paper.rect(0,0,20,30));
494
+ *
495
+ * If you want to use more shapes, you'll have to put them into a set.
496
+ *
497
+ */
498
+ Raphael.el.tooltip = function (tp) {
499
+ this.tp = tp;
500
+ this.tp.o = {x: 0, y: 0};
501
+ this.tp.hide();
502
+ this.hover(
503
+ function(event){
504
+ this.mousemove(function(event){
505
+ this.tp.translate(event.clientX -
506
+ this.tp.o.x,event.clientY - this.tp.o.y);
507
+ this.tp.o = {x: event.clientX, y: event.clientY};
508
+ });
509
+ this.tp.show().toFront();
510
+ },
511
+ function(event){
512
+ this.tp.hide();
513
+ this.unmousemove();
514
+ });
515
+ return this;
516
+ };
517
+
518
+ /* For IE */
519
+ if (!Array.prototype.forEach)
520
+ {
521
+ Array.prototype.forEach = function(fun /*, thisp*/)
522
+ {
523
+ var len = this.length;
524
+ if (typeof fun != "function")
525
+ throw new TypeError();
526
+
527
+ var thisp = arguments[1];
528
+ for (var i = 0; i < len; i++)
529
+ {
530
+ if (i in this)
531
+ fun.call(thisp, this[i], i, this);
532
+ }
533
+ };
534
+ }