/*
 *  Dracula Graph Layout and Drawing Framework 0.0.3alpha
 *  (c) 2010 Philipp Strathausen <strathausen@gmail.com>, http://strathausen.eu
 *  Contributions by Jake Stothard <stothardj@gmail.com>.
 *
 *  based on the Graph JavaScript framework, version 0.0.1
 *  (c) 2006 Aslak Hellesoy <aslak.hellesoy@gmail.com>
 *  (c) 2006 Dave Hoover <dave.hoover@gmail.com>
 *
 *  Ported from Graph::Layouter::Spring in
 *    http://search.cpan.org/~pasky/Graph-Layderer-0.02/
 *  The algorithm is based on a spring-style layouter of a Java-based social
 *  network tracker PieSpy written by Paul Mutton <paul@jibble.org>.
 *
 *  This code is freely distributable under the MIT license. Commercial use is
 *  hereby granted without any cost or restriction.
 *
 *  Links:
 *
 *  Graph Dracula JavaScript Framework:
 *      http://graphdracula.net
 *
 /*--------------------------------------------------------------------------*/
var AbstractEdge=function(){};AbstractEdge.prototype={hide:function(){this.connection.fg.hide();this.connection.bg&&this.bg.connection.hide()}};var EdgeFactory=function(){this.template=new AbstractEdge;this.template.style={};this.template.style.directed=!1;this.template.weight=1};EdgeFactory.prototype={build:function(a,b){var d=jQuery.extend(!0,{},this.template);d.source=a;d.target=b;return d}};var Graph=function(){this.nodes={};this.edges=[];this.snapshots=[];this.edgeFactory=new EdgeFactory};
Graph.prototype={addNode:function(a,b){void 0==this.nodes[a]&&(this.nodes[a]=new Graph.Node(a,b));return this.nodes[a]},addEdge:function(a,b,d){var a=this.addNode(a),b=this.addNode(b),e=this.edgeFactory.build(a,b);jQuery.extend(e.style,d);a.edges.push(e);this.edges.push(e);b.edges.push(e)},snapShot:function(){},removeNode:function(a){delete this.nodes[a];for(var b=0;b<this.edges.length;b++)if(this.edges[b].source.id==a||this.edges[b].target.id==a)this.edges.splice(b,1),b--}};
Graph.Node=function(a,b){b=b||{};b.id=a;b.edges=[];b.hide=function(){this.hidden=!0;this.shape&&this.shape.hide();for(i in this.edges)(this.edges[i].source.id==a||this.edges[i].target==a)&&this.edges[i].hide&&this.edges[i].hide()};b.show=function(){this.hidden=!1;this.shape&&this.shape.show();for(i in this.edges)(this.edges[i].source.id==a||this.edges[i].target==a)&&this.edges[i].show&&this.edges[i].show()};return b};Graph.Node.prototype={};Graph.Renderer={};
Graph.Renderer.Raphael=function(a,b,d,e){this.width=d||400;this.height=e||400;var c=this;this.r=Raphael(a,this.width,this.height);this.radius=40;this.graph=b;this.mouse_in=!1;if(!this.graph.render)this.graph.render=function(){};this.isDrag=!1;this.dragger=function(a){this.dx=a.clientX;this.dy=a.clientY;c.isDrag=this;this.set&&this.set.animate({"fill-opacity":0.1},200)&&this.set.toFront();a.preventDefault&&a.preventDefault()};a=document.getElementById(a);a.onmousemove=function(a){a=a||window.event;
if(c.isDrag){var b=c.isDrag.set.getBBox(),d=a.clientX-c.isDrag.dx+(b.x+b.width/2),b=a.clientY-c.isDrag.dy+(b.y+b.height/2),d=a.clientX-(20>d?d-20:d>c.width-20?d-c.width+20:0),a=a.clientY-(20>b?b-20:b>c.height-20?b-c.height+20:0);c.isDrag.set.translate(d-Math.round(c.isDrag.dx),a-Math.round(c.isDrag.dy));for(var e in c.graph.edges)c.graph.edges[e].connection&&c.graph.edges[e].connection.draw();c.isDrag.dx=d;c.isDrag.dy=a}};a.onmouseup=function(){c.isDrag&&c.isDrag.set.animate({"fill-opacity":0.6},
500);c.isDrag=!1};this.draw()};
Graph.Renderer.Raphael.prototype={translate:function(a){return[(a[0]-this.graph.layoutMinX)*this.factorX+this.radius,(a[1]-this.graph.layoutMinY)*this.factorY+this.radius]},rotate:function(a,b,d){var e=b*Math.cos(d),b=b*Math.sin(d);return[a[0]+e,a[1]+b]},draw:function(){this.factorX=(this.width-2*this.radius)/(this.graph.layoutMaxX-this.graph.layoutMinX);this.factorY=(this.height-2*this.radius)/(this.graph.layoutMaxY-this.graph.layoutMinY);for(a in this.graph.nodes)this.drawNode(this.graph.nodes[a]);for(var a=
0;a<this.graph.edges.length;a++)this.drawEdge(this.graph.edges[a])},drawNode:function(a){var b=this.translate([a.layoutPosX,a.layoutPosY]);a.point=b;if(a.shape){var d=a.shape.getBBox(),e=d.y+d.height/2;a.shape.translate(Math.round(b[0]-(d.x+d.width/2)),Math.round(b[1]-e));this.r.safari();return a}var c;if(!a.render)a.render=function(a,b){var d=Raphael.getColor(),d=a.ellipse(0,0,30,20).attr({fill:d,stroke:d,"stroke-width":2});d.node.id=b.label||b.id;return c=a.set().push(d).push(a.text(0,30,b.label||
b.id))};c=a.render(this.r,a).hide();c.attr({"fill-opacity":0.6});c.items.forEach(function(a){a.set=c;a.node.style.cursor="move"});c.mousedown(this.dragger);d=c.getBBox();c.translate(Math.round(b[0]-(d.x+d.width/2)),Math.round(b[1]-(d.y+d.height/2)));a.hidden||c.show();a.shape=c},drawEdge:function(a){if(!a.backedge)a.source.hidden||a.target.hidden?a.connection&&a.connection.fg.hide()|a.connection.bg&&a.connection.bg.hide():a.connection?(a.connection.fg.show(),a.connection.bg&&a.connection.bg.show(),
a.connection.draw()):(a.style&&a.style.callback&&a.style.callback(a),a.connection=this.r.connection(a.source.shape,a.target.shape,a.style))}};Graph.Layout={};Graph.Layout.Spring=function(a){this.graph=a;this.iterations=500;this.maxRepulsiveForceDistance=6;this.k=2;this.c=0.01;this.maxVertexMovement=0.5;this.layout()};
Graph.Layout.Spring.prototype={layout:function(){this.layoutPrepare();for(var a=0;a<this.iterations;a++)this.layoutIteration();this.layoutCalcBounds()},layoutPrepare:function(){for(i in this.graph.nodes){var a=this.graph.nodes[i];a.layoutPosX=0;a.layoutPosY=0;a.layoutForceX=0;a.layoutForceY=0}},layoutCalcBounds:function(){var a=Infinity,b=-Infinity,d=Infinity,e=-Infinity;for(i in this.graph.nodes){var c=this.graph.nodes[i].layoutPosX,f=this.graph.nodes[i].layoutPosY;c>b&&(b=c);c<a&&(a=c);f>e&&(e=
f);f<d&&(d=f)}this.graph.layoutMinX=a;this.graph.layoutMaxX=b;this.graph.layoutMinY=d;this.graph.layoutMaxY=e},layoutIteration:function(){var a=[],b;for(b in this.graph.nodes){var d=this.graph.nodes[b],e;for(e in a)this.layoutRepulsive(d,this.graph.nodes[a[e]]);a.push(b)}for(a=0;a<this.graph.edges.length;a++)this.layoutAttractive(this.graph.edges[a]);for(a in this.graph.nodes){b=this.graph.nodes[a];d=this.c*b.layoutForceX;e=this.c*b.layoutForceY;var c=this.maxVertexMovement;d>c&&(d=c);d<-c&&(d=-c);
e>c&&(e=c);e<-c&&(e=-c);b.layoutPosX+=d;b.layoutPosY+=e;b.layoutForceX=0;b.layoutForceY=0}},layoutRepulsive:function(a,b){if(!("undefined"==typeof a||"undefined"==typeof b)){var d=b.layoutPosX-a.layoutPosX,e=b.layoutPosY-a.layoutPosY,c=d*d+e*e;0.01>c&&(d=0.1*Math.random()+0.1,e=0.1*Math.random()+0.1,c=d*d+e*e);c=Math.sqrt(c);if(c<this.maxRepulsiveForceDistance){var f=this.k*this.k/c;b.layoutForceX+=f*d/c;b.layoutForceY+=f*e/c;a.layoutForceX-=f*d/c;a.layoutForceY-=f*e/c}}},layoutAttractive:function(a){var b=
a.source,d=a.target,e=d.layoutPosX-b.layoutPosX,c=d.layoutPosY-b.layoutPosY,f=e*e+c*c;0.01>f&&(e=0.1*Math.random()+0.1,c=0.1*Math.random()+0.1,f=e*e+c*c);var g=Math.sqrt(f);if(g>this.maxRepulsiveForceDistance)g=this.maxRepulsiveForceDistance,f=g*g;f=(f-this.k*this.k)/this.k;if(void 0==a.attraction)a.attraction=1;f*=0.5*Math.log(a.attraction)+1;d.layoutForceX-=f*e/g;d.layoutForceY-=f*c/g;b.layoutForceX+=f*e/g;b.layoutForceY+=f*c/g}};Graph.Layout.Ordered=function(a,b){this.graph=a;this.order=b;this.layout()};
Graph.Layout.Ordered.prototype={layout:function(){this.layoutPrepare();this.layoutCalcBounds()},layoutPrepare:function(){for(i in this.graph.nodes){var a=this.graph.nodes[i];a.layoutPosX=0;a.layoutPosY=0}var b=0;for(i in this.order)a=this.order[i],a.layoutPosX=b,a.layoutPosY=Math.random(),b++},layoutCalcBounds:function(){var a=Infinity,b=-Infinity,d=Infinity,e=-Infinity;for(i in this.graph.nodes){var c=this.graph.nodes[i].layoutPosX,f=this.graph.nodes[i].layoutPosY;c>b&&(b=c);c<a&&(a=c);f>e&&(e=f);
f<d&&(d=f)}this.graph.layoutMinX=a;this.graph.layoutMaxX=b;this.graph.layoutMinY=d;this.graph.layoutMaxY=e}};function log(a){console.log&&console.log(a)}Raphael.el.tooltip=function(a){this.tp=a;this.tp.o={x:0,y:0};this.tp.hide();this.hover(function(){this.mousemove(function(a){this.tp.translate(a.clientX-this.tp.o.x,a.clientY-this.tp.o.y);this.tp.o={x:a.clientX,y:a.clientY}});this.tp.show().toFront()},function(){this.tp.hide();this.unmousemove()});return this};
if(!Array.prototype.forEach)Array.prototype.forEach=function(a,b){var d=this.length;if("function"!=typeof a)throw new TypeError;for(var e=0;e<d;e++)e in this&&a.call(b,this[e],e,this)};