diff --git a/app.js b/app.js index 2a38c8d..bb36aee 100644 --- a/app.js +++ b/app.js @@ -1,3 +1,4 @@ +var _ = require('lodash'); var express = require('express'); var app = express(); var path = require('path'); @@ -7,8 +8,8 @@ var askedForHistory = false; var askedForHistoryTime = 0; var Primus = require('primus'), - api, - client; + api, + client; var WS_SECRET = process.env.WS_SECRET || "eth-net-stats-has-a-secret"; @@ -18,18 +19,18 @@ var Nodes = new Collection(); var server = require('http').createServer(app); api = new Primus(server, { - transformer: 'websockets', - pathname: '/api', - parser: 'JSON' + transformer: 'websockets', + pathname: '/api', + parser: 'JSON' }); api.use('emit', require('primus-emit')); api.use('spark-latency', require('primus-spark-latency')); var client = new Primus(server, { - transformer: 'websockets', - pathname: '/primus', - parser: 'JSON' + transformer: 'websockets', + pathname: '/primus', + parser: 'JSON' }); var clientLatency = 0; @@ -37,118 +38,152 @@ var clientLatency = 0; client.use('emit', require('primus-emit')); api.on('connection', function(spark) { - console.log('Latency: ', spark.latency); - console.log(spark.id); - console.log(spark.address); - console.log(spark.query); + console.log('Latency: ', spark.latency); + console.log(spark.id); + console.log(spark.address); + console.log(spark.query); - spark.on('hello', function(data) - { - console.log('Latency: ', spark.latency); - console.log('Got hello data from ', spark.id); - console.log(data); + spark.on('hello', function(data) + { + console.log('Latency: ', spark.latency); + console.log('Got hello data from ', spark.id); + console.log(data); - if(typeof data.secret === 'undefined' || data.secret !== WS_SECRET) - { - spark.end(undefined, { reconnect: false }); + if( _.isUndefined(data.secret) || data.secret !== WS_SECRET ) + { + spark.end(undefined, { reconnect: false }); - return false; - } + return false; + } - if(typeof data.id !== 'undefined' && typeof data.info !== 'undefined') - { - data.ip = spark.address.ip; - data.spark = spark.id; - data.latency = spark.latency; + if( !_.isUndefined(data.id) && !_.isUndefined(data.info) ) + { + data.ip = spark.address.ip; + data.spark = spark.id; + data.latency = spark.latency; - var info = Nodes.add(data); - spark.emit('ready'); + var info = Nodes.add(data); + spark.emit('ready'); - client.write({action: 'add', data: info}); - client.write({action: 'charts', data: Nodes.getCharts()}); - } - }); + client.write({ + action: 'add', + data: info + }); - spark.on('update', function(data) - { - if(typeof data.id !== 'undefined' && typeof data.stats !== 'undefined') - { - data.stats.latency = spark.latency; + client.write({ + action: 'charts', + data: Nodes.getCharts() + }); + } + }); - var stats = Nodes.update(data.id, data.stats); - if(stats !== false) - { - client.write({action: 'update', data: stats}); - client.write({action: 'charts', data: Nodes.getCharts()}); - } + spark.on('update', function(data) + { + if( !_.isUndefined(data.id) && !_.isUndefined(data.stats) ) + { + data.stats.latency = spark.latency; - if(Nodes.getHistory().requiresUpdate() && Nodes.canNodeUpdate(data.id) && (!askedForHistory || (new Date()).getTime() - askedForHistoryTime > 120000)) - { - var range = Nodes.getHistory().getHistoryRequestInterval(); - console.log("asked " + data.id + " for history"); - console.log('interval', range); - spark.emit('history', range); - askedForHistory = true; - askedForHistoryTime = (new Date()).getTime(); - } - } - }); + var stats = Nodes.update(data.id, data.stats); - spark.on('history', function(data){ - console.log("got history from " + data.id); - client.write({action: 'charts', data: Nodes.addHistory(data.id, data.history)}); - askedForHistory = false; - }); + if(stats !== false) + { + client.write({ + action: 'update', + data: stats + }); - spark.on('node-ping', function(data) - { - spark.emit('node-pong'); - }); + client.write({ + action: 'charts', + data: Nodes.getCharts() + }); + } + } + }); - spark.on('latency', function(data) - { - if(typeof data.id !== 'undefined') - { - var stats = Nodes.updateLatency(data.id, data.latency); + spark.on('history', function(data) + { + console.log("got history from " + data.id); - client.write({action: 'latency', data: stats}); + client.write({ + action: 'charts', + data: Nodes.addHistory(data.id, data.history) + }); - if(Nodes.getHistory().requiresUpdate() && Nodes.canNodeUpdate(data.id) && (!askedForHistory || (new Date()).getTime() - askedForHistoryTime > 120000)) - { - var range = Nodes.getHistory().getHistoryRequestInterval(); - console.log("asked " + data.id + " for history"); - console.log('interval', range); - spark.emit('history', range); - askedForHistory = true; - askedForHistoryTime = (new Date()).getTime(); - } - } - }); + askedForHistory = false; - spark.on('end', function(data) - { - var stats = Nodes.inactive(spark.id); + client.write({ + action: 'charts', + data: Nodes.getCharts() + }); + }); - client.write({action: 'inactive', data: stats}); - }); + spark.on('node-ping', function(data) + { + spark.emit('node-pong'); + }); + + spark.on('latency', function(data) + { + if( !_.isUndefined(data.id) ) + { + var stats = Nodes.updateLatency(data.id, data.latency); + + client.write({ + action: 'latency', + data: stats + }); + + if( Nodes.requiresUpdate(data.id) && (!askedForHistory || _.now() - askedForHistoryTime > 200000) ) + { + var range = Nodes.getHistory().getHistoryRequestRange(); + + console.log("asked " + data.id + " for history"); + console.log('interval', range); + + spark.emit('history', range); + + askedForHistory = true; + askedForHistoryTime = _.now(); + } + } + }); + + spark.on('end', function(data) + { + var stats = Nodes.inactive(spark.id); + + client.write({ + action: 'inactive', + data: stats + }); + }); }); -client.on('connection', function(spark) { - spark.on('ready', function(data){ - spark.emit('init', {nodes: Nodes.all()}); +client.on('connection', function(clientSpark) +{ + clientSpark.on('ready', function(data) + { + clientSpark.emit('init', { nodes: Nodes.all() }); - spark.write({action: 'charts', data: Nodes.getCharts()}); - }); + clientSpark.write({ + action: 'charts', + data: Nodes.getCharts() + }); + }); - spark.on('client-pong', function(data) { - var latency = Math.ceil(((new Date()).getTime() - clientLatency)/2); - spark.emit('client-latency', { latency: latency }); - }); + clientSpark.on('client-pong', function(data) + { + var latency = Math.ceil( (_.now() - clientLatency) / 2 ); + + clientSpark.emit('client-latency', { latency: latency }); + }); }); -var latencyTimeout = setInterval(function(){ - clientLatency = (new Date()).getTime(); - client.write({action: 'client-ping'}); +var latencyTimeout = setInterval( function () +{ + clientLatency = _.now(); + + client.write({ action: 'client-ping' }); }, 5000); // view engine setup @@ -165,29 +200,29 @@ app.get('/', function(req, res) { // catch 404 and forward to error handler app.use(function(req, res, next) { - var err = new Error('Not Found'); - err.status = 404; - next(err); + var err = new Error('Not Found'); + err.status = 404; + next(err); }); // error handlers if (app.get('env') === 'development') { - app.use(function(err, req, res, next) { - res.status(err.status || 500); - res.render('error', { - message: err.message, - error: err - }); - }); + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); } // production error handler app.use(function(err, req, res, next) { - res.status(err.status || 500); - res.render('error', { - message: err.message, - error: {} - }); + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); }); server.listen(process.env.PORT || 3000); diff --git a/models/collection.js b/models/collection.js index 819993c..7f55d05 100644 --- a/models/collection.js +++ b/models/collection.js @@ -4,9 +4,8 @@ var Node = require('./node'); var Collection = function Collection() { - this._list = []; - this._history = new Blockchain(); - this._bestBlock = null; + this._items = []; + this._blockchain = new Blockchain(); return this; } @@ -26,12 +25,12 @@ Collection.prototype.update = function(id, stats) if(!node) return false; - var block = this._history.add(stats.block, id); + var block = this._blockchain.add(stats.block, id); if(! block) return false; - var propagationHistory = this._history.getNodePropagation(id); + var propagationHistory = this._blockchain.getNodePropagation(id); stats.block.arrived = block.arrived; stats.block.received = block.received; @@ -47,8 +46,10 @@ Collection.prototype.addHistory = function(id, blocks) if(!node) return false; - for (var i = blocks.length - 1; i >= 0; i--) { - this._history.add(blocks[i], id); + console.log(blocks); + for (var i = 0; i <= blocks.length - 1; i++) + { + this._blockchain.add(blocks[i], id); }; return this.getCharts(); @@ -78,7 +79,7 @@ Collection.prototype.inactive = function(id) Collection.prototype.getIndex = function(search) { - return _.findIndex(this._list, search); + return _.findIndex(this._items, search); } Collection.prototype.getNode = function(search) @@ -86,15 +87,15 @@ Collection.prototype.getNode = function(search) var index = this.getIndex(search); if(index >= 0) - return this._list[index]; + return this._items[index]; return false; } Collection.prototype.getNodeByIndex = function(index) { - if(this._list[index]) - return this._list[index]; + if(this._items[index]) + return this._items[index]; return false; } @@ -103,7 +104,7 @@ Collection.prototype.getIndexOrNew = function(search, data) { var index = this.getIndex(search); - return (index >= 0 ? index : this._list.push(new Node(data)) - 1); + return (index >= 0 ? index : this._items.push(new Node(data)) - 1); } Collection.prototype.getNodeOrNew = function(search, data) @@ -113,27 +114,27 @@ Collection.prototype.getNodeOrNew = function(search, data) Collection.prototype.all = function() { - return this._list; + return this._items; } Collection.prototype.blockPropagationChart = function() { - return this._history.getBlockPropagation(); + return this._blockchain.getBlockPropagation(); } Collection.prototype.getUncleCount = function() { - return this._history.getUncleCount(); + return this._blockchain.getUncleCount(); } Collection.prototype.getCharts = function() { - return this._history.getCharts(); + return this._blockchain.getCharts(); } Collection.prototype.getHistory = function() { - return this._history; + return this._blockchain; } Collection.prototype.canNodeUpdate = function(id) @@ -145,13 +146,17 @@ Collection.prototype.canNodeUpdate = function(id) if(node.canUpdate()) { - var diff = this._history.bestBlockNumber() - node.getBlockNumber(); + var diff = this._blockchain.bestBlockNumber() - node.getBlockNumber(); - if(diff <= 0) - return true; + return (diff <= 0); } return false; } +Collection.prototype.requiresUpdate = function(id) +{ + return ( this.canNodeUpdate(id) && this._blockchain.requiresUpdate() ); +} + module.exports = Collection; \ No newline at end of file diff --git a/models/history.js b/models/history.js index d190b58..deb1d89 100644 --- a/models/history.js +++ b/models/history.js @@ -33,26 +33,30 @@ var History = function History(data) History.prototype.add = function(block, id) { - if(typeof block !== 'undefined' && typeof block.number !== 'undefined' && typeof block.uncles !== 'undefined' && typeof block.transactions !== 'undefined' && typeof block.difficulty !== 'undefined') + if( !_.isUndefined(block) && !_.isUndefined(block.number) && !_.isUndefined(block.uncles) && !_.isUndefined(block.transactions) && !_.isUndefined(block.difficulty) ) { var historyBlock = this.search(block.number); - var now = (new Date()).getTime(); + var now = _.now(); block.arrived = now; block.received = now; block.propagation = 0; - if(historyBlock) + if( historyBlock ) { - var propIndex = _.findIndex(historyBlock.propagTimes, {node: id}); + var propIndex = _.findIndex( historyBlock.propagTimes, {node: id} ); - if(propIndex === -1) + if( propIndex === -1 ) { block.arrived = historyBlock.block.arrived; block.received = now; block.propagation = now - historyBlock.block.received; - historyBlock.propagTimes.push({node: id, received: now, propagation: block.propagation}); + historyBlock.propagTimes.push({ + node: id, + received: now, + propagation: block.propagation + }); } else { @@ -65,26 +69,32 @@ History.prototype.add = function(block, id) { var prevBlock = this.prevMaxBlock(block.number); - if(prevBlock) + if( prevBlock ) { block.time = block.arrived - prevBlock.block.arrived; if(block.number < this.bestBlock().height) - block.time = (block.timestamp - prevBlock.block.timestamp)*1000; + block.time = (block.timestamp - prevBlock.block.timestamp) * 1000; } else { block.time = 0; } + var item = { height: block.number, block: block, propagTimes: [] } - if(this._items.length === 0 || block.number >= (this.bestBlock().height - MAX_HISTORY + 1)) + if( this._items.length === 0 || block.number >= (this.bestBlockNumber() - MAX_HISTORY + 1) ) { - item.propagTimes.push({node: id, received: now, propagation: block.propagation}); + item.propagTimes.push({ + node: id, + received: now, + propagation: block.propagation + }); + this._save(item); } } @@ -99,16 +109,17 @@ History.prototype._save = function(block) { this._items.unshift(block); - if(this._items.length > MAX_HISTORY){ + if(this._items.length > MAX_HISTORY) + { this._items.pop(); } - this._items = _.sortByOrder(this._items, 'height', false); + this._items = _.sortByOrder( this._items, 'height', false ); } History.prototype.search = function(number) { - var index = _.findIndex(this._items, {height: number}); + var index = _.findIndex( this._items, {height: number} ); if(index < 0) return false; @@ -137,7 +148,7 @@ History.prototype.bestBlockNumber = function() { var best = this.bestBlock(); - if(typeof best.height !== 'undefined') + if( !_.isUndefined(best.height) ) return best.height; return 0; @@ -145,23 +156,22 @@ History.prototype.bestBlockNumber = function() History.prototype.getNodePropagation = function(id) { - var propagation = new Array(MAX_PEER_PROPAGATION); - var bestBlock = this.bestBlock().height; - + var propagation = new Array( MAX_PEER_PROPAGATION ); + var bestBlock = this.bestBlockNumber(); _.fill(propagation, -1); - var sorted = _(this._items) - .sortByOrder('height', false) - .slice(0, MAX_PEER_PROPAGATION) + var sorted = _( this._items ) + .sortByOrder( 'height', false ) + .slice( 0, MAX_PEER_PROPAGATION ) .reverse() - .forEach(function(n, key) + .forEach(function (item, key) { - var index = MAX_PEER_PROPAGATION - 1 - bestBlock + n.height; + var index = MAX_PEER_PROPAGATION - 1 - bestBlock + item.height; if(index > 0) { - propagation[index] = _.result(_.find(n.propagTimes, 'node', id), 'propagation', -1); + propagation[index] = _.result(_.find(item.propagTimes, 'node', id), 'propagation', -1); } }) .value(); @@ -174,9 +184,9 @@ History.prototype.getBlockPropagation = function() var propagation = []; var avgPropagation = 0; - _.forEach(this._items, function(n, key) + _.forEach(this._items, function (n, key) { - _.forEach(n.propagTimes, function(p, i) + _.forEach(n.propagTimes, function (p, i) { var prop = _.result(p, 'propagation', -1); @@ -187,57 +197,69 @@ History.prototype.getBlockPropagation = function() if(propagation.length > 0) { - var avgPropagation = Math.round(_.sum(propagation) / propagation.length); + var avgPropagation = Math.round( _.sum(propagation) / propagation.length ); } var x = d3.scale.linear() - .domain([MIN_PROPAGATION_RANGE, MAX_PROPAGATION_RANGE]) - .interpolate(d3.interpolateRound); + .domain([ MIN_PROPAGATION_RANGE, MAX_PROPAGATION_RANGE ]) + .interpolate( d3.interpolateRound ); var data = d3.layout.histogram() - .frequency(false) - .bins(x.ticks(MAX_BINS)) - (propagation); + .frequency( false ) + .bins( x.ticks(MAX_BINS) ) + ( propagation ); var freqCum = 0; - var histogram = data.map(function(val) { + var histogram = data.map(function (val) { freqCum += val.length; - var cumPercent = (freqCum / Math.max(1, propagation.length)); - return {x: val.x, dx: val.dx, y: val.y, frequency: val.length, cumulative: freqCum, cumpercent: cumPercent}; + var cumPercent = ( freqCum / Math.max(1, propagation.length) ); + + return { + x: val.x, + dx: val.dx, + y: val.y, + frequency: val.length, + cumulative: freqCum, + cumpercent: cumPercent + }; }); - return {histogram: histogram, avg: avgPropagation}; + return { + histogram: histogram, + avg: avgPropagation + }; } History.prototype.getUncleCount = function() { - var uncles = _(this._items) - .sortByOrder('height', false) - .map(function(item) + var uncles = _( this._items ) + .sortByOrder( 'height', false ) + .map(function (item) { return item.block.uncles.length; }) .value(); - var uncleBins = _.fill(Array(MAX_BINS), 0); + var uncleBins = _.fill( Array(MAX_BINS), 0 ); - var sumMapper = function(array, key) { + var sumMapper = function (array, key) + { uncleBins[key] = _.sum(array); return _.sum(array); }; - _.map(_.chunk(uncles, MAX_UNCLES_PER_BIN), sumMapper); + _.map(_.chunk( uncles, MAX_UNCLES_PER_BIN ), sumMapper); return uncleBins; } History.prototype.getBlockTimes = function() { - var blockTimes = _(this._items) - .sortByOrder('height', false) + var blockTimes = _( this._items ) + .sortByOrder( 'height', false ) .slice(0, MAX_BINS) .reverse() - .map(function(item) + .map(function (item) { return item.block.time; }) @@ -248,11 +270,11 @@ History.prototype.getBlockTimes = function() History.prototype.getDifficulty = function() { - var difficultyHistory = _(this._items) - .sortByOrder('height', false) + var difficultyHistory = _( this._items ) + .sortByOrder( 'height', false ) .slice(0, MAX_BINS) .reverse() - .map(function(item) + .map(function (item) { return item.block.difficulty; }) @@ -263,11 +285,11 @@ History.prototype.getDifficulty = function() History.prototype.getTransactionsCount = function() { - var txCount = _(this._items) - .sortByOrder('height', false) + var txCount = _( this._items ) + .sortByOrder( 'height', false ) .slice(0, MAX_BINS) .reverse() - .map(function(item) + .map(function (item) { return item.block.transactions.length; }) @@ -278,11 +300,11 @@ History.prototype.getTransactionsCount = function() History.prototype.getGasSpending = function() { - var gasSpending = _(this._items) - .sortByOrder('height', false) + var gasSpending = _( this._items ) + .sortByOrder( 'height', false ) .slice(0, MAX_BINS) .reverse() - .map(function(item) + .map(function (item) { return item.block.gasUsed; }) @@ -293,51 +315,50 @@ History.prototype.getGasSpending = function() History.prototype.getAvgHashrate = function() { - if(this._items.length === 0) + if( _.isEmpty(this._items) ) return 0; - var difficultyHistory = _(this._items) - .map(function(item) + var difficultyHistory = _( this._items ) + .map(function (item) { return item.block.difficulty; }) .value(); - var avgDifficulty = _.sum(difficultyHistory)/difficultyHistory.length; + var avgDifficulty = _.sum(difficultyHistory) / difficultyHistory.length; - var blocktimeHistory = _(this._items) - .map(function(item) + var blocktimeHistory = _( this._items ) + .map(function (item) { return item.block.time; }) .value(); - var avgBlocktime = _.sum(blocktimeHistory)/blocktimeHistory.length; + var avgBlocktime = _.sum(blocktimeHistory) / blocktimeHistory.length; - return avgDifficulty/1000 * 12 * (12/avgBlocktime); + return avgDifficulty / 1000 * 12 * ( 12 / avgBlocktime ); } History.prototype.getCharts = function() { - var chartHistory = _(this._items) - .sortByOrder('height', false) + var chartHistory = _( this._items ) + .sortByOrder( 'height', false ) .slice(0, MAX_BINS) .reverse() - .map(function(item) + .map(function (item) { - var chart = { + return { height: item.height, - blocktime: item.block.time/1000, + blocktime: item.block.time / 1000, difficulty: item.block.difficulty, uncles: item.block.uncles.length, transactions: item.block.transactions.length, gasSpending: item.block.gasUsed - } - return chart; + }; }) .value(); - var chart = { + return { height: _.pluck(chartHistory, 'height'), blocktime: _.pluck(chartHistory, 'blocktime'), avgBlocktime: _.sum(_.pluck(chartHistory, 'blocktime')) / (chartHistory.length === 0 ? 1 : chartHistory.length), @@ -348,28 +369,29 @@ History.prototype.getCharts = function() propagation: this.getBlockPropagation(), uncleCount: this.getUncleCount(), avgHashrate: this.getAvgHashrate() - } - - return chart; -} - -History.prototype.history = function() -{ - return this._items; + }; } History.prototype.requiresUpdate = function() { - return ! (this._items.length === MAX_HISTORY); + return ( this._items.length < MAX_HISTORY && !_.isEmpty(this._items) ); } -History.prototype.getHistoryRequestInterval = function() +History.prototype.getHistoryRequestRange = function() { - if(this._items.length === 0) - return null; + if( _.isEmpty(this._items) ) + return false; + + var blocks = _.pluck( this._items, 'height' ); + var best = _.max( blocks ); + var range = _.range( _.max([ 0, best - MAX_HISTORY ]), best + 1); + + var missing = _.difference( range, blocks ); + + console.log('missing', missing.join(', ')); var max = _.min(this._items, 'height').height - 1; - var min = max - Math.min(50, (MAX_HISTORY - this._items.length + 1)) + 1; + var min = max - Math.min( 50, (MAX_HISTORY - this._items.length + 1) ) + 1; return {max: max, min: min}; } diff --git a/models/node.js b/models/node.js index a9ae4b6..5002af2 100644 --- a/models/node.js +++ b/models/node.js @@ -20,6 +20,7 @@ var Node = function Node(data) number: 0, gasLimit: 0, timestamp: 0, + time: 0, arrival: 0, propagation: 0, received: 0, @@ -34,79 +35,91 @@ var Node = function Node(data) uptime: 0, lastUpdate: 0 }; + this.history = new Array(MAX_HISTORY); + this.uptime = { started: null, history: [] }; + this.init(data); + + return this; +} + +Node.prototype.init = function(data) +{ _.fill(this.history, -1); - if(this.id === null) { - this.uptime.started = (new Date()).getTime(); + if( this.id === null ) + { + this.uptime.started = _.now(); this.setState(true); } - if(typeof data.id !== 'undefined') + if( !_.isUndefined(data.id) ) this.id = data.id; - if(typeof data.info !== 'undefined') { - this.info = data.info; + this.setInfo(data); - if(typeof data.info.canUpdateHistory === 'undefined') - data.info.canUpdateHistory = false; - } - - if(typeof data.ip !== 'undefined'){ - if(data.ip === '::ffff:127.0.0.1') - data.ip = '84.117.82.122'; - this.info.ip = data.ip; - this.setGeo(data.ip); - } - - if(typeof data.spark !== 'undefined') - this.spark = data.spark; - - if(typeof data.latency !== 'undefined') + if( !_.isUndefined(data.latency) ) this.stats.latency = data.latency; return this; } -Node.prototype.setGeo = function(ip) -{ - this.geo = geoip.lookup(ip); -} - Node.prototype.setInfo = function(data) { - if(typeof data.info !== 'undefined') { + if( !_.isUndefined(data.info) ) + { this.info = data.info; - if(typeof data.info.canUpdateHistory === 'undefined') + if( _.isUndefined(data.info.canUpdateHistory) ) data.info.canUpdateHistory = false; } - if(typeof data.ip !== 'undefined'){ + if( !_.isUndefined(data.ip) ) + { + if(data.ip === '::ffff:127.0.0.1') + data.ip = '84.117.82.122'; + this.info.ip = data.ip; - this.setGeo(data.ip); + this.setGeo(); } - if(typeof data.spark !== 'undefined') + if( !_.isUndefined(data.spark) ) this.spark = data.spark; - if(this.uptime.history.length > 0 && this.uptime.history[this.uptime.history.length - 1].status == 'down') + var uptimeCnt = this.uptime.history.length; + + if( uptimeCnt > 0 && this.uptime.history[uptimeCnt - 1].status === 'down' ) this.setState(true); + + return this; +} + +Node.prototype.setGeo = function() +{ + this.geo = geoip.lookup(this.info.ip); + + return this; } Node.prototype.getInfo = function() { - return {id: this.id, info: this.info, geo: this.geo, stats: this.stats, history: this.history}; + return { + id: this.id, + info: this.info, + geo: this.geo, + stats: this.stats, + history: this.history + }; } Node.prototype.setStats = function(stats, history) { - if(typeof stats !== 'undefined' && typeof stats.block !== 'undefined' && typeof stats.block.number !== 'undefined') + if( !_.isUndefined(stats) && !_.isUndefined(stats.block) && !_.isUndefined(stats.block.number) ) { this.history = history; @@ -115,7 +128,7 @@ Node.prototype.setStats = function(stats, history) }); if(positives.length > 0) - stats.propagationAvg = Math.round(_.sum(positives)/positives.length); + stats.propagationAvg = Math.round( _.sum(positives) / positives.length ); else stats.propagationAvg = 0; @@ -129,11 +142,14 @@ Node.prototype.setStats = function(stats, history) Node.prototype.setLatency = function(latency) { - if(typeof latency !== 'undefined') + if( !_.isUndefined(latency) ) { this.stats.latency = latency; - return { id: this.id, latency: latency }; + return { + id: this.id, + latency: latency + }; } return false; @@ -141,13 +157,20 @@ Node.prototype.setLatency = function(latency) Node.prototype.getStats = function() { - return {id: this.id, stats: this.stats, history: this.history}; + return { + id: this.id, + stats: this.stats, + history: this.history + }; } Node.prototype.setState = function(active) { this.stats.active = active; - this.uptime.history.push({status: (active ? 'up' : 'down'), time: (new Date()).getTime()}); + this.uptime.history.push({ + status: (active ? 'up' : 'down'), + time: _.now() + }); } Node.prototype.getBlockNumber = function() diff --git a/public/css/style.css b/public/css/style.css index e290851..be8cefe 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -130,6 +130,13 @@ span.small-title span.small { font-size: 50px; line-height: 55px; letter-spacing: -4px; + word-spacing: nowrap !important; +} + +.big-info .big-details-holder { + position: absolute; + top: 15px; + left: 99px; } .big-info.chart { diff --git a/views/index.jade b/views/index.jade index b7b7176..3a24542 100644 --- a/views/index.jade +++ b/views/index.jade @@ -7,7 +7,7 @@ block content div.big-info.bestblock.text-info div.pull-left.icon-full-width i.icon-block - div.pull-left + div.big-details-holder span.small-title best block span.big-details {{'#'}}{{ bestBlock | number}} div.clearfix @@ -15,7 +15,7 @@ block content div.big-info.uncleCount.text-info div.pull-left.icon-full-width i.icon-uncle - div.pull-left + div.big-details-holder span.small-title uncles #[ ] span.small (current / last 50) span.big-details {{ bestStats.block.uncles.length }}/{{ uncleCount }} @@ -24,7 +24,7 @@ block content div.big-info.blocktime(class="{{ lastBlock | timeClass : true }}") div.pull-left.icon-full-width i.icon-time - div.pull-left + div.big-details-holder span.small-title last block span.big-details {{ lastBlock | blockTimeFilter }} div.clearfix @@ -32,7 +32,7 @@ block content div.big-info.avgblocktime(class="{{ avgBlockTime | avgTimeClass }}") div.pull-left.icon-full-width i.icon-gas - div.pull-left + div.big-details-holder span.small-title avg block time span.big-details {{ avgBlockTime | avgTimeFilter }} div.clearfix @@ -40,7 +40,7 @@ block content div.big-info.difficulty.text-orange div.pull-left.icon-full-width i.icon-hashrate - div.pull-left + div.big-details-holder span.small-title avg network hashrate span.big-details {{ avgHashrate | number : 1 }} MH/s div.clearfix @@ -48,7 +48,7 @@ block content div.big-info.difficulty.text-danger div.pull-left.icon-full-width i.icon-difficulty - div.pull-left + div.big-details-holder span.small-title difficulty span.big-details {{ lastDifficulty | number }} div.clearfix