From 8954fbae169112f96321185a43f8dfc5e0da9e17 Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 04:04:15 +0200 Subject: [PATCH 01/11] update to primus --- app.js | 119 +++++++++++++++++++++++++++++++------------ models/collection.js | 53 +++++++++++++++++++ models/node.js | 70 +++++++------------------ package.json | 6 ++- 4 files changed, 162 insertions(+), 86 deletions(-) create mode 100644 models/collection.js diff --git a/app.js b/app.js index 88614cd..bb3b348 100644 --- a/app.js +++ b/app.js @@ -1,19 +1,73 @@ var express = require('express.io'); var path = require('path'); var favicon = require('serve-favicon'); -var logger = require('morgan'); var bodyParser = require('body-parser'); -var fs = require('fs'); -var config; - -if(fs.existsSync('./config/nodes.js')){ - config = require('./config/nodes'); -} else { - config = require('./config/nodes.default'); -} - var Node = require('./models/node'); +var Primus = require('primus'), + http = require('http'), + server, + api, + client; + +var Collection = require('./models/collection'); + +var Nodes = new Collection(); + +server = http.createServer(); +api = new Primus(server, { + transformer: 'websockets', + pathname: '/api', + parser: 'JSON' +}); + +api.use('emit', require('primus-emit')); + +api.on('connection', function(spark) { + console.log(spark.id); + console.log(spark.address); + console.log(spark.query); + + spark.on('hello', function(data){ + console.log('got hello data from ' + spark.id); + console.log(data); + Nodes.add(data); + }); + + spark.on('update', function(data){ + console.log('got update from ' + spark.id); + console.log(data); + }); +}); + +var client = new Primus(server, { + transformer: 'websockets', + pathname: '/primus', + parser: 'JSON' +}); + +client.use('emit', require('primus-emit')); + +client.on('connection', function(spark) { + console.log(spark); + + console.log(spark.id); + console.log(spark.headers); + console.log(spark.address); + console.log(spark.query); + spark.write({give: 'identity'}); +}); + +server.listen(process.env.API_PORT || 3000); + +// if(fs.existsSync('./config/nodes.js')){ +// config = require('./config/nodes'); +// } else { +// config = require('./config/nodes.default'); +// } + +// var Node = require('./models/node'); + var app = express(); app.http().io(); @@ -23,37 +77,36 @@ app.set('view engine', 'jade'); // uncomment after placing favicon in /public //app.use(favicon(__dirname + '/public/favicon.ico')); -app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(express.static(path.join(__dirname, 'public'))); -var nodes = [], - nodeStatus = [], - nodeInterval; +// var nodes = [], +// nodeStatus = [], +// nodeInterval; -for(i in config) { - nodes[i] = new Node(config[i], i); - console.log(nodes[i]); - nodeStatus[i] = nodes[i].update(); -} +// for(i in config) { +// nodes[i] = new Node(config[i], i); +// console.log(nodes[i]); +// nodeStatus[i] = nodes[i].update(); +// } -nodeInterval = setInterval(function(){ - for(i in nodes){ - app.io.broadcast('update', nodes[i].update()); - } -}, 10000); +// nodeInterval = setInterval(function(){ +// for(i in nodes){ +// app.io.broadcast('update', nodes[i].update()); +// } +// }, 10000); -app.get('/', function(req, res) { - res.render('index', { title: 'Ethereum Network Status' }); -}); +// app.get('/', function(req, res) { +// res.render('index', { title: 'Ethereum Network Status' }); +// }); -app.io.route('ready', function(req) { - req.io.emit('init', { - nodes: nodeStatus - }); - console.log('emited'); -}); +// app.io.route('ready', function(req) { +// req.io.emit('init', { +// nodes: nodeStatus +// }); +// console.log('emited'); +// }); // catch 404 and forward to error handler app.use(function(req, res, next) { diff --git a/models/collection.js b/models/collection.js new file mode 100644 index 0000000..b18becb --- /dev/null +++ b/models/collection.js @@ -0,0 +1,53 @@ +var _ = require('lodash'); +var Node = require('./node'); + +var Collection = function Collection() +{ + this._list = []; + + return this; +} + +Collection.prototype.add = function(data) +{ + if(typeof data.id == 'undefined') + return false; + + var node = this.getNodeOrNew({ id : data.id }); + + node.info = data.info; +} + +Collection.prototype.get = function(id) +{ + return this.getNode(id); +} + +Collection.prototype.getIndex = function(search) +{ + return _.findIndex(this._list, search); + + return (index >= 0 ? index : false); +} + +Collection.prototype.getNode = function(search) +{ + var index = this.getIndex(search); + + if(index) + return this._list[index]; + + return false; +} + +Collection.prototype.getIndexOrNew = function(search) +{ + var index = this.getIndex(search) || this._list.push(new Node()); +} + +Collection.prototype.getNodeOrNew = function(search) +{ + return this.getNode(this.getIndexOrNew(search)); +} + +module.exports = Collection; \ No newline at end of file diff --git a/models/node.js b/models/node.js index 7acacdd..d69bea1 100644 --- a/models/node.js +++ b/models/node.js @@ -1,68 +1,34 @@ var geoip = require('geoip-lite'); -var Node = function Node(options, id) +var Node = function Node() { - this.options = options; - this.info = { - name: options.name, - ip: options.rpcHost, - type: options.type, - os: options.os - }; - - this.info.geo = geoip.lookup(this.info.ip); - this.info.id = parseInt(id); - this.info.stats = { + this.id = null; + this.info = {}; + this.geo = {} + this.stats = { active: false, - peers: 0, + listening: false, mining: false, - block: { - number: 0, - hash: '?', - timestamp: 0 - }, + peers: 0, + pending: 0, + gasPrice: 0, + block: {}, + blocktimeAvg: 0, + difficulty: [], uptime: { down: 0, inc: 0, total: 0 - } - } - - this.web3 = require('ethereum.js'); + }, + lastUpdate: 0 + }; return this; } -Node.prototype.update = function() +Node.prototype.setGeo = function() { - var sock = new this.web3.providers.HttpSyncProvider('http://' + this.options.rpcHost + ':' + this.options.rpcPort); - this.web3.setProvider(sock); - - var eth = this.web3.eth; - - try { - this.info.stats.peers = eth.peerCount; - } - catch (err) { - this.info.stats.peers = null; - } - - if(this.info.stats.peers != null) { - this.info.stats.block = eth.block(parseInt(eth.number)); - if(this.info.stats.block.hash != '?' && typeof this.info.stats.block.difficulty !== 'undefined'){ - this.info.stats.block.difficulty = this.web3.toDecimal(this.info.stats.block.difficulty); - } - this.info.stats.mining = eth.mining; - this.info.stats.active = true; - } else { - this.info.stats.active = false; - this.info.stats.uptime.down++; - } - - this.info.stats.uptime.inc++; - this.info.stats.uptime.total = ((this.info.stats.uptime.inc - this.info.stats.uptime.down) / this.info.stats.uptime.inc) * 100; - - return this.info; -}; + this.geo = geoip.lookup(ip); +} module.exports = Node; diff --git a/package.json b/package.json index 7200f76..7d8047a 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,12 @@ "express.io": "^1.1.13", "geoip-lite": "^1.1.4", "jade": "~1.6.0", + "lodash": "^3.2.0", "morgan": "~1.3.0", + "primus": "^2.4.12", + "primus-emit": "^0.1.2", "serve-favicon": "~2.1.3", - "stylus": "0.42.3" + "stylus": "0.42.3", + "ws": "^0.7.1" } } From dd0dcd00da3aab9032c89b6d31d1b82cda0e8c6e Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 07:12:44 +0200 Subject: [PATCH 02/11] added socket functionality --- app.js | 116 ++++++++++++++++----------------------- bin/www | 8 +-- models/collection.js | 47 +++++++++++----- models/node.js | 29 +++++++--- public/js/controllers.js | 25 ++++++++- public/js/script.js | 6 -- public/js/services.js | 43 ++++++++------- views/index.jade | 10 ++-- views/layout.jade | 2 +- 9 files changed, 156 insertions(+), 130 deletions(-) diff --git a/app.js b/app.js index bb3b348..0a6e4cf 100644 --- a/app.js +++ b/app.js @@ -1,20 +1,18 @@ -var express = require('express.io'); +var express = require('express'); +var app = express(); var path = require('path'); var favicon = require('serve-favicon'); var bodyParser = require('body-parser'); -var Node = require('./models/node'); var Primus = require('primus'), - http = require('http'), - server, api, client; var Collection = require('./models/collection'); - var Nodes = new Collection(); -server = http.createServer(); +var server = require('http').createServer(app); + api = new Primus(server, { transformer: 'websockets', pathname: '/api', @@ -23,23 +21,6 @@ api = new Primus(server, { api.use('emit', require('primus-emit')); -api.on('connection', function(spark) { - console.log(spark.id); - console.log(spark.address); - console.log(spark.query); - - spark.on('hello', function(data){ - console.log('got hello data from ' + spark.id); - console.log(data); - Nodes.add(data); - }); - - spark.on('update', function(data){ - console.log('got update from ' + spark.id); - console.log(data); - }); -}); - var client = new Primus(server, { transformer: 'websockets', pathname: '/primus', @@ -48,65 +29,63 @@ var client = new Primus(server, { client.use('emit', require('primus-emit')); -client.on('connection', function(spark) { - console.log(spark); - +api.on('connection', function(spark) { console.log(spark.id); - console.log(spark.headers); console.log(spark.address); console.log(spark.query); - spark.write({give: 'identity'}); + + spark.on('hello', function(data){ + console.log('got hello data from ' + spark.id); + console.log(data); + + if(typeof data.id !== 'undefined' && typeof data.info !== 'undefined') + { + data.ip = spark.address.ip; + + var info = Nodes.add(data); + spark.emit('ready'); + + client.emit('new', info); + } + }); + + spark.on('update', function(data){ + console.log('got update from ' + spark.id); + console.log(data); + + if(typeof data.id !== 'undefined' && typeof data.stats !== 'undefined') + { + var stats = Nodes.update(data.id, data.stats); + + client.emit('updated', stats); + } + }); }); -server.listen(process.env.API_PORT || 3000); +client.on('connection', function(spark) { + console.log(spark.id); + console.log(spark.address); + console.log(spark.query); -// if(fs.existsSync('./config/nodes.js')){ -// config = require('./config/nodes'); -// } else { -// config = require('./config/nodes.default'); -// } + spark.on('ready', function(data){ + console.log('got hello data from ' + spark.id); + console.log(data); -// var Node = require('./models/node'); - -var app = express(); -app.http().io(); + spark.emit('init', {nodes: Nodes.all()}); + }); +}); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); - -// uncomment after placing favicon in /public //app.use(favicon(__dirname + '/public/favicon.ico')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(express.static(path.join(__dirname, 'public'))); -// var nodes = [], -// nodeStatus = [], -// nodeInterval; - -// for(i in config) { -// nodes[i] = new Node(config[i], i); -// console.log(nodes[i]); -// nodeStatus[i] = nodes[i].update(); -// } - -// nodeInterval = setInterval(function(){ -// for(i in nodes){ -// app.io.broadcast('update', nodes[i].update()); -// } -// }, 10000); - -// app.get('/', function(req, res) { -// res.render('index', { title: 'Ethereum Network Status' }); -// }); - -// app.io.route('ready', function(req) { -// req.io.emit('init', { -// nodes: nodeStatus -// }); -// console.log('emited'); -// }); +app.get('/', function(req, res) { + res.render('index', { title: 'Ethereum Network Status' }); +}); // catch 404 and forward to error handler app.use(function(req, res, next) { @@ -116,9 +95,6 @@ app.use(function(req, res, next) { }); // error handlers - -// development error handler -// will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.status(err.status || 500); @@ -130,7 +106,6 @@ if (app.get('env') === 'development') { } // production error handler -// no stacktraces leaked to user app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { @@ -139,5 +114,6 @@ app.use(function(err, req, res, next) { }); }); +server.listen(process.env.PORT || 3000); module.exports = app; diff --git a/bin/www b/bin/www index e77df43..b59653e 100755 --- a/bin/www +++ b/bin/www @@ -2,8 +2,8 @@ var debug = require('debug')('eth-netstats'); var app = require('../app'); -app.set('port', process.env.PORT || 3000); +// app.set('port', process.env.PORT || 3000); -var server = app.listen(app.get('port'), function() { - debug('Express server listening on port ' + server.address().port); -}); +// var server = app.listen(app.get('port'), function() { +// debug('Express server listening on port ' + server.address().port); +// }); diff --git a/models/collection.js b/models/collection.js index b18becb..01b0588 100644 --- a/models/collection.js +++ b/models/collection.js @@ -10,44 +10,63 @@ var Collection = function Collection() Collection.prototype.add = function(data) { - if(typeof data.id == 'undefined') - return false; + var node = this.getNodeOrNew({ id : data.id }, data); - var node = this.getNodeOrNew({ id : data.id }); - - node.info = data.info; + return node.getInfo(); } -Collection.prototype.get = function(id) +Collection.prototype.update = function(id, stats) { - return this.getNode(id); + var node = this.getNode({ id: id }); + + if(!node) + return false; + + node.stats = stats; + + console.log(this.all()); + + return node.getStats(); } Collection.prototype.getIndex = function(search) { return _.findIndex(this._list, search); - - return (index >= 0 ? index : false); } Collection.prototype.getNode = function(search) { var index = this.getIndex(search); - if(index) + if(index >= 0) return this._list[index]; return false; } -Collection.prototype.getIndexOrNew = function(search) +Collection.prototype.getNodeByIndex = function(index) { - var index = this.getIndex(search) || this._list.push(new Node()); + if(this._list[index]) + return this._list[index]; + + return false; } -Collection.prototype.getNodeOrNew = function(search) +Collection.prototype.getIndexOrNew = function(search, data) { - return this.getNode(this.getIndexOrNew(search)); + var index = this.getIndex(search); + + return (index >= 0 ? index : this._list.push(new Node(data)) - 1); +} + +Collection.prototype.getNodeOrNew = function(search, data) +{ + return this.getNodeByIndex(this.getIndexOrNew(search, data)); +} + +Collection.prototype.all = function() +{ + return this._list; } module.exports = Collection; \ No newline at end of file diff --git a/models/node.js b/models/node.js index d69bea1..ca757e7 100644 --- a/models/node.js +++ b/models/node.js @@ -1,6 +1,6 @@ var geoip = require('geoip-lite'); -var Node = function Node() +var Node = function Node(data) { this.id = null; this.info = {}; @@ -15,20 +15,35 @@ var Node = function Node() block: {}, blocktimeAvg: 0, difficulty: [], - uptime: { - down: 0, - inc: 0, - total: 0 - }, + uptime: 0, lastUpdate: 0 }; + if(typeof data.id !== 'undefined') + this.id = data.id; + + if(typeof data.info !== 'undefined') + this.info = data.info; + + if(typeof data.ip !== 'undefined') + this.setGeo(data.ip); + return this; } -Node.prototype.setGeo = function() +Node.prototype.setGeo = function(ip) { this.geo = geoip.lookup(ip); } +Node.prototype.getInfo = function() +{ + return {id: this.id, info: this.info, geo: this.geo}; +} + +Node.prototype.getStats = function() +{ + return {id: this.id, stats: this.stats}; +} + module.exports = Node; diff --git a/public/js/controllers.js b/public/js/controllers.js index bef4448..ccc61f8 100644 --- a/public/js/controllers.js +++ b/public/js/controllers.js @@ -18,10 +18,27 @@ function StatsCtrl($scope, $filter, socket, _) { // Socket listeners // ---------------- - socket.emit('ready'); + socket = new Primus(); + + socket.on('open', function open() { + socket.emit('ready'); + console.log('The connection has been opened.'); + }).on('end', function end() { + self._socket = false; + }).on('error', function error(err) { + console.log(err); + }).on('reconnecting', function reconnecting(opts) { + console.log('We are scheduling a reconnect operation', opts); + }).on('data', function incoming(data) { + console.log('Received some data', data); + }); + + // socket.emit('ready', 'test'); socket.on('init', function(data) { + console.log(data); + $scope.nodes = data.nodes; updateStats(); @@ -29,6 +46,8 @@ function StatsCtrl($scope, $filter, socket, _) { socket.on('update', function(data) { + console.log(data); + $scope.nodes[data.id] = data; updateStats(); @@ -51,7 +70,7 @@ function StatsCtrl($scope, $filter, socket, _) { }).stats.block.timestamp; $scope.upTimeTotal = _.reduce($scope.nodes, function(total, node) { - return total + node.stats.uptime.total; + return total + node.stats.uptime; }, 0) / $scope.nodes.length; $scope.map = _.map($scope.nodes, function(node) { @@ -69,5 +88,7 @@ function StatsCtrl($scope, $filter, socket, _) { longitude: 0 }; }); + + $scope.$apply(); } } \ No newline at end of file diff --git a/public/js/script.js b/public/js/script.js index b4fec9d..3c7068c 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -1,10 +1,4 @@ (function() { - var socket = io.connect(); - - socket.on('init', function(data){ - console.log(data); - }); - $('body').on('mouseenter', '[data-toggle="tooltip"]', function( event ) { $(this).tooltip('show'); }).on('mouseleave', '[data-toggle="tooltip"]', function( event ) { diff --git a/public/js/services.js b/public/js/services.js index 097919a..a0d2f0d 100644 --- a/public/js/services.js +++ b/public/js/services.js @@ -3,27 +3,28 @@ /* Services */ app.factory('socket', function ($rootScope) { - var socket = io.connect(); - return { - on: function (eventName, callback) { - socket.on(eventName, function () { - var args = arguments; - $rootScope.$apply(function () { - callback.apply(socket, args); - }); - }); - }, - emit: function (eventName, data, callback) { - socket.emit(eventName, data, function () { - var args = arguments; - $rootScope.$apply(function () { - if (callback) { - callback.apply(socket, args); - } - }); - }) - } - }; + var socket;// = new Primus(); + return socket; + // return { + // on: function (eventName, callback) { + // socket.on(eventName, function () { + // var args = arguments; + // $rootScope.$apply(function () { + // callback.apply(socket, args); + // }); + // }); + // }, + // emit: function (eventName, data, callback) { + // socket.emit(eventName, data, function () { + // var args = arguments; + // $rootScope.$apply(function () { + // if (callback) { + // callback.apply(socket, args); + // } + // }); + // }) + // } + // }; }); var underscore = angular.module('underscore', []); diff --git a/views/index.jade b/views/index.jade index c4280d5..0a7b745 100644 --- a/views/index.jade +++ b/views/index.jade @@ -75,10 +75,10 @@ block content tbody tr(ng-repeat='node in nodes', class="{{ node.stats | mainClass : bestBlock }}") td(rel="{{node.id}}") - span(data-toggle="tooltip", data-placement="top", data-original-title="{{node.geo | geoTooltip}}") {{node.name}} - div.small {{node.ip}} - td {{node.type}} - div.small {{node.os}} + span(data-toggle="tooltip", data-placement="top", data-original-title="{{node.geo | geoTooltip}}") {{node.info.name}} + div.small {{node.info.ip}} + td {{node.info.node}} + div.small {{node.info.os}}, {{node.info.os_v}} td(class="{{ node.stats.peers | peerClass }}") {{node.stats.peers}} td(class="{{ node.stats.mining | miningClass }}") i(class="{{ node.stats.mining | miningIconClass }}") @@ -87,4 +87,4 @@ block content span.small {{node.stats.block.hash}} div.small Difficulty: {{node.stats.block.difficulty | gasFilter}} | Gas used: {{node.stats.block.gasUsed | gasFilter}} | Min gas price: {{node.stats.block.minGasPrice | gasFilter}} | Gas limit: {{node.stats.block.gasLimit | gasFilter}} td(am-time-ago="node.stats.block.timestamp", am-preprocess="unix", class="{{ node.stats.block.timestamp | timeClass }}") ∞ - td(class="{{ node.stats.uptime.total | upTimeClass }}") {{ node.stats.uptime.total | upTimeFilter }} + td(class="{{ node.stats.uptime | upTimeClass }}") {{ node.stats.uptime | upTimeFilter }} diff --git a/views/layout.jade b/views/layout.jade index d768fd0..2bcacfd 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -12,7 +12,7 @@ html(ng-app="netStatsApp") block content script(src="/js/lib/angular.min.js") - script(src="/socket.io/socket.io.js") + script(src="/primus/primus.js") script(src="/js/lib/underscore.min.js") script(src="/js/lib/jquery.min.js") script(src="/js/lib/d3.v3.min.js") From ae7dc4579c3bbb5b2385c43b3880b5145430c8a3 Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 07:21:41 +0200 Subject: [PATCH 03/11] changed express.io to express --- config/nodes.default.js | 106 ---------------------------------------- package.json | 2 +- 2 files changed, 1 insertion(+), 107 deletions(-) delete mode 100644 config/nodes.default.js diff --git a/config/nodes.default.js b/config/nodes.default.js deleted file mode 100644 index 44e7c17..0000000 --- a/config/nodes.default.js +++ /dev/null @@ -1,106 +0,0 @@ -var nodes = [ - // - // Duplicate this file and rename it to "nodes.js". - // It will be loaded as the main config file and - // will be ignored by git. Add nodes like the one below - // - // { - // name: 'Readable name of the node', - // type: 'C++/Go...', - // rpcHost: 'your node IP here', - // rpcPort: 'JSON-RPC port here' - // }, - { - name: 'instance 1', - type: 'C++', - os: 'linux', - rpcHost: '54.173.252.182', - rpcPort: '8080' - }, - { - name: 'instance 2', - type: 'C++', - os: 'linux', - rpcHost: '54.209.133.248', - rpcPort: '8080' - }, - { - name: 'instance 3', - type: 'C++', - os: 'linux', - rpcHost: '54.174.67.126', - rpcPort: '8080' - }, - { - name: 'instance 4', - type: 'C++', - os: 'linux', - rpcHost: '54.152.111.192', - rpcPort: '8080' - }, - { - name: 'instance 5', - type: 'C++', - os: 'linux', - rpcHost: '52.0.57.108', - rpcPort: '8080' - }, - { - name: 'instance 6', - type: 'C++', - os: 'linux', - rpcHost: '52.0.28.243', - rpcPort: '8080' - }, - { - name: 'instance 7', - type: 'C++', - os: 'linux', - rpcHost: '52.1.21.22', - rpcPort: '8080' - }, - { - name: 'instance 8', - type: 'C++', - os: 'linux', - rpcHost: '52.0.207.16', - rpcPort: '8080' - }, - { - name: 'instance 9', - type: 'C++', - os: 'linux', - rpcHost: '52.0.250.60', - rpcPort: '8080' - }, - { - name: 'instance 10', - type: 'C++', - os: 'linux', - rpcHost: '52.0.160.249', - rpcPort: '8080' - }, - { - name: 'instance 11', - type: 'C++', - os: 'linux', - rpcHost: '52.0.216.64', - rpcPort: '8080' - }, - { - name: 'instance 12', - type: 'C++', - os: 'linux', - rpcHost: '52.0.144.162', - rpcPort: '8080' - }, - { - name: 'instance 13', - type: 'C++', - os: 'linux', - rpcHost: '52.0.93.126', - rpcPort: '8080' - } -]; - -module.exports = nodes; diff --git a/package.json b/package.json index 7d8047a..4e3a25a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "body-parser": "~1.8.1", "debug": "~2.0.0", "ethereum.js": "*", - "express.io": "^1.1.13", + "express": "^4.11.2", "geoip-lite": "^1.1.4", "jade": "~1.6.0", "lodash": "^3.2.0", From b0fdf6928ce7b8fbea8c427561f59e1876850278 Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 11:30:41 +0200 Subject: [PATCH 04/11] added more socket functionality --- app.js | 4 +- models/collection.js | 3 +- models/node.js | 25 ++++++++- public/js/app.js | 2 +- public/js/controllers.js | 99 ++++++++++++++++++++------------- public/js/lib/lodash.min.js | 88 +++++++++++++++++++++++++++++ public/js/lib/underscore.min.js | 6 -- public/js/services.js | 4 +- views/layout.jade | 2 +- 9 files changed, 178 insertions(+), 55 deletions(-) create mode 100644 public/js/lib/lodash.min.js delete mode 100644 public/js/lib/underscore.min.js diff --git a/app.js b/app.js index 0a6e4cf..7fdfe6f 100644 --- a/app.js +++ b/app.js @@ -45,7 +45,7 @@ api.on('connection', function(spark) { var info = Nodes.add(data); spark.emit('ready'); - client.emit('new', info); + client.write({action: 'add', data: info}); } }); @@ -57,7 +57,7 @@ api.on('connection', function(spark) { { var stats = Nodes.update(data.id, data.stats); - client.emit('updated', stats); + client.write({action: 'update', data: stats}); } }); }); diff --git a/models/collection.js b/models/collection.js index 01b0588..07713ab 100644 --- a/models/collection.js +++ b/models/collection.js @@ -11,6 +11,7 @@ var Collection = function Collection() Collection.prototype.add = function(data) { var node = this.getNodeOrNew({ id : data.id }, data); + node.setInfo(data); return node.getInfo(); } @@ -24,8 +25,6 @@ Collection.prototype.update = function(id, stats) node.stats = stats; - console.log(this.all()); - return node.getStats(); } diff --git a/models/node.js b/models/node.js index ca757e7..6178205 100644 --- a/models/node.js +++ b/models/node.js @@ -12,7 +12,13 @@ var Node = function Node(data) peers: 0, pending: 0, gasPrice: 0, - block: {}, + block: { + difficulty: 0, + number: 0, + gasLimit: 0, + timestamp: 0, + blocktime: 0 + }, blocktimeAvg: 0, difficulty: [], uptime: 0, @@ -25,8 +31,10 @@ var Node = function Node(data) if(typeof data.info !== 'undefined') this.info = data.info; - if(typeof data.ip !== 'undefined') + if(typeof data.ip !== 'undefined'){ + this.info.ip = data.ip; this.setGeo(data.ip); + } return this; } @@ -36,9 +44,20 @@ Node.prototype.setGeo = function(ip) this.geo = geoip.lookup(ip); } +Node.prototype.setInfo = function(data) +{ + if(typeof data.info !== 'undefined') + this.info = data.info; + + if(typeof data.ip !== 'undefined'){ + this.info.ip = data.ip; + this.setGeo(data.ip); + } +} + Node.prototype.getInfo = function() { - return {id: this.id, info: this.info, geo: this.geo}; + return {id: this.id, info: this.info, geo: this.geo, stats: this.stats}; } Node.prototype.getStats = function() diff --git a/public/js/app.js b/public/js/app.js index 24c6627..db06252 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -1,6 +1,6 @@ 'use strict'; -var app = angular.module('netStatsApp', ['underscore', 'angularMoment', 'netStatsApp.filters', 'netStatsApp.directives']); +var app = angular.module('netStatsApp', ['lodash', 'angularMoment', 'netStatsApp.filters', 'netStatsApp.directives']); app.run(function(amMoment) { amMoment.changeLocale('en-gb'); diff --git a/public/js/controllers.js b/public/js/controllers.js index ccc61f8..41456e6 100644 --- a/public/js/controllers.js +++ b/public/js/controllers.js @@ -23,18 +23,21 @@ function StatsCtrl($scope, $filter, socket, _) { socket.on('open', function open() { socket.emit('ready'); console.log('The connection has been opened.'); - }).on('end', function end() { + }) + .on('end', function end() { self._socket = false; - }).on('error', function error(err) { + }) + .on('error', function error(err) { console.log(err); - }).on('reconnecting', function reconnecting(opts) { + }) + .on('reconnecting', function reconnecting(opts) { console.log('We are scheduling a reconnect operation', opts); - }).on('data', function incoming(data) { + }) + .on('data', function incoming(data) { console.log('Received some data', data); + socketAction(data.action, data.data); }); - // socket.emit('ready', 'test'); - socket.on('init', function(data) { console.log(data); @@ -44,50 +47,70 @@ function StatsCtrl($scope, $filter, socket, _) { updateStats(); }); - socket.on('update', function(data) + function socketAction(action, data) { - console.log(data); + switch(action) { + case "add": + console.log(data); + $scope.nodes.push(data); + break; - $scope.nodes[data.id] = data; + case "update": + console.log(data); + $scope.nodes[findIndex({id: data.id})].stats = data.stats; + break; + + case "info": + console.log(data); + $scope.nodes[findIndex({id: data.id})].info = data.info; + } updateStats(); - }); + } + + function findIndex(search) + { + return _.findIndex($scope.nodes, search); + } function updateStats() { - $scope.nodesTotal = $scope.nodes.length; + if($scope.nodes.length) + { + $scope.nodesTotal = $scope.nodes.length; - $scope.nodesActive = _.filter($scope.nodes, function(node) { - return node.stats.active == true; - }).length; + $scope.nodesActive = _.filter($scope.nodes, function(node) { + return node.stats.active == true; + }).length; - $scope.bestBlock = _.max($scope.nodes, function(node) { - return parseInt(node.stats.block.number); - }).stats.block.number; + $scope.bestBlock = _.max($scope.nodes, function(node) { + return parseInt(node.stats.block.number); + }).stats.block.number; - $scope.lastBlock = _.max($scope.nodes, function(node) { - return parseInt(node.stats.block.timestamp); - }).stats.block.timestamp; + $scope.lastBlock = _.max($scope.nodes, function(node) { + return parseInt(node.stats.block.timestamp); + }).stats.block.timestamp; - $scope.upTimeTotal = _.reduce($scope.nodes, function(total, node) { - return total + node.stats.uptime; - }, 0) / $scope.nodes.length; + $scope.upTimeTotal = _.reduce($scope.nodes, function(total, node) { + return total + node.stats.uptime; + }, 0) / $scope.nodes.length; - $scope.map = _.map($scope.nodes, function(node) { - if(node.geo != null) - return { - radius: 3, - latitude: node.geo.ll[0], - longitude: node.geo.ll[1], - fillKey: $filter('bubbleClass')(node, $scope.bestBlock) - }; - else - return { - radius: 0, - latitude: 0, - longitude: 0 - }; - }); + $scope.map = _.map($scope.nodes, function(node) { + if(node.geo != null) + return { + radius: 3, + latitude: node.geo.ll[0], + longitude: node.geo.ll[1], + fillKey: $filter('bubbleClass')(node, $scope.bestBlock) + }; + else + return { + radius: 0, + latitude: 0, + longitude: 0 + }; + }); + } $scope.$apply(); } diff --git a/public/js/lib/lodash.min.js b/public/js/lib/lodash.min.js new file mode 100644 index 0000000..e29d33a --- /dev/null +++ b/public/js/lib/lodash.min.js @@ -0,0 +1,88 @@ +/** + * @license + * lodash 3.2.0 (Custom Build) lodash.com/license | Underscore.js 1.7.0 underscorejs.org/LICENSE + * Build: `lodash modern -o ./lodash.js` + */ +;(function(){function n(n,t){if(n!==t){var r=n===n,e=t===t;if(n>t||!r||typeof n=="undefined"&&e)return 1;if(n=n&&9<=n&&13>=n||32==n||160==n||5760==n||6158==n||8192<=n&&(8202>=n||8232==n||8233==n||8239==n||8287==n||12288==n||65279==n) +}function g(n,t){for(var r=-1,e=n.length,u=-1,o=[];++re&&(e=u)}return e}function Yt(n,t,r,e){var u=-1,o=n.length;for(e&&o&&(r=n[++u]);++ui(r,a)&&u.push(a);return u}function or(n,t){var r=n?n.length:0;if(!oe(r))return _r(n,t);for(var e=-1,u=pe(n);++et&&(t=-t>u?0:u+t),r=typeof r=="undefined"||r>u?u:+r||0,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,r=wu(u);++eu(a,s)&&((r||f)&&a.push(s),c.push(l))}return c}function Cr(n,t){for(var r=-1,e=t.length,u=wu(e);++r>>1,i=n[o];(r?i<=t:it||null==r)return r;if(3=o&&f<=i&&(e=O&&t>u||e>u&&t>=O)||o)&&(t&x&&(r[2]=p[2],f|=e&x?0:j),(e=p[3])&&(u=r[3],r[3]=u?Fr(u,e,p[4]):zt(e),r[4]=u?g(r[3],B):zt(p[4])),(e=p[5])&&(u=r[5],r[5]=u?Lr(u,e,p[6]):zt(e),r[6]=u?g(r[5],B):zt(p[6])),(e=p[7])&&(r[7]=zt(e)),t&C&&(r[8]=null==r[8]?p[8]:oo(r[8],p[8])),null==r[9]&&(r[9]=p[9]),r[0]=p[0],r[1]=f),t=r[1],f=r[9] +}return r[9]=null==f?a?0:n.length:uo(f-c,0)||0,(p?bo:jo)(t==x?zr(r[0],r[2]):t!=R&&t!=(x|R)||r[4].length?Pr.apply(w,r):Vr.apply(w,r),r)}function Zr(n,t,r,e,u,o,i){var f=-1,a=n.length,c=t.length,l=true;if(a!=c&&(!u||c<=a))return false;for(;l&&++fu)||i===e&&i===o)&&(u=i,o=n)}),o}function Hr(n,t,r){var e=Wt.callback||_u,e=e===_u?tr:e;return r?e(n,t,r):e}function Qr(n,r,e){var u=Wt.indexOf||de,u=u===de?t:u;return n?u(n,r,e):u}function ne(n){var t=n.length,r=new n.constructor(t);return t&&"string"==typeof n[0]&&Uu.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function te(n){return n=n.constructor,typeof n=="function"&&n instanceof n||(n=Eu),new n +}function re(n,t,r){var e=n.constructor;switch(t){case J:return Ur(n);case M:case q:return new e(+n);case X:case H:case Q:case nt:case tt:case rt:case et:case ut:case ot:return t=n.buffer,new e(r?Ur(t):t,n.byteOffset,n.length);case V:case G:return new e(n);case Z:var u=new e(n.source,yt.exec(n));u.lastIndex=n.lastIndex}return u}function ee(n,t){return n=+n,t=null==t?vo:t,-1t?0:t)):[]}function ge(n,t,r){var e=n?n.length:0; +return e?((r?ue(n,t,r):null==t)&&(t=1),t=e-(+t||0),Rr(n,0,0>t?0:t)):[]}function ve(n,t,r){var e=-1,u=n?n.length:0;for(t=Hr(t,r,3);++ee?uo(u+e,0):e||0;else if(e)return e=Sr(n,r),n=n[e],(r===r?r===n:n!==n)?e:-1;return t(n,r,e)}function me(n){return _e(n,1)}function we(n,r,e,u){if(!n||!n.length)return[];typeof r!="boolean"&&null!=r&&(u=e,e=ue(n,r,u)?null:r,r=false); +var o=Hr();if((o!==tr||null!=e)&&(e=o(e,u,3)),r&&Qr()==t){r=e;var i;e=-1,u=n.length;for(var o=-1,f=[];++e>>0,e=wu(r);++tr?uo(e+r,0):r||0:0,typeof n=="string"||!So(n)&&tu(n)?rarguments.length,or)}function We(n,t,r,e){return(So(n)?Zt:Er)(n,Hr(t,e,4),r,3>arguments.length,ir)}function Ne(n,t,r){return(r?ue(n,t,r):null==t)?(n=se(n),t=n.length,0t?0:+t||0,n.length),n)}function Ue(n){n=se(n); +for(var t=-1,r=n.length,e=wu(r);++t=r||r>t?(f&&qu(f),r=p,f=s=p=w,r&&(h=To(),a=n.apply(l,i),s||f||(i=l=null))):s=Gu(e,r)}function u(){s&&qu(s),f=s=p=w,(g||_!==t)&&(h=To(),a=n.apply(l,i),s||f||(i=l=null)) +}function o(){if(i=arguments,c=To(),l=this,p=g&&(s||!v),false===_)var r=v&&!s;else{f||v||(h=c);var o=_-(c-h),y=0>=o||o>_;y?(f&&(f=qu(f)),h=c,a=n.apply(l,i)):f||(f=Gu(u,o))}return y&&s?s=qu(s):s||t===_||(s=Gu(e,t)),r&&(y=true,a=n.apply(l,i)),!y||s||f||(i=l=null),a}var i,f,a,c,l,s,p,h=0,_=false,g=true;if(typeof n!="function")throw new Ou($);if(t=0>t?0:t,true===r)var v=true,g=false;else Xe(r)&&(v=r.leading,_="maxWait"in r&&uo(+r.maxWait||0,t),g="trailing"in r?r.trailing:g);return o.cancel=function(){s&&qu(s),f&&qu(f),f=s=p=w +},o}function qe(){var n=arguments,t=n.length-1;if(0>t)return function(n){return n};if(!qt(n,Je))throw new Ou($);return function(){for(var r=t,e=n[r].apply(this,arguments);r--;)e=n[r].call(this,e);return e}}function Pe(n,t){function r(){var e=r.cache,u=t?t.apply(this,arguments):arguments[0];if(e.has(u))return e.get(u);var o=n.apply(this,arguments);return e.set(u,o),o}if(typeof n!="function"||t&&typeof t!="function")throw new Ou($);return r.cache=new Pe.Cache,r}function Ke(n){var t=Rr(arguments,1),r=g(t,Ke.placeholder); +return Yr(n,R,null,t,r)}function Ve(n){var t=Rr(arguments,1),r=g(t,Ve.placeholder);return Yr(n,I,null,t,r)}function Ye(n){return oe(h(n)?n.length:w)&&Lu.call(n)==z||false}function Ze(n){return n&&1===n.nodeType&&h(n)&&-1t||!n||!ro(t))return r;do t%2&&(r+=n),t=Pu(t/2),n+=n;while(t);return r}function su(n,t,r){var u=n;return(n=e(n))?(r?ue(u,t,r):null==t)?n.slice(v(n),y(n)+1):(t+="",n.slice(o(n,t),i(n,t)+1)):n +}function pu(n,t,r){return r&&ue(n,t,r)&&(t=null),n=e(n),n.match(t||Rt)||[]}function hu(n){try{return n.apply(w,Rr(arguments,1))}catch(t){return Ge(t)?t:new xu(t)}}function _u(n,t,r){return r&&ue(n,t,r)&&(t=null),h(n)?yu(n):tr(n,t)}function gu(n){return function(){return n}}function vu(n){return n}function yu(n){return br(rr(n,true))}function du(n,t,r){if(null==r){var e=Xe(t),u=e&&Fo(t);((u=u&&u.length&&vr(t,u))?u.length:e)||(u=false,r=t,t=n,n=this)}u||(u=vr(t,Fo(t)));var o=true,e=-1,i=Je(n),f=u.length; +!1===r?o=false:Xe(r)&&"chain"in r&&(o=r.chain);for(;++e>>1,go=Qu?Qu.BYTES_PER_ELEMENT:0,vo=ju.pow(2,53)-1,yo=Hu&&new Hu,mo=Wt.support={};!function(n){mo.funcDecomp=!He(_.WinRTError)&&kt.test(m),mo.funcNames=typeof Au.name=="string";try{mo.dom=11===Su.createDocumentFragment().nodeType +}catch(t){mo.dom=false}try{mo.nonEnumArgs=!Yu.call(arguments,1)}catch(r){mo.nonEnumArgs=true}}(0,0),Wt.templateSettings={escape:ht,evaluate:_t,interpolate:gt,variable:"",imports:{_:Wt}};var wo=function(){function n(){}return function(t){if(Xe(t)){n.prototype=t;var r=new n;n.prototype=null}return r||_.Object()}}(),bo=yo?function(n,t){return yo.set(n,t),n}:vu;Du||(Ur=zu&&Xu?function(n){var t=n.byteLength,r=Qu?Pu(t/go):0,e=r*go,u=new zu(t);if(r){var o=new Qu(u,0,r);o.set(new Qu(n,0,r))}return t!=e&&(o=new Xu(u,e),o.set(new Xu(n,e))),u +}:gu(null));var xo=to&&Zu?function(n){return new Lt(n)}:gu(null),Ao=yo?function(n){return yo.get(n)}:mu,jo=function(){var n=0,t=0;return function(r,e){var u=To(),o=N-(u-t);if(t=u,0=W)return r}else n=0;return bo(r,e)}}(),ko=$r(function(n,t,r){Uu.call(n,r)?++n[r]:n[r]=1}),Eo=$r(function(n,t,r){Uu.call(n,r)?n[r].push(t):n[r]=[t]}),Ro=$r(function(n,t,r){n[r]=t}),Io=qr(Vt),Oo=qr(function(n){for(var t=-1,r=n.length,e=so;++t--n?t.apply(this,arguments):void 0}},Wt.ary=function(n,t,r){return r&&ue(n,t,r)&&(t=null),t=n&&null==t?n.length:uo(+t||0,0),Yr(n,C,null,null,null,null,t)},Wt.assign=Uo,Wt.at=function(n){return oe(n?n.length:0)&&(n=se(n)),Qt(n,lr(arguments,false,false,1))},Wt.before=Le,Wt.bind=$e,Wt.bindAll=function(n){for(var t=n,r=1r&&(r=-r>u?0:u+r),e=typeof e=="undefined"||e>u?u:+e||0,0>e&&(e+=u),u=r>e?0:e>>>0,r>>>=0;r(s?Bt(s,f):o(l,f))){for(r=e;--r;){var p=u[r];if(0>(p?Bt(p,f):o(n[r],f)))continue n}s&&s.push(f),l.push(f)}return l},Wt.invert=function(n,t,r){r&&ue(n,t,r)&&(t=null),r=-1; +for(var e=Fo(n),u=e.length,o={};++rt?0:t)):[]},Wt.takeRight=function(n,t,r){var e=n?n.length:0;return e?((r?ue(n,t,r):null==t)&&(t=1),t=e-(+t||0),Rr(n,0>t?0:t)):[]},Wt.takeRightWhile=function(n,t,r){var e=n?n.length:0;if(!e)return[];for(t=Hr(t,r,3);e--&&t(n[e],e,n););return Rr(n,e+1)},Wt.takeWhile=function(n,t,r){var e=n?n.length:0;if(!e)return[];var u=-1;for(t=Hr(t,r,3);++un||!ro(n))return[];var e=-1,u=wu(oo(n,po));for(t=Nr(t,r,1);++er?0:+r||0,u))-t.length,0<=r&&n.indexOf(t,r)==r},Wt.escape=function(n){return(n=e(n))&&pt.test(n)?n.replace(lt,l):n},Wt.escapeRegExp=au,Wt.every=Ee,Wt.find=Ie,Wt.findIndex=ve,Wt.findKey=function(n,t,r){return t=Hr(t,r,3),cr(n,t,_r,true)},Wt.findLast=function(n,t,r){return t=Hr(t,r,3),cr(n,t,ir)},Wt.findLastIndex=function(n,t,r){var e=n?n.length:0; +for(t=Hr(t,r,3);e--;)if(t(n[e],e,n))return e;return-1},Wt.findLastKey=function(n,t,r){return t=Hr(t,r,3),cr(n,t,gr,true)},Wt.findWhere=function(n,t){return Ie(n,br(t))},Wt.first=ye,Wt.has=function(n,t){return n?Uu.call(n,t):false},Wt.identity=vu,Wt.includes=ke,Wt.indexOf=de,Wt.isArguments=Ye,Wt.isArray=So,Wt.isBoolean=function(n){return true===n||false===n||h(n)&&Lu.call(n)==M||false},Wt.isDate=function(n){return h(n)&&Lu.call(n)==q||false},Wt.isElement=Ze,Wt.isEmpty=function(n){if(null==n)return true;var t=n.length; +return oe(t)&&(So(n)||tu(n)||Ye(n)||h(n)&&Je(n.splice))?!t:!Fo(n).length},Wt.isEqual=function(n,t,r,e){return r=typeof r=="function"&&Nr(r,e,3),!r&&ie(n)&&ie(t)?n===t:(e=r?r(n,t):w,typeof e=="undefined"?dr(n,t,r):!!e)},Wt.isError=Ge,Wt.isFinite=Wo,Wt.isFunction=Je,Wt.isMatch=function(n,t,r,e){var u=Fo(t),o=u.length;if(r=typeof r=="function"&&Nr(r,e,3),!r&&1==o){var i=u[0];if(e=t[i],ie(e))return null!=n&&e===n[i]&&Uu.call(n,i)}for(var i=wu(o),f=wu(o);o--;)e=i[o]=t[u[o]],f[o]=ie(e);return mr(n,u,i,f,r) +},Wt.isNaN=function(n){return Qe(n)&&n!=+n},Wt.isNative=He,Wt.isNull=function(n){return null===n},Wt.isNumber=Qe,Wt.isObject=Xe,Wt.isPlainObject=No,Wt.isRegExp=nu,Wt.isString=tu,Wt.isTypedArray=ru,Wt.isUndefined=function(n){return typeof n=="undefined"},Wt.kebabCase=Bo,Wt.last=function(n){var t=n?n.length:0;return t?n[t-1]:w},Wt.lastIndexOf=function(n,t,r){var e=n?n.length:0;if(!e)return-1;var u=e;if(typeof r=="number")u=(0>r?uo(e+r,0):oo(r||0,e-1))+1;else if(r)return u=Sr(n,t,true)-1,n=n[u],(t===t?t===n:n!==n)?u:-1; +if(t!==t)return p(n,u,true);for(;u--;)if(n[u]===t)return u;return-1},Wt.max=Io,Wt.min=Oo,Wt.noConflict=function(){return _._=$u,this},Wt.noop=mu,Wt.now=To,Wt.pad=function(n,t,r){n=e(n),t=+t;var u=n.length;return ur?0:+r||0,n.length),n.lastIndexOf(t,r)==r +},Wt.template=function(n,t,r){var u=Wt.templateSettings;r&&ue(n,t,r)&&(t=r=null),n=e(n),t=Ht(Ht({},r||t),u,Xt),r=Ht(Ht({},t.imports),u.imports,Xt);var o,i,f=Fo(r),a=Cr(r,f),c=0;r=t.interpolate||xt;var l="__p+='";r=Ru((t.escape||xt).source+"|"+r.source+"|"+(r===gt?vt:xt).source+"|"+(t.evaluate||xt).source+"|$","g");var p="sourceURL"in t?"//# sourceURL="+t.sourceURL+"\n":"";if(n.replace(r,function(t,r,e,u,f,a){return e||(e=u),l+=n.slice(c,a).replace(Et,s),r&&(o=true,l+="'+__e("+r+")+'"),f&&(i=true,l+="';"+f+";\n__p+='"),e&&(l+="'+((__t=("+e+"))==null?'':__t)+'"),c=a+t.length,t +}),l+="';",(t=t.variable)||(l="with(obj){"+l+"}"),l=(i?l.replace(it,""):l).replace(ft,"$1").replace(at,"$1;"),l="function("+(t||"obj")+"){"+(t?"":"obj||(obj={});")+"var __t,__p=''"+(o?",__e=_.escape":"")+(i?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}",t=hu(function(){return Au(f,p+"return "+l).apply(w,a)}),t.source=l,Ge(t))throw t;return t},Wt.trim=su,Wt.trimLeft=function(n,t,r){var u=n;return(n=e(n))?n.slice((r?ue(u,t,r):null==t)?v(n):o(n,t+"")):n +},Wt.trimRight=function(n,t,r){var u=n;return(n=e(n))?(r?ue(u,t,r):null==t)?n.slice(0,y(n)+1):n.slice(0,i(n,t+"")+1):n},Wt.trunc=function(n,t,r){r&&ue(n,t,r)&&(t=null);var u=T;if(r=S,null!=t)if(Xe(t)){var o="separator"in t?t.separator:o,u="length"in t?+t.length||0:u;r="omission"in t?e(t.omission):r}else u=+t||0;if(n=e(n),u>=n.length)return n;if(u-=r.length,1>u)return r;if(t=n.slice(0,u),null==o)return t+r;if(nu(o)){if(n.slice(u).search(o)){var i,f=n.slice(0,u);for(o.global||(o=Ru(o.source,(yt.exec(o)||"")+"g")),o.lastIndex=0;n=o.exec(f);)i=n.index; +t=t.slice(0,null==i?u:i)}}else n.indexOf(o,u)!=u&&(o=t.lastIndexOf(o),-1o.__dir__,f.push({iteratee:Hr(n,u,3),type:t}),o}}),Mt(["drop","take"],function(n,t){var r="__"+n+"Count__",e=n+"While";Ut.prototype[n]=function(e){e=null==e?1:uo(Pu(e)||0,0); +var u=this.clone();if(u.__filtered__){var o=u[r];u[r]=t?oo(o,e):o+e}else(u.__views__||(u.__views__=[])).push({size:e,type:n+(0>u.__dir__?"Right":"")});return u},Ut.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()},Ut.prototype[n+"RightWhile"]=function(n,t){return this.reverse()[e](n,t).reverse()}}),Mt(["first","last"],function(n,t){var r="take"+(t?"Right":"");Ut.prototype[n]=function(){return this[r](1).value()[0]}}),Mt(["initial","rest"],function(n,t){var r="drop"+(t?"":"Right"); +Ut.prototype[n]=function(){return this[r](1)}}),Mt(["pluck","where"],function(n,t){var r=t?"filter":"map",e=t?br:jr;Ut.prototype[n]=function(n){return this[r](e(n))}}),Ut.prototype.compact=function(){return this.filter(vu)},Ut.prototype.dropWhile=function(n,t){var r;return n=Hr(n,t,3),this.filter(function(t,e,u){return r||(r=!n(t,e,u))})},Ut.prototype.reject=function(n,t){return n=Hr(n,t,3),this.filter(function(t,r,e){return!n(t,r,e)})},Ut.prototype.slice=function(n,t){n=null==n?0:+n||0;var r=0>n?this.takeRight(-n):this.drop(n); +return typeof t!="undefined"&&(t=+t||0,r=0>t?r.dropRight(-t):r.take(t-n)),r},Ut.prototype.toArray=function(){return this.drop(0)},_r(Ut.prototype,function(n,t){var r=Wt[t],e=/^(?:first|last)$/.test(t);Wt.prototype[t]=function(){function t(n){return n=[n],Vu.apply(n,o),r.apply(Wt,n)}var u=this.__wrapped__,o=arguments,i=this.__chain__,f=!!this.__actions__.length,a=u instanceof Ut,c=a&&!f;return e&&!i?c?n.call(u):r.call(Wt,this.value()):a||So(u)?(u=n.apply(c?u:new Ut(this),o),e||!f&&!u.__actions__||(u.__actions__||(u.__actions__=[])).push({func:je,args:[t],thisArg:Wt}),new Nt(u,i)):this.thru(t) +}}),Mt("concat join pop push shift sort splice unshift".split(" "),function(n){var t=Cu[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:join|pop|shift)$/.test(n);Wt.prototype[n]=function(){var n=arguments;return e&&!this.__chain__?t.apply(this.value(),n):this[r](function(r){return t.apply(r,n)})}}),Ut.prototype.clone=function(){var n=this.__actions__,t=this.__iteratees__,r=this.__views__,e=new Ut(this.__wrapped__);return e.__actions__=n?zt(n):null,e.__dir__=this.__dir__,e.__dropCount__=this.__dropCount__,e.__filtered__=this.__filtered__,e.__iteratees__=t?zt(t):null,e.__takeCount__=this.__takeCount__,e.__views__=r?zt(r):null,e +},Ut.prototype.reverse=function(){if(this.__filtered__){var n=new Ut(this);n.__dir__=-1,n.__filtered__=true}else n=this.clone(),n.__dir__*=-1;return n},Ut.prototype.value=function(){var n=this.__wrapped__.value();if(!So(n))return Tr(n,this.__actions__);var t,r=this.__dir__,e=0>r;t=n.length;for(var u=this.__views__,o=0,i=-1,f=u?u.length:0;++i"'`]/g,st=RegExp(ct.source),pt=RegExp(lt.source),ht=/<%-([\s\S]+?)%>/g,_t=/<%([\s\S]+?)%>/g,gt=/<%=([\s\S]+?)%>/g,vt=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,yt=/\w*$/,dt=/^\s*function[ \n\r\t]+\w/,mt=/^0[xX]/,wt=/^\[object .+?Constructor\]$/,bt=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,xt=/($^)/,At=/[.*+?^${}()|[\]\/\\]/g,jt=RegExp(At.source),kt=/\bthis\b/,Et=/['\n\r\u2028\u2029\\]/g,Rt=RegExp("[A-Z\\xc0-\\xd6\\xd8-\\xde]{2,}(?=[A-Z\\xc0-\\xd6\\xd8-\\xde][a-z\\xdf-\\xf6\\xf8-\\xff]+)|[A-Z\\xc0-\\xd6\\xd8-\\xde]?[a-z\\xdf-\\xf6\\xf8-\\xff]+|[A-Z\\xc0-\\xd6\\xd8-\\xde]+|[0-9]+","g"),It=" \t\x0b\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",Ot="Array ArrayBuffer Date Error Float32Array Float64Array Function Int8Array Int16Array Int32Array Math Number Object RegExp Set String _ clearTimeout document isFinite parseInt setTimeout TypeError Uint8Array Uint8ClampedArray Uint16Array Uint32Array WeakMap window WinRTError".split(" "),Ct={}; +Ct[X]=Ct[H]=Ct[Q]=Ct[nt]=Ct[tt]=Ct[rt]=Ct[et]=Ct[ut]=Ct[ot]=true,Ct[z]=Ct[D]=Ct[J]=Ct[M]=Ct[q]=Ct[P]=Ct[K]=Ct["[object Map]"]=Ct[V]=Ct[Y]=Ct[Z]=Ct["[object Set]"]=Ct[G]=Ct["[object WeakMap]"]=false;var Tt={};Tt[z]=Tt[D]=Tt[J]=Tt[M]=Tt[q]=Tt[X]=Tt[H]=Tt[Q]=Tt[nt]=Tt[tt]=Tt[V]=Tt[Y]=Tt[Z]=Tt[G]=Tt[rt]=Tt[et]=Tt[ut]=Tt[ot]=true,Tt[P]=Tt[K]=Tt["[object Map]"]=Tt["[object Set]"]=Tt["[object WeakMap]"]=false;var St={leading:false,maxWait:0,trailing:false},Wt={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss"},Nt={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},Ut={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"},Ft={"function":true,object:true},Lt={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},$t=Ft[typeof window]&&window!==(this&&this.window)?window:this,Bt=Ft[typeof exports]&&exports&&!exports.nodeType&&exports,Ft=Ft[typeof module]&&module&&!module.nodeType&&module,zt=Bt&&Ft&&typeof global=="object"&&global; +!zt||zt.global!==zt&&zt.window!==zt&&zt.self!==zt||($t=zt);var zt=Ft&&Ft.exports===Bt&&Bt,Dt=m();typeof define=="function"&&typeof define.amd=="object"&&define.amd?($t._=Dt, define(function(){return Dt})):Bt&&Ft?zt?(Ft.exports=Dt)._=Dt:Bt._=Dt:$t._=Dt}).call(this); \ No newline at end of file diff --git a/public/js/lib/underscore.min.js b/public/js/lib/underscore.min.js deleted file mode 100644 index 11f1d96..0000000 --- a/public/js/lib/underscore.min.js +++ /dev/null @@ -1,6 +0,0 @@ -// Underscore.js 1.7.0 -// http://underscorejs.org -// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Underscore may be freely distributed under the MIT license. -(function(){var n=this,t=n._,r=Array.prototype,e=Object.prototype,u=Function.prototype,i=r.push,a=r.slice,o=r.concat,l=e.toString,c=e.hasOwnProperty,f=Array.isArray,s=Object.keys,p=u.bind,h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=h),exports._=h):n._=h,h.VERSION="1.7.0";var g=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}};h.iteratee=function(n,t,r){return null==n?h.identity:h.isFunction(n)?g(n,t,r):h.isObject(n)?h.matches(n):h.property(n)},h.each=h.forEach=function(n,t,r){if(null==n)return n;t=g(t,r);var e,u=n.length;if(u===+u)for(e=0;u>e;e++)t(n[e],e,n);else{var i=h.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},h.map=h.collect=function(n,t,r){if(null==n)return[];t=h.iteratee(t,r);for(var e,u=n.length!==+n.length&&h.keys(n),i=(u||n).length,a=Array(i),o=0;i>o;o++)e=u?u[o]:o,a[o]=t(n[e],e,n);return a};var v="Reduce of empty array with no initial value";h.reduce=h.foldl=h.inject=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length,o=0;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[o++]:o++]}for(;a>o;o++)u=i?i[o]:o,r=t(r,n[u],u,n);return r},h.reduceRight=h.foldr=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[--a]:--a]}for(;a--;)u=i?i[a]:a,r=t(r,n[u],u,n);return r},h.find=h.detect=function(n,t,r){var e;return t=h.iteratee(t,r),h.some(n,function(n,r,u){return t(n,r,u)?(e=n,!0):void 0}),e},h.filter=h.select=function(n,t,r){var e=[];return null==n?e:(t=h.iteratee(t,r),h.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e)},h.reject=function(n,t,r){return h.filter(n,h.negate(h.iteratee(t)),r)},h.every=h.all=function(n,t,r){if(null==n)return!0;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,!t(n[u],u,n))return!1;return!0},h.some=h.any=function(n,t,r){if(null==n)return!1;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,t(n[u],u,n))return!0;return!1},h.contains=h.include=function(n,t){return null==n?!1:(n.length!==+n.length&&(n=h.values(n)),h.indexOf(n,t)>=0)},h.invoke=function(n,t){var r=a.call(arguments,2),e=h.isFunction(t);return h.map(n,function(n){return(e?t:n[t]).apply(n,r)})},h.pluck=function(n,t){return h.map(n,h.property(t))},h.where=function(n,t){return h.filter(n,h.matches(t))},h.findWhere=function(n,t){return h.find(n,h.matches(t))},h.max=function(n,t,r){var e,u,i=-1/0,a=-1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],e>i&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(u>a||u===-1/0&&i===-1/0)&&(i=n,a=u)});return i},h.min=function(n,t,r){var e,u,i=1/0,a=1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],i>e&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(a>u||1/0===u&&1/0===i)&&(i=n,a=u)});return i},h.shuffle=function(n){for(var t,r=n&&n.length===+n.length?n:h.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=h.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},h.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=h.values(n)),n[h.random(n.length-1)]):h.shuffle(n).slice(0,Math.max(0,t))},h.sortBy=function(n,t,r){return t=h.iteratee(t,r),h.pluck(h.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var m=function(n){return function(t,r,e){var u={};return r=h.iteratee(r,e),h.each(t,function(e,i){var a=r(e,i,t);n(u,e,a)}),u}};h.groupBy=m(function(n,t,r){h.has(n,r)?n[r].push(t):n[r]=[t]}),h.indexBy=m(function(n,t,r){n[r]=t}),h.countBy=m(function(n,t,r){h.has(n,r)?n[r]++:n[r]=1}),h.sortedIndex=function(n,t,r,e){r=h.iteratee(r,e,1);for(var u=r(t),i=0,a=n.length;a>i;){var o=i+a>>>1;r(n[o])t?[]:a.call(n,0,t)},h.initial=function(n,t,r){return a.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},h.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:a.call(n,Math.max(n.length-t,0))},h.rest=h.tail=h.drop=function(n,t,r){return a.call(n,null==t||r?1:t)},h.compact=function(n){return h.filter(n,h.identity)};var y=function(n,t,r,e){if(t&&h.every(n,h.isArray))return o.apply(e,n);for(var u=0,a=n.length;a>u;u++){var l=n[u];h.isArray(l)||h.isArguments(l)?t?i.apply(e,l):y(l,t,r,e):r||e.push(l)}return e};h.flatten=function(n,t){return y(n,t,!1,[])},h.without=function(n){return h.difference(n,a.call(arguments,1))},h.uniq=h.unique=function(n,t,r,e){if(null==n)return[];h.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=h.iteratee(r,e));for(var u=[],i=[],a=0,o=n.length;o>a;a++){var l=n[a];if(t)a&&i===l||u.push(l),i=l;else if(r){var c=r(l,a,n);h.indexOf(i,c)<0&&(i.push(c),u.push(l))}else h.indexOf(u,l)<0&&u.push(l)}return u},h.union=function(){return h.uniq(y(arguments,!0,!0,[]))},h.intersection=function(n){if(null==n)return[];for(var t=[],r=arguments.length,e=0,u=n.length;u>e;e++){var i=n[e];if(!h.contains(t,i)){for(var a=1;r>a&&h.contains(arguments[a],i);a++);a===r&&t.push(i)}}return t},h.difference=function(n){var t=y(a.call(arguments,1),!0,!0,[]);return h.filter(n,function(n){return!h.contains(t,n)})},h.zip=function(n){if(null==n)return[];for(var t=h.max(arguments,"length").length,r=Array(t),e=0;t>e;e++)r[e]=h.pluck(arguments,e);return r},h.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},h.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=h.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}for(;u>e;e++)if(n[e]===t)return e;return-1},h.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=n.length;for("number"==typeof r&&(e=0>r?e+r+1:Math.min(e,r+1));--e>=0;)if(n[e]===t)return e;return-1},h.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var d=function(){};h.bind=function(n,t){var r,e;if(p&&n.bind===p)return p.apply(n,a.call(arguments,1));if(!h.isFunction(n))throw new TypeError("Bind must be called on a function");return r=a.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(a.call(arguments)));d.prototype=n.prototype;var u=new d;d.prototype=null;var i=n.apply(u,r.concat(a.call(arguments)));return h.isObject(i)?i:u}},h.partial=function(n){var t=a.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===h&&(e[u]=arguments[r++]);for(;r=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=h.bind(n[r],n);return n},h.memoize=function(n,t){var r=function(e){var u=r.cache,i=t?t.apply(this,arguments):e;return h.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},h.delay=function(n,t){var r=a.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},h.defer=function(n){return h.delay.apply(h,[n,1].concat(a.call(arguments,1)))},h.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var l=function(){o=r.leading===!1?0:h.now(),a=null,i=n.apply(e,u),a||(e=u=null)};return function(){var c=h.now();o||r.leading!==!1||(o=c);var f=t-(c-o);return e=this,u=arguments,0>=f||f>t?(clearTimeout(a),a=null,o=c,i=n.apply(e,u),a||(e=u=null)):a||r.trailing===!1||(a=setTimeout(l,f)),i}},h.debounce=function(n,t,r){var e,u,i,a,o,l=function(){var c=h.now()-a;t>c&&c>0?e=setTimeout(l,t-c):(e=null,r||(o=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,a=h.now();var c=r&&!e;return e||(e=setTimeout(l,t)),c&&(o=n.apply(i,u),i=u=null),o}},h.wrap=function(n,t){return h.partial(t,n)},h.negate=function(n){return function(){return!n.apply(this,arguments)}},h.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},h.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},h.before=function(n,t){var r;return function(){return--n>0?r=t.apply(this,arguments):t=null,r}},h.once=h.partial(h.before,2),h.keys=function(n){if(!h.isObject(n))return[];if(s)return s(n);var t=[];for(var r in n)h.has(n,r)&&t.push(r);return t},h.values=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},h.pairs=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},h.invert=function(n){for(var t={},r=h.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},h.functions=h.methods=function(n){var t=[];for(var r in n)h.isFunction(n[r])&&t.push(r);return t.sort()},h.extend=function(n){if(!h.isObject(n))return n;for(var t,r,e=1,u=arguments.length;u>e;e++){t=arguments[e];for(r in t)c.call(t,r)&&(n[r]=t[r])}return n},h.pick=function(n,t,r){var e,u={};if(null==n)return u;if(h.isFunction(t)){t=g(t,r);for(e in n){var i=n[e];t(i,e,n)&&(u[e]=i)}}else{var l=o.apply([],a.call(arguments,1));n=new Object(n);for(var c=0,f=l.length;f>c;c++)e=l[c],e in n&&(u[e]=n[e])}return u},h.omit=function(n,t,r){if(h.isFunction(t))t=h.negate(t);else{var e=h.map(o.apply([],a.call(arguments,1)),String);t=function(n,t){return!h.contains(e,t)}}return h.pick(n,t,r)},h.defaults=function(n){if(!h.isObject(n))return n;for(var t=1,r=arguments.length;r>t;t++){var e=arguments[t];for(var u in e)n[u]===void 0&&(n[u]=e[u])}return n},h.clone=function(n){return h.isObject(n)?h.isArray(n)?n.slice():h.extend({},n):n},h.tap=function(n,t){return t(n),n};var b=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof h&&(n=n._wrapped),t instanceof h&&(t=t._wrapped);var u=l.call(n);if(u!==l.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]===n)return e[i]===t;var a=n.constructor,o=t.constructor;if(a!==o&&"constructor"in n&&"constructor"in t&&!(h.isFunction(a)&&a instanceof a&&h.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c,f;if("[object Array]"===u){if(c=n.length,f=c===t.length)for(;c--&&(f=b(n[c],t[c],r,e)););}else{var s,p=h.keys(n);if(c=p.length,f=h.keys(t).length===c)for(;c--&&(s=p[c],f=h.has(t,s)&&b(n[s],t[s],r,e)););}return r.pop(),e.pop(),f};h.isEqual=function(n,t){return b(n,t,[],[])},h.isEmpty=function(n){if(null==n)return!0;if(h.isArray(n)||h.isString(n)||h.isArguments(n))return 0===n.length;for(var t in n)if(h.has(n,t))return!1;return!0},h.isElement=function(n){return!(!n||1!==n.nodeType)},h.isArray=f||function(n){return"[object Array]"===l.call(n)},h.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},h.each(["Arguments","Function","String","Number","Date","RegExp"],function(n){h["is"+n]=function(t){return l.call(t)==="[object "+n+"]"}}),h.isArguments(arguments)||(h.isArguments=function(n){return h.has(n,"callee")}),"function"!=typeof/./&&(h.isFunction=function(n){return"function"==typeof n||!1}),h.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},h.isNaN=function(n){return h.isNumber(n)&&n!==+n},h.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===l.call(n)},h.isNull=function(n){return null===n},h.isUndefined=function(n){return n===void 0},h.has=function(n,t){return null!=n&&c.call(n,t)},h.noConflict=function(){return n._=t,this},h.identity=function(n){return n},h.constant=function(n){return function(){return n}},h.noop=function(){},h.property=function(n){return function(t){return t[n]}},h.matches=function(n){var t=h.pairs(n),r=t.length;return function(n){if(null==n)return!r;n=new Object(n);for(var e=0;r>e;e++){var u=t[e],i=u[0];if(u[1]!==n[i]||!(i in n))return!1}return!0}},h.times=function(n,t,r){var e=Array(Math.max(0,n));t=g(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},h.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},h.now=Date.now||function(){return(new Date).getTime()};var _={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},w=h.invert(_),j=function(n){var t=function(t){return n[t]},r="(?:"+h.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};h.escape=j(_),h.unescape=j(w),h.result=function(n,t){if(null==n)return void 0;var r=n[t];return h.isFunction(r)?n[t]():r};var x=0;h.uniqueId=function(n){var t=++x+"";return n?n+t:t},h.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var A=/(.)^/,k={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},O=/\\|'|\r|\n|\u2028|\u2029/g,F=function(n){return"\\"+k[n]};h.template=function(n,t,r){!t&&r&&(t=r),t=h.defaults({},t,h.templateSettings);var e=RegExp([(t.escape||A).source,(t.interpolate||A).source,(t.evaluate||A).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(O,F),u=o+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":a&&(i+="';\n"+a+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=new Function(t.variable||"obj","_",i)}catch(o){throw o.source=i,o}var l=function(n){return a.call(this,n,h)},c=t.variable||"obj";return l.source="function("+c+"){\n"+i+"}",l},h.chain=function(n){var t=h(n);return t._chain=!0,t};var E=function(n){return this._chain?h(n).chain():n};h.mixin=function(n){h.each(h.functions(n),function(t){var r=h[t]=n[t];h.prototype[t]=function(){var n=[this._wrapped];return i.apply(n,arguments),E.call(this,r.apply(h,n))}})},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=r[n];h.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],E.call(this,r)}}),h.each(["concat","join","slice"],function(n){var t=r[n];h.prototype[n]=function(){return E.call(this,t.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}).call(this); -//# sourceMappingURL=underscore-min.map \ No newline at end of file diff --git a/public/js/services.js b/public/js/services.js index a0d2f0d..02eb73c 100644 --- a/public/js/services.js +++ b/public/js/services.js @@ -27,7 +27,7 @@ app.factory('socket', function ($rootScope) { // }; }); -var underscore = angular.module('underscore', []); -underscore.factory('_', function() { +var lodash = angular.module('lodash', []); +lodash.factory('_', function() { return window._; }); \ No newline at end of file diff --git a/views/layout.jade b/views/layout.jade index 2bcacfd..73bc3dd 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -13,7 +13,7 @@ html(ng-app="netStatsApp") script(src="/js/lib/angular.min.js") script(src="/primus/primus.js") - script(src="/js/lib/underscore.min.js") + script(src="/js/lib/lodash.min.js") script(src="/js/lib/jquery.min.js") script(src="/js/lib/d3.v3.min.js") script(src="/js/lib/topojson.v1.min.js") From 8681622a1a896e508b44d7959c43559efa970ad2 Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 11:40:43 +0200 Subject: [PATCH 05/11] removed unnecessary dependencies --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 4e3a25a..eb56f0a 100644 --- a/package.json +++ b/package.json @@ -12,16 +12,13 @@ "dependencies": { "body-parser": "~1.8.1", "debug": "~2.0.0", - "ethereum.js": "*", "express": "^4.11.2", "geoip-lite": "^1.1.4", "jade": "~1.6.0", "lodash": "^3.2.0", - "morgan": "~1.3.0", "primus": "^2.4.12", "primus-emit": "^0.1.2", "serve-favicon": "~2.1.3", - "stylus": "0.42.3", "ws": "^0.7.1" } } From 3b3cde669e2e7af710c473b67f46a2031b588e61 Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 12:14:48 +0200 Subject: [PATCH 06/11] added Toastr --- app.js | 10 +++++++--- public/css/toastr.min.css | 1 + public/js/controllers.js | 27 ++++++++++++++++----------- public/js/lib/toastr.min.js | 1 + public/js/services.js | 22 ++++++++++++++++++++++ views/layout.jade | 2 ++ 6 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 public/css/toastr.min.css create mode 100644 public/js/lib/toastr.min.js diff --git a/app.js b/app.js index 7fdfe6f..519d281 100644 --- a/app.js +++ b/app.js @@ -34,8 +34,8 @@ api.on('connection', function(spark) { console.log(spark.address); console.log(spark.query); - spark.on('hello', function(data){ - console.log('got hello data from ' + spark.id); + spark.on('hello', function(data) { + console.log('got hello data from ', spark.id); console.log(data); if(typeof data.id !== 'undefined' && typeof data.info !== 'undefined') @@ -49,7 +49,7 @@ api.on('connection', function(spark) { } }); - spark.on('update', function(data){ + spark.on('update', function(data) { console.log('got update from ' + spark.id); console.log(data); @@ -60,6 +60,10 @@ api.on('connection', function(spark) { client.write({action: 'update', data: stats}); } }); + + spark.on('end', function(data) { + // + }); }); client.on('connection', function(spark) { diff --git a/public/css/toastr.min.css b/public/css/toastr.min.css new file mode 100644 index 0000000..613c47b --- /dev/null +++ b/public/css/toastr.min.css @@ -0,0 +1 @@ +.toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:alpha(Opacity=80);filter:alpha(opacity=80)}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:alpha(Opacity=40);filter:alpha(opacity=40)}button.toast-close-button{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8;-ms-filter:alpha(Opacity=80);filter:alpha(opacity=80)}#toast-container>:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:alpha(Opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}} \ No newline at end of file diff --git a/public/js/controllers.js b/public/js/controllers.js index 41456e6..07f342d 100644 --- a/public/js/controllers.js +++ b/public/js/controllers.js @@ -2,7 +2,7 @@ /* Controllers */ -function StatsCtrl($scope, $filter, socket, _) { +function StatsCtrl($scope, $filter, socket, _, toastr) { // Main Stats init // --------------- @@ -13,6 +13,7 @@ function StatsCtrl($scope, $filter, socket, _) { $scope.lastBlock = 0; $scope.upTimeTotal = 0; + $scope.nodes = []; $scope.map = []; // Socket listeners @@ -25,7 +26,7 @@ function StatsCtrl($scope, $filter, socket, _) { console.log('The connection has been opened.'); }) .on('end', function end() { - self._socket = false; + console.log('Socket connection ended.') }) .on('error', function error(err) { console.log(err); @@ -34,35 +35,39 @@ function StatsCtrl($scope, $filter, socket, _) { console.log('We are scheduling a reconnect operation', opts); }) .on('data', function incoming(data) { - console.log('Received some data', data); socketAction(data.action, data.data); }); socket.on('init', function(data) { - console.log(data); - - $scope.nodes = data.nodes; - - updateStats(); + socketAction("init", data.nodes); }); function socketAction(action, data) { + console.log('Action: ', action); + console.log('Data: ', data); + switch(action) { + case "init": + $scope.nodes = data; + if($scope.nodes.length > 0){ + toastr['success']("Got nodes list", "Got nodes!"); + } + break; + case "add": - console.log(data); $scope.nodes.push(data); + toastr['success']("New node connected!", "New node!"); break; case "update": - console.log(data); $scope.nodes[findIndex({id: data.id})].stats = data.stats; break; case "info": - console.log(data); $scope.nodes[findIndex({id: data.id})].info = data.info; + break; } updateStats(); diff --git a/public/js/lib/toastr.min.js b/public/js/lib/toastr.min.js new file mode 100644 index 0000000..b322e5b --- /dev/null +++ b/public/js/lib/toastr.min.js @@ -0,0 +1 @@ +!function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return f({type:O.error,iconClass:g().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=g()),v=e("#"+t.containerId),v.length?v:(n&&(v=c(t)),v)}function i(e,t,n){return f({type:O.info,iconClass:g().iconClasses.info,message:e,optionsOverride:n,title:t})}function o(e){w=e}function s(e,t,n){return f({type:O.success,iconClass:g().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return f({type:O.warning,iconClass:g().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e){var t=g();v||n(t),l(e,t)||u(t)}function d(t){var i=g();return v||n(i),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function u(t){for(var n=v.children(),i=n.length-1;i>=0;i--)l(e(n[i]),t)}function l(t,n){return t&&0===e(":focus",t).length?(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0):!1}function c(t){return v=e("
").attr("id",t.containerId).addClass(t.positionClass).attr("aria-live","polite").attr("role","alert"),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",target:"body",closeHtml:"",newestOnTop:!0,preventDuplicates:!1,progressBar:!1}}function m(e){w&&w(e)}function f(t){function i(t){return!e(":focus",l).length||t?(clearTimeout(O.intervalId),l[r.hideMethod]({duration:r.hideDuration,easing:r.hideEasing,complete:function(){h(l),r.onHidden&&"hidden"!==b.state&&r.onHidden(),b.state="hidden",b.endTime=new Date,m(b)}})):void 0}function o(){(r.timeOut>0||r.extendedTimeOut>0)&&(u=setTimeout(i,r.extendedTimeOut),O.maxHideTime=parseFloat(r.extendedTimeOut),O.hideEta=(new Date).getTime()+O.maxHideTime)}function s(){clearTimeout(u),O.hideEta=0,l.stop(!0,!0)[r.showMethod]({duration:r.showDuration,easing:r.showEasing})}function a(){var e=(O.hideEta-(new Date).getTime())/O.maxHideTime*100;f.width(e+"%")}var r=g(),d=t.iconClass||r.iconClass;if(r.preventDuplicates){if(t.message===C)return;C=t.message}"undefined"!=typeof t.optionsOverride&&(r=e.extend(r,t.optionsOverride),d=t.optionsOverride.iconClass||d),T++,v=n(r,!0);var u=null,l=e("
"),c=e("
"),p=e("
"),f=e("
"),w=e(r.closeHtml),O={intervalId:null,hideEta:null,maxHideTime:null},b={toastId:T,state:"visible",startTime:new Date,options:r,map:t};return t.iconClass&&l.addClass(r.toastClass).addClass(d),t.title&&(c.append(t.title).addClass(r.titleClass),l.append(c)),t.message&&(p.append(t.message).addClass(r.messageClass),l.append(p)),r.closeButton&&(w.addClass("toast-close-button").attr("role","button"),l.prepend(w)),r.progressBar&&(f.addClass("toast-progress"),l.prepend(f)),l.hide(),r.newestOnTop?v.prepend(l):v.append(l),l[r.showMethod]({duration:r.showDuration,easing:r.showEasing,complete:r.onShown}),r.timeOut>0&&(u=setTimeout(i,r.timeOut),O.maxHideTime=parseFloat(r.timeOut),O.hideEta=(new Date).getTime()+O.maxHideTime,r.progressBar&&(O.intervalId=setInterval(a,10))),l.hover(s,o),!r.onclick&&r.tapToDismiss&&l.click(i),r.closeButton&&w&&w.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),i(!0)}),r.onclick&&l.click(function(){r.onclick(),i()}),m(b),r.debug&&console&&console.log(b),l}function g(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&v.remove())}var v,w,C,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:d,error:t,getContainer:n,info:i,options:{},subscribe:o,success:s,version:"2.1.0",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)}); \ No newline at end of file diff --git a/public/js/services.js b/public/js/services.js index 02eb73c..dbde145 100644 --- a/public/js/services.js +++ b/public/js/services.js @@ -27,6 +27,28 @@ app.factory('socket', function ($rootScope) { // }; }); +app.factory('toastr', function ($rootScope) { + toastr = window.toastr; + toastr.options = { + "closeButton": false, + "debug": false, + "newestOnTop": true, + "progressBar": false, + "positionClass": "toast-top-right", + "preventDuplicates": false, + "onclick": null, + "showDuration": "300", + "hideDuration": "1000", + "timeOut": "5000", + "extendedTimeOut": "1000", + "showEasing": "swing", + "hideEasing": "linear", + "showMethod": "fadeIn", + "hideMethod": "fadeOut" + }; + return toastr; +}); + var lodash = angular.module('lodash', []); lodash.factory('_', function() { return window._; diff --git a/views/layout.jade b/views/layout.jade index 73bc3dd..49e72be 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -5,6 +5,7 @@ html(ng-app="netStatsApp") title= title link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600,700') link(rel='stylesheet', href='/css/bootstrap.min.css') + link(rel='stylesheet', href='/css/toastr.min.css') link(rel='stylesheet', href='/css/minimal-icons.css') link(rel='stylesheet', href='/css/animation.css') link(rel='stylesheet', href='/css/style.css') @@ -19,6 +20,7 @@ html(ng-app="netStatsApp") script(src="/js/lib/topojson.v1.min.js") script(src="/js/lib/datamaps.min.js") script(src="/js/lib/moment.min.js") + script(src="/js/lib/toastr.min.js") script(src="/js/lib/locale/en-gb.js") script(src="/js/lib/angular-moment.min.js") script(src="/js/lib/bootstrap.min.js") From 71c24b3e6ac3f1927cb5d68a145b563c896d9c5a Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 16:07:50 +0200 Subject: [PATCH 07/11] node version filter --- public/js/filters.js | 5 +++++ views/index.jade | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/public/js/filters.js b/public/js/filters.js index cc23bea..882aec7 100644 --- a/public/js/filters.js +++ b/public/js/filters.js @@ -39,6 +39,11 @@ angular.module('netStatsApp.filters', []) return (! mining ? 'icon-cancel' : 'icon-check'); }; }) +.filter('nodeVersion', function() { + return function(version) { + return version.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '
' + '$2') + } +}) .filter('blockClass', function() { return function(current, best) { return (best - current <= 1 ? 'text-success' : (best - current > 1 && best - current < 4 ? 'text-warning' : 'text-danger')); diff --git a/views/index.jade b/views/index.jade index 0a7b745..fbc335e 100644 --- a/views/index.jade +++ b/views/index.jade @@ -77,7 +77,7 @@ block content td(rel="{{node.id}}") span(data-toggle="tooltip", data-placement="top", data-original-title="{{node.geo | geoTooltip}}") {{node.info.name}} div.small {{node.info.ip}} - td {{node.info.node}} + td.small {{ node.info.node | nodeVersion }} div.small {{node.info.os}}, {{node.info.os_v}} td(class="{{ node.stats.peers | peerClass }}") {{node.stats.peers}} td(class="{{ node.stats.mining | miningClass }}") From a2af189c990fe07c90dada9280207eeff43a7220 Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 16:13:05 +0200 Subject: [PATCH 08/11] fixed node version filter --- views/index.jade | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/views/index.jade b/views/index.jade index fbc335e..e4d30fd 100644 --- a/views/index.jade +++ b/views/index.jade @@ -77,7 +77,8 @@ block content td(rel="{{node.id}}") span(data-toggle="tooltip", data-placement="top", data-original-title="{{node.geo | geoTooltip}}") {{node.info.name}} div.small {{node.info.ip}} - td.small {{ node.info.node | nodeVersion }} + td + span.small(ng-bind-html="node.info.node | nodeVersion") div.small {{node.info.os}}, {{node.info.os_v}} td(class="{{ node.stats.peers | peerClass }}") {{node.stats.peers}} td(class="{{ node.stats.mining | miningClass }}") From 35072afb90fab6557fad8e22fba2c516f94f8302 Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 16:52:38 +0200 Subject: [PATCH 09/11] fixed node version filter 2 --- views/index.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/index.jade b/views/index.jade index e4d30fd..c0131c1 100644 --- a/views/index.jade +++ b/views/index.jade @@ -77,8 +77,8 @@ block content td(rel="{{node.id}}") span(data-toggle="tooltip", data-placement="top", data-original-title="{{node.geo | geoTooltip}}") {{node.info.name}} div.small {{node.info.ip}} - td - span.small(ng-bind-html="node.info.node | nodeVersion") + td.small + span(ng-bind-html-unsafe="node.info.node | nodeVersion") div.small {{node.info.os}}, {{node.info.os_v}} td(class="{{ node.stats.peers | peerClass }}") {{node.stats.peers}} td(class="{{ node.stats.mining | miningClass }}") From 9f93f7059b0566bffb67ba8d939404a66b68f5dc Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 17:29:26 +0200 Subject: [PATCH 10/11] node version parser --- public/js/filters.js | 10 +++++++--- views/index.jade | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/public/js/filters.js b/public/js/filters.js index 882aec7..3a52971 100644 --- a/public/js/filters.js +++ b/public/js/filters.js @@ -39,10 +39,14 @@ angular.module('netStatsApp.filters', []) return (! mining ? 'icon-cancel' : 'icon-check'); }; }) -.filter('nodeVersion', function() { +.filter('nodeVersion', function($sce) { return function(version) { - return version.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '
' + '$2') - } + version = version.replace('eth version ', 'v') + .replace("\n" + 'Network protocol version: ', ' (') + .replace("\n" + 'Client database version: ', ',') + .replace("\n" + 'Build: ', ')
'); + return $sce.trustAsHtml(version); + }; }) .filter('blockClass', function() { return function(current, best) { diff --git a/views/index.jade b/views/index.jade index c0131c1..1d9dd05 100644 --- a/views/index.jade +++ b/views/index.jade @@ -78,7 +78,7 @@ block content span(data-toggle="tooltip", data-placement="top", data-original-title="{{node.geo | geoTooltip}}") {{node.info.name}} div.small {{node.info.ip}} td.small - span(ng-bind-html-unsafe="node.info.node | nodeVersion") + div.small(ng-bind-html="node.info.node | nodeVersion") div.small {{node.info.os}}, {{node.info.os_v}} td(class="{{ node.stats.peers | peerClass }}") {{node.stats.peers}} td(class="{{ node.stats.mining | miningClass }}") From b347f825a461425a6ac8e596354d4b5e65ab2735 Mon Sep 17 00:00:00 2001 From: cubedro Date: Tue, 17 Feb 2015 20:14:38 +0200 Subject: [PATCH 11/11] added reconnect functionality --- public/js/controllers.js | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/public/js/controllers.js b/public/js/controllers.js index 07f342d..b74d506 100644 --- a/public/js/controllers.js +++ b/public/js/controllers.js @@ -51,14 +51,21 @@ function StatsCtrl($scope, $filter, socket, _, toastr) { switch(action) { case "init": $scope.nodes = data; - if($scope.nodes.length > 0){ + + if($scope.nodes.length > 0) + { toastr['success']("Got nodes list", "Got nodes!"); } break; case "add": - $scope.nodes.push(data); - toastr['success']("New node connected!", "New node!"); + if(addNewNode(data)) + { + toastr['success']("New node connected!", "New node!"); + } else { + toastr['info']("Node reconnected!", "Node is back!"); + } + break; case "update": @@ -78,6 +85,21 @@ function StatsCtrl($scope, $filter, socket, _, toastr) { return _.findIndex($scope.nodes, search); } + function addNewNode(data) + { + var index = findIndex({id: data.id}); + if(index < 0) + { + $scope.nodes.push(data); + + return true; + } + + $scope.nodes[index] = data; + + return false; + } + function updateStats() { if($scope.nodes.length)