var graphics = Viva.Graph.View.webglGraphics(); var isWebgl = graphics.isSupported(); if (!isWebgl) { alert("Turn on webgl or use modern browser"); } var graph = Viva.Graph.graph(), layout = Viva.Graph.Layout.forceDirected(graph, { springLength : 80, springCoeff : 0.0002, dragCoeff : 0.009, gravity : -30, theta : 0.7 }), minNodeSize = 0.1, maxNodeSize = 100, txCount1=0, txCount10=0, txCount50=0, txCount100=0, txCount1000=0, totalBTC=0; function log10(val) { return Math.log(val) / Math.LN10; } function log2(val) { return Math.log(val) / Math.LN2; } var scaleType = "LOG"; // LINEAR var getNodeColor = function(node) { // here different colors for tx, input, output, mixed and txconfirmed if(node.data && node.data.t && node.data.t == "i"){ return 0x00FF00; }else if(node.data && node.data.t && node.data.t == "o"){ return 0xFF0000; } return 0x008ED2; }, getNodeSize = function(node){ if(! node.data || !node.data.s){ return 25; } var rmin = 20; var rmax = 96; // linear normalization to a range rmin,rmax if(scaleType == "LINEAR"){ return rmin + (rmax - rmin) * ( (node.data.s - minNodeSize)/(maxNodeSize - minNodeSize) ) ; }else{ // log normalization to a range rmin,rmax var min = log2(minNodeSize); var max = log2(maxNodeSize); var val = log2( node.data.s ); // linear scaling from min.max -> rmin rmax return rmin + (rmax - rmin) * ( (val - min)/(max - min) ) ; } }, getNodeDetails = function(node){ // // http://blockchain.info/rawtx/$tx_index?format=json var label = "transaction"; var id = node.id; if(node.data && node.data.t){ if(node.data.t == "i"){ // input node label = "input"; }else if(node.data.t == "o"){ // output node label = "output"; }else if(node.data.t == "io"){ // node which is both input and output label = "input to output"; } else if(node.data.t == "oi"){ // node which is both input and output label = "output to input"; } // for addresses infor cors not enabled :-( // enabled for blocks var balance = 0; balance=node.data.s; //lets get balance document.getElementById("info").innerHTML = label+"
"+id+"
balance: "+balance +" BTC"; }else{ // transaction node document.getElementById("info").innerHTML = label+"
"+id; } }; // need to get these 2 from yavis.reddit.min.js graphics.setLinkProgram(Viva.Graph.View.webglDualColorLinkProgram()); graphics.setNodeProgram(Viva.Graph.View.webglCustomNodeProgram()); graphics .node(function(node){ var img = Viva.Graph.View.webglSquare(getNodeSize(node), getNodeColor(node)); return img; }) .link(function(link){ var fromColor, toColor; fromColor = toColor = 0x808080; var line = Viva.Graph.View.webglDualColorLine(fromColor, toColor); line.oldStart = fromColor; line.oldEnd = toColor; return line; }); var renderer = Viva.Graph.View.renderer(graph,{ layout : layout, graphics : graphics, container : document.getElementById('g') //prerender : 10 }); var events = Viva.Graph.webglInputEvents(graphics, graph), lastHovered = null, colorLinks = function(node, color) { if (node && node.id) { graph.forEachLinkedNode(node.id, function(node, link){ if (color) { link.ui.start = link.ui.end = color; } else { //link.ui.start = link.ui.oldStart; //link.ui.end =link.ui.oldEnd; link.ui.start = link.ui.end = 0x80808040; } }); } }; events.mouseEnter(function(node){ getNodeDetails(node); colorLinks(lastHovered); lastHovered = node; graph.forEachLinkedNode(node.id, function(node, link){ link.ui.start = link.ui.end = 0xffffffff; graphics.bringLinkToFront(link.ui); }); renderer.rerender(); }).mouseLeave(function(node) { colorLinks(lastHovered); lastHovered = null; colorLinks(node); renderer.rerender(); }); // pause rendere on spacebar var paused = false; $(window).keydown(function(e) { if (e.keyCode === 32) { // toggle on spacebar; e.preventDefault(); paused = !paused; if (paused) { renderer.pause(); } else { renderer.resume(); } } }); var width = $("#g").width(), height= $("#g").height(); renderer.run(); graphics.scale(0.15, {x : width/2, y : height/2}); // websockets part var linksBuffer = []; var wsUri = "wss://ws.blockchain.info/inv"; if (document.location.protocol.indexOf("https") === 0) { wsUri = "wss://ws.blockchain.info/inv"; } var output; function init() { output = document.getElementById("output"); testWebSocket(); } var colorNodes = function(node, color) { if (node && node.id) { graph.forEachNode(function(node){ if (color) { node.ui.color = color; } }); } }; function addNodes(link){ if(link.t == "i"){ var node = graph.getNode(link.from); if( !node ){ graph.addNode(link.from,{s:link.value,t:link.t}); } else { // such a node already exists if(node.data && node.data.t && node.data.t == "o" ){ node.data.t = "io"; node.ui.color = 0xFFFFFF; renderer.rerender(); } } } else if(link.t == "o"){ var node = graph.getNode(link.to); if( ! node){ graph.addNode(link.to,{s:link.value,t:link.t}); } else { // such a node alredy exists. if(node.data && node.data.t && node.data.t == "i"){ node.data.t = "oi"; node.ui.color = 0x9932CC; renderer.rerender(); } } } } function testWebSocket() { websocket = new WebSocket(wsUri); websocket.onopen = function(evt) { onOpen(evt) }; websocket.onclose = function(evt) { onClose(evt) }; websocket.onmessage = function(evt) { onMessage(evt) }; websocket.onerror = function(evt) { onError(evt) }; } function onOpen(evt) { writeToScreen("CONNECTED"); doSend({"op":"unconfirmed_sub"}); } function onClose(evt) { writeToScreen("DISCONNECTED"); } function onMessage(evt) { // parse message var msg = JSON.parse(evt.data); var txHash = msg.x.hash; if(msg.op == "utx"){ // uncorfimed transactions var inputs = msg.x.inputs; var outputs = msg.x.out; // generate from to var links = []; var txValue=0; for(var i=0;i=1&&txValue<10){txCount10++; } else if(txValue>=10&&txValue<50){txCount50++;} else if(txValue50>=50&&txValue<100){txCount100++; } else if(txValue>=100){txCount1000++; } document.getElementById("output2").innerHTML = "Total:"+totalBTC+"
BTC 0-1: "+txCount1+"
BTC 1-10: "+txCount10+"
BTC 10-50: "+txCount50+"
BTC 50-100: "+txCount100+"
BTC 100+: "+txCount1000; } for(var i=0;ifrom: ' + link.from+' to ' +link.to+' value: '+ (link.value/100000000)+''); } //websocket.close(); } function onError(evt) { writeToScreen('ERROR: ' + evt.data); } function doSend(message) { //writeToScreen("SENT: " + JSON.stringify(message)); websocket.send(JSON.stringify(message)); } function writeToScreen(message) { var pre = document.createElement("p"); pre.style.wordWrap = "break-word"; pre.innerHTML = message; output.appendChild(pre); } window.addEventListener("load", init, false); window.l = layout; window.g = graph; window.r = renderer; $("input[name='scaleType']").change(function(){ scaleType = this.value; graph.forEachNode(function(node){ node.ui.size = getNodeSize(node); }) });