jsmetric4java 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.
- data/.gitignore +7 -0
- data/.rvmrc +1 -0
- data/Gemfile +8 -0
- data/README +16 -0
- data/Rakefile +37 -0
- data/bin/jsmetric4java +20 -0
- data/boot.rb +5 -0
- data/build +1 -0
- data/features/cyclometric_complexity/boolean_complexity_counting.feature +46 -0
- data/features/cyclometric_complexity/case_complexity_counting.feature +117 -0
- data/features/cyclometric_complexity/exception_complexity_counting.feature +81 -0
- data/features/cyclometric_complexity/function_detection.feature +128 -0
- data/features/cyclometric_complexity/if_else_complexity_counting.feature +178 -0
- data/features/cyclometric_complexity/loop_complexity_counting.feature +81 -0
- data/features/graphing/draw_basic_graph.feature +14 -0
- data/features/reporting/report.feature +13 -0
- data/features/sample_js_files_for_test/foobar.js +30 -0
- data/features/step_definitions/cyclometric_complexity_steps.rb +31 -0
- data/features/step_definitions/graph_steps.rb +10 -0
- data/features/step_definitions/reporting_steps.rb +14 -0
- data/features/support/env.rb +1 -0
- data/jsgraphlib/Curry-1.0.1.js +29 -0
- data/jsgraphlib/dracula_algorithms.js +599 -0
- data/jsgraphlib/dracula_graffle.js +106 -0
- data/jsgraphlib/dracula_graph.js +534 -0
- data/jsgraphlib/graphtest.html +57 -0
- data/jsgraphlib/jquery-1.4.2.min.js +154 -0
- data/jsgraphlib/jsgraphsource.js +12 -0
- data/jsgraphlib/raphael-min.js +7 -0
- data/jsgraphlib/seedrandom.js +266 -0
- data/jsmetric.gemspec +23 -0
- data/lib/cc_report.rb +24 -0
- data/lib/complexity_analyser.rb +90 -0
- data/lib/fulljslint.js +6100 -0
- data/lib/graphing/graph_analyser.rb +19 -0
- data/lib/js_lint.rb +23 -0
- data/lib/json2.js +480 -0
- data/lib/options.js +24 -0
- data/lib/report.rb +26 -0
- data/lib/utils.rb +18 -0
- data/lib/version.rb +3 -0
- data/spec/spec_helper.rb +1 -0
- data/tasks/dev.rb +4 -0
- data/tasks/run.rb +55 -0
- metadata +129 -0
@@ -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
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Graphing JavaScript with JavaScript</title>
|
4
|
+
|
5
|
+
</head>
|
6
|
+
<body>
|
7
|
+
<div id='canvas'></div>
|
8
|
+
|
9
|
+
<script type="text/javascript" src="raphael-min.js"></script>
|
10
|
+
<script type="text/javascript" src="dracula_graffle.js"></script>
|
11
|
+
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
|
12
|
+
<script type="text/javascript" src="dracula_graph.js"></script>
|
13
|
+
<script type="text/javascript" src="dracula_algorithms.js"></script>
|
14
|
+
<script type="text/javascript" src="jsgraphsource.js"></script>
|
15
|
+
<script type="text/javascript">
|
16
|
+
$(document).ready(function() {
|
17
|
+
|
18
|
+
for (var js_class in graphdata) {
|
19
|
+
if (graphdata.hasOwnProperty(js_class)) {
|
20
|
+
process_class(graphdata[js_class]);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
});
|
24
|
+
|
25
|
+
function process_class(js_class) {
|
26
|
+
var width = 300;
|
27
|
+
var height = 300;
|
28
|
+
|
29
|
+
var g = new Graph();
|
30
|
+
g.edgeFactory.template.style.directed = true;
|
31
|
+
for (js_method in js_class) {
|
32
|
+
if (js_class.hasOwnProperty(js_method)) {
|
33
|
+
process_method(g, js_method, js_class[js_method]);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
var layouter = new Graph.Layout.Ordered(g, topological_sort(g));
|
37
|
+
var renderer = new Graph.Renderer.Raphael('canvas', g, width, height);
|
38
|
+
}
|
39
|
+
|
40
|
+
function process_method(graph, js_method_name, js_calls) {
|
41
|
+
var i;
|
42
|
+
for (i = 0; i < js_calls.length; i++) {
|
43
|
+
graph.addEdge(js_method_name, js_calls[i]);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
</script>
|
49
|
+
|
50
|
+
|
51
|
+
</body>
|
52
|
+
</html>
|
53
|
+
<!-- Ideas - to do
|
54
|
+
Initial load produces a list of classes
|
55
|
+
click a link to view a graph for that class instead of all graphs on one page
|
56
|
+
|
57
|
+
-->
|