diff --git a/app.js b/app.js index 8e30469..516dc61 100644 --- a/app.js +++ b/app.js @@ -437,6 +437,12 @@ app.set('website', settings.website); app.set('genesis_block', settings.genesis_block); app.set('index', settings.index); +app.set('reward_page', settings.reward_page); +app.set('masternodes_page', settings.masternodes_page); +app.set('movement_page', settings.movement_page); +app.set('network_page', settings.network_page); +app.set('richlist_page', settings.richlist_page); +app.set('markets_page', settings.markets_page); app.set('use_rpc', settings.use_rpc); app.set('heavy', settings.heavy); app.set('save_stats_after_sync_blocks', settings.save_stats_after_sync_blocks); diff --git a/lib/database.js b/lib/database.js index f5d48fa..ce5d0ac 100644 --- a/lib/database.js +++ b/lib/database.js @@ -759,10 +759,12 @@ module.exports = { return cb(distribution); }); }, + // updates heavy stats for coin // height: current block height, count: amount of votes to store update_heavy: function(coin, height, count, cb) { var newVotes = []; + lib.get_maxmoney( function (maxmoney) { lib.get_maxvote( function (maxvote) { lib.get_vote( function (vote) { @@ -773,14 +775,13 @@ module.exports = { lib.get_nextin( function (nextin) { lib.syncLoop(count, function (loop) { var i = loop.iteration(); - lib.get_blockhash(height-i, function (hash) { + lib.get_blockhash(height - i, function (hash) { lib.get_block(hash, function (block) { - newVotes.push({count:height-i,reward:block.reward,vote:(block && block.vote ? block.vote : 0)}); + newVotes.push({ count: height - i, reward: block.reward, vote: (block && block.vote ? block.vote : 0) }); loop.next(); }); }); - }, function(){ - console.log(newVotes); + }, function() { Heavy.updateOne({coin: coin}, { lvote: (vote ? vote : 0), reward: (reward ? reward : 0), @@ -790,10 +791,13 @@ module.exports = { phase: (phase ? phase : 'N/A'), maxvote: (maxvote ? maxvote : 0), nextin: (nextin ? nextin : 'N/A'), - votes: newVotes, + votes: newVotes }, function() { - //console.log('address updated: %s', hash); - return cb(); + // update reward_last_updated value + module.exports.update_last_updated_stats(settings.coin, { reward_last_updated: Math.floor(new Date() / 1000) }, function (new_cb) { + console.log('heavy update complete'); + return cb(); + }); }); }); }); @@ -1195,13 +1199,48 @@ module.exports = { // updates last_updated stats; called by sync.js update_last_updated_stats: function (coin, param, cb) { - if (param.masternodes_last_updated) { + if (param.blockchain_last_updated) { + // update blockchain last updated date + Stats.updateOne({ coin: coin }, { + blockchain_last_updated: param.blockchain_last_updated + }, function () { + return cb(true); + }); + } else if (param.reward_last_updated) { + // update reward last updated date + Stats.updateOne({ coin: coin }, { + reward_last_updated: param.reward_last_updated + }, function () { + return cb(true); + }); + } else if (param.masternodes_last_updated) { // update masternode last updated date Stats.updateOne({ coin: coin }, { masternodes_last_updated: param.masternodes_last_updated }, function () { return cb(true); }); + } else if (param.network_last_updated) { + // update network last updated date + Stats.updateOne({ coin: coin }, { + network_last_updated: param.network_last_updated + }, function () { + return cb(true); + }); + } else if (param.richlist_last_updated) { + // update richlist last updated date + Stats.updateOne({ coin: coin }, { + richlist_last_updated: param.richlist_last_updated + }, function () { + return cb(true); + }); + } else if (param.markets_last_updated) { + // update markets last updated date + Stats.updateOne({ coin: coin }, { + markets_last_updated: param.markets_last_updated + }, function () { + return cb(true); + }); } else { // invalid option return cb(false); diff --git a/lib/settings.js b/lib/settings.js index 752e638..51f95ae 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -140,12 +140,50 @@ exports.movement = { //index exports.index = { + // show_last_updated: determine whether to show a label above the transaction data with the last updated date + "show_last_updated": true, "show_hashrate": false, "difficulty": "POS", "last_txs": 100, "txs_per_page": 10 }; +// reward page +exports.reward_page = { + // show_last_updated: determine whether to show a label above the reward data with the last updated date + "show_last_updated": true +}; + +// masternodes page +exports.masternodes_page = { + // show_last_updated: determine whether to show a label above the masternode data with the last updated date + "show_last_updated": true +}; + +// movement page +exports.movement_page = { + // show_last_updated: determine whether to show a label above the movement data with the last updated date + "show_last_updated": true +}; + +// network page +exports.network_page = { + // show_last_updated: determine whether to show a label above the network data with the last updated date + "show_last_updated": true +}; + +// richlist page +exports.richlist_page = { + // show_last_updated: determine whether to show a label above the richlist data with the last updated date + "show_last_updated": true +}; + +// markets page +exports.markets_page = { + // show_last_updated: determine whether to show a label above the market data with the last updated date + "show_last_updated": true +}; + // twitter, facebook, googleplus, bitcointalk, github, slack, discord, telegram, reddit, youtube, website exports.twitter = "your-twitter-username"; exports.facebook = "your-facebook-username"; diff --git a/models/stats.js b/models/stats.js index 0511ed9..5d3de7f 100644 --- a/models/stats.js +++ b/models/stats.js @@ -10,7 +10,12 @@ var StatsSchema = new Schema({ connections: { type: Number, default: 0 }, last_price: { type: Number, default: 0 }, last_usd_price: { type: Number, default: 0 }, - masternodes_last_updated: { type: Number, default: 0 } + blockchain_last_updated: { type: Number, default: 0 }, + reward_last_updated: { type: Number, default: 0 }, + masternodes_last_updated: { type: Number, default: 0 }, + network_last_updated: { type: Number, default: 0 }, + richlist_last_updated: { type: Number, default: 0 }, + markets_last_updated: { type: Number, default: 0 } }); module.exports = mongoose.model('coinstats', StatsSchema); \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index b8dd07e..03bd323 100644 --- a/routes/index.js +++ b/routes/index.js @@ -150,7 +150,16 @@ function route_get_tx(res, txid) { } function route_get_index(res, error) { - res.render('index', { active: 'home', error: error, showSync: db.check_show_sync_message()}); + // check if index page should show last updated date + if (settings.index.show_last_updated == true) { + // lookup last updated date + db.get_stats(settings.coin, function (stats) { + res.render('index', { active: 'home', error: error, last_updated: stats.blockchain_last_updated, showSync: db.check_show_sync_message()}); + }); + } else { + // skip lookup up the last updated date and display the page now + res.render('index', { active: 'home', error: error, last_updated: null, showSync: db.check_show_sync_message()}); + } } function route_get_address(res, hash, count) { @@ -191,36 +200,67 @@ router.get('/info', function(req, res) { }); router.get('/markets/:market', function(req, res) { - var market = req.params['market']; - if (settings.markets.enabled.indexOf(market) != -1) { - db.get_market(market, function(data) { - var exMarket = require('../lib/markets/' + market); - res.render('./market', { - active: 'markets', - marketdata: { - market_name: (exMarket.market_name == null ? '' : exMarket.market_name), - market_logo: (exMarket.market_logo == null ? '' : exMarket.market_logo), - coin: settings.markets.coin, - exchange: settings.markets.exchange, - data: data, - }, - market: market, - showSync: db.check_show_sync_message() + // ensure markets page is enabled + if (settings.display.markets == true) { + var market_id = req.params['market']; + + if (settings.markets.enabled.indexOf(market_id) != -1) { + // lookup market data + db.get_market(market_id, function(data) { + // load market data + var market_data = require('../lib/markets/' + market_id); + // check if markets page should show last updated date + if (settings.markets_page.show_last_updated == true) { + // lookup last updated date + db.get_stats(settings.coin, function (stats) { + res.render('./market', { + active: 'markets', + marketdata: { + market_name: (market_data.market_name == null ? '' : market_data.market_name), + market_logo: (market_data.market_logo == null ? '' : market_data.market_logo), + coin: settings.markets.coin, + exchange: settings.markets.exchange, + data: data, + }, + market: market_id, + last_updated: stats.markets_last_updated, + showSync: db.check_show_sync_message() + }); + }); + } else { + // skip lookup up the last updated date and display the page now + res.render('./market', { + active: 'markets', + marketdata: { + market_name: (market_data.market_name == null ? '' : market_data.market_name), + market_logo: (market_data.market_logo == null ? '' : market_data.market_logo), + coin: settings.markets.coin, + exchange: settings.markets.exchange, + data: data, + }, + market: market_id, + last_updated: null, + showSync: db.check_show_sync_message() + }); + } }); - }); + } else { + // selected market is not enabled so default to the index page + route_get_index(res, null); + } } else { + // markets page is not enabled so default to the index page route_get_index(res, null); } }); router.get('/richlist', function(req, res) { - if (settings.display.richlist == true ) { + // ensure richlist page is enabled + if (settings.display.richlist == true) { db.get_stats(settings.coin, function (stats) { - db.get_richlist(settings.coin, function(richlist){ - //console.log(richlist); + db.get_richlist(settings.coin, function(richlist) { if (richlist) { db.get_distribution(richlist, stats, function(distribution) { - //console.log(distribution); res.render('richlist', { active: 'richlist', balance: richlist.balance, @@ -234,35 +274,74 @@ router.get('/richlist', function(req, res) { show_dist: settings.richlist.distribution, show_received: settings.richlist.received, show_balance: settings.richlist.balance, - showSync: db.check_show_sync_message() + last_updated: (settings.richlist_page.show_last_updated == true ? stats.richlist_last_updated : null), + showSync: db.check_show_sync_message() }); }); } else { + // richlist data not found so default to the index page route_get_index(res, null); } }); }); } else { + // richlist page is not enabled so default to the index page route_get_index(res, null); } }); router.get('/movement', function(req, res) { - res.render('movement', {active: 'movement', flaga: settings.movement.low_flag, flagb: settings.movement.high_flag, min_amount:settings.movement.min_amount, showSync: db.check_show_sync_message()}); + // ensure movement page is enabled + if (settings.display.movement == true) { + // check if movement page should show last updated date + if (settings.movement_page.show_last_updated == true) { + // lookup last updated date + db.get_stats(settings.coin, function (stats) { + res.render('movement', {active: 'movement', flaga: settings.movement.low_flag, flagb: settings.movement.high_flag, min_amount:settings.movement.min_amount, last_updated: stats.blockchain_last_updated, showSync: db.check_show_sync_message()}); + }); + } else { + // skip lookup up the last updated date and display the page now + res.render('movement', {active: 'movement', flaga: settings.movement.low_flag, flagb: settings.movement.high_flag, min_amount:settings.movement.min_amount, last_updated: null, showSync: db.check_show_sync_message()}); + } + } else { + // movement page is not enabled so default to the index page + route_get_index(res, null); + } }); router.get('/network', function(req, res) { - res.render('network', {active: 'network', showSync: db.check_show_sync_message()}); + // ensure network page is enabled + if (settings.display.network == true) { + // check if network page should show last updated date + if (settings.network_page.show_last_updated == true) { + // lookup last updated date + db.get_stats(settings.coin, function (stats) { + res.render('network', {active: 'network', last_updated: stats.network_last_updated, showSync: db.check_show_sync_message()}); + }); + } else { + // skip lookup up the last updated date and display the page now + res.render('network', {active: 'network', last_updated: null, showSync: db.check_show_sync_message()}); + } + } else { + // network page is not enabled so default to the index page + route_get_index(res, null); + } }); // masternode list page router.get('/masternodes', function(req, res) { // ensure masternode page is enabled if (settings.display.masternodes == true) { - // lookup last updated date - db.get_stats(settings.coin, function (stats) { - res.render('masternodes', {active: 'masternodes', last_updated: stats.masternodes_last_updated, showSync: db.check_show_sync_message()}); - }); + // check if masternodes page should show last updated date + if (settings.masternodes_page.show_last_updated == true) { + // lookup last updated date + db.get_stats(settings.coin, function (stats) { + res.render('masternodes', {active: 'masternodes', last_updated: stats.masternodes_last_updated, showSync: db.check_show_sync_message()}); + }); + } else { + // skip lookup up the last updated date and display the page now + res.render('masternodes', {active: 'masternodes', last_updated: null, showSync: db.check_show_sync_message()}); + } } else { // masternode page is not enabled so default to the index page route_get_index(res, null); @@ -270,29 +349,36 @@ router.get('/masternodes', function(req, res) { }); router.get('/reward', function(req, res) { - if (settings.heavy) { + // ensure reward page is enabled + if (settings.heavy == true) { db.get_stats(settings.coin, function (stats) { - console.log(stats); db.get_heavy(settings.coin, function (heavy) { if (!heavy) heavy = { coin: settings.coin, lvote: 0, reward: 0, supply: 0, cap: 0, estnext: 0, phase: 'N/A', maxvote: 0, nextin: 'N/A', votes: [] }; var votes = heavy.votes; - votes.sort(function (a,b) { - if (a.count < b.count) { + votes.sort(function (a, b) { + if (a.count < b.count) return -1; - } else if (a.count > b.count) { + else if (a.count > b.count) return 1; - } else { + else return 0; - } }); - res.render('reward', { active: 'reward', stats: stats, heavy: heavy, votes: votes, showSync: db.check_show_sync_message() }); + res.render('reward', { + active: 'reward', + stats: stats, + heavy: heavy, + votes: votes, + last_updated: (settings.reward_page.show_last_updated == true ? stats.reward_last_updated : null), + showSync: db.check_show_sync_message() + }); }); }); } else { + // reward page is not enabled so default to the index page route_get_index(res, null); } }); diff --git a/scripts/sync.js b/scripts/sync.js index 470e64e..a4ad210 100644 --- a/scripts/sync.js +++ b/scripts/sync.js @@ -229,8 +229,11 @@ if (database == 'peers') { } }); }, function() { - console.log('peer sync complete'); - exit(); + // update network_last_updated value + db.update_last_updated_stats(settings.coin, { network_last_updated: Math.floor(new Date() / 1000) }, function (cb) { + console.log('peer sync complete'); + exit(); + }); }); } else { console.log('no peers found'); @@ -296,43 +299,47 @@ if (database == 'peers') { exit(); } else { db.update_db(settings.coin, function(stats){ - if (settings.heavy == true) { - db.update_heavy(settings.coin, stats.count, 20, function(){ - }); - } - if (mode == 'reindex') { - Tx.deleteMany({}, function(err) { - console.log('TXs cleared.'); - Address.deleteMany({}, function(err2) { - console.log('Addresses cleared.'); - AddressTx.deleteMany({}, function(err3) { - console.log('Address TXs cleared.'); - Richlist.updateOne({coin: settings.coin}, { - received: [], - balance: [], - }, function(err3) { - Stats.updateOne({coin: settings.coin}, { - last: 0, - count: 0, - supply: 0 - }, function() { - console.log('index cleared (reindex)'); - }); + if (settings.heavy == true) + db.update_heavy(settings.coin, stats.count, 20, function() {}); + if (mode == 'reindex') { + Tx.deleteMany({}, function(err) { + console.log('TXs cleared.'); + Address.deleteMany({}, function(err2) { + console.log('Addresses cleared.'); + AddressTx.deleteMany({}, function(err3) { + console.log('Address TXs cleared.'); + Richlist.updateOne({coin: settings.coin}, { + received: [], + balance: [], + }, function(err3) { + Stats.updateOne({coin: settings.coin}, { + last: 0, + count: 0, + supply: 0 + }, function() { + console.log('index cleared (reindex)'); + }); - // Check if there are more than 1000 blocks to index - var showSync = check_show_sync_message(stats.count); + // Check if there are more than 1000 blocks to index + var showSync = check_show_sync_message(stats.count); - db.update_tx_db(settings.coin, 1, stats.count, stats.txes, settings.update_timeout, function(){ - db.update_richlist('received', function(){ - db.update_richlist('balance', function(){ - db.get_stats(settings.coin, function(nstats){ - // Check if the sync msg was showing - if (showSync) { - // Remove the sync msg - remove_sync_message(); - } - console.log('reindex complete (block: %s)', nstats.last); - exit(); + db.update_tx_db(settings.coin, 1, stats.count, stats.txes, settings.update_timeout, function() { + db.update_richlist('received', function() { + db.update_richlist('balance', function() { + db.get_stats(settings.coin, function(nstats) { + // Check if the sync msg was showing + if (showSync) { + // Remove the sync msg + remove_sync_message(); + } + // update richlist_last_updated value + db.update_last_updated_stats(settings.coin, { richlist_last_updated: Math.floor(new Date() / 1000) }, function (cb) { + // update blockchain_last_updated value + db.update_last_updated_stats(settings.coin, { blockchain_last_updated: Math.floor(new Date() / 1000) }, function (cb) { + console.log('reindex complete (block: %s)', nstats.last); + exit(); + }); + }); }); }); }); @@ -366,8 +373,14 @@ if (database == 'peers') { // Remove the sync msg remove_sync_message(); } - console.log('update complete (block: %s)', nstats.last); - exit(); + // update richlist_last_updated value + db.update_last_updated_stats(settings.coin, { richlist_last_updated: Math.floor(new Date() / 1000) }, function (cb) { + // update blockchain_last_updated value + db.update_last_updated_stats(settings.coin, { blockchain_last_updated: Math.floor(new Date() / 1000) }, function (cb) { + console.log('update complete (block: %s)', nstats.last); + exit(); + }); + }); }); }); }); @@ -383,8 +396,11 @@ if (database == 'peers') { db.update_richlist('received', function() { console.log('richlist updated received.'); db.update_richlist('balance', function() { - console.log('richlist update complete'); - exit(); + // update richlist_last_updated value + db.update_last_updated_stats(settings.coin, { richlist_last_updated: Math.floor(new Date() / 1000) }, function (cb) { + console.log('richlist update complete'); + exit(); + }); }); }); }); @@ -402,6 +418,18 @@ if (database == 'peers') { exit(); }); }); + } else if (mode == 'reindex-last') { + console.log('calculating last tx.. please wait..'); + // Resetting the last counter requires a single lookup on the txes collection to find all txes that have a positive or zero total and 1 or more vout + Tx.find({'total': {$gte: 0}, 'vout': { $gte: { $size: 1 }}}).countDocuments(function(err, count) { + console.log('found tx count: ' + count.toString()); + Stats.updateOne({coin: settings.coin}, { + txes: count + }, function() { + console.log('tx count update complete'); + exit(); + }); + }); } }); } @@ -482,5 +510,11 @@ function remove_sync_message() { function get_last_usd_price() { // Get the last usd price for coinstats - db.get_last_usd_price(function(retVal) { exit(); }); + db.get_last_usd_price(function(retVal) { + // update markets_last_updated value + db.update_last_updated_stats(settings.coin, { markets_last_updated: Math.floor(new Date() / 1000) }, function (cb) { + console.log('market sync complete'); + exit(); + }); + }); } \ No newline at end of file diff --git a/settings.json.template b/settings.json.template index ec75963..6165023 100644 --- a/settings.json.template +++ b/settings.json.template @@ -144,12 +144,50 @@ // index page (valid options for difficulty are POW, POS or Hybrid) "index": { + // show_last_updated: determine whether to show a label above the transaction data with the last updated date + "show_last_updated": true, "show_hashrate": true, "difficulty": "POS", "last_txs": 100, "txs_per_page": 10 }, + // reward page + "reward_page": { + // show_last_updated: determine whether to show a label above the reward data with the last updated date + "show_last_updated": true + }, + + // masternodes page + "masternodes_page": { + // show_last_updated: determine whether to show a label above the masternode data with the last updated date + "show_last_updated": true + }, + + // movement page + "movement_page": { + // show_last_updated: determine whether to show a label above the movement data with the last updated date + "show_last_updated": true + }, + + // network page + "network_page": { + // show_last_updated: determine whether to show a label above the network data with the last updated date + "show_last_updated": true + }, + + // richlist page + "richlist_page": { + // show_last_updated: determine whether to show a label above the richlist data with the last updated date + "show_last_updated": true + }, + + // markets page + "markets_page": { + // show_last_updated: determine whether to show a label above the market data with the last updated date + "show_last_updated": true + }, + // ensure links on API page are valid "api": { "blockindex": 6415, diff --git a/views/index.pug b/views/index.pug index a8686d6..3be1180 100644 --- a/views/index.pug +++ b/views/index.pug @@ -77,6 +77,9 @@ block content strong #{settings.locale.ex_error} div #{error} .col-md-12.cardSpacer + if settings.index.show_last_updated == true + div.font-weight-bold(style='margin-bottom:15px;') Blockchain data last updated: + span.font-weight-normal=(last_updated == null || last_updated == '0' ? ' N/A' : ' ' + format_unixtime(last_updated)) .card.card-default.border-0.cardSpacer .card-header strong #{settings.locale.ex_latest_transactions} diff --git a/views/market.pug b/views/market.pug index 08e28dd..707a14e 100644 --- a/views/market.pug +++ b/views/market.pug @@ -50,6 +50,9 @@ block content }); }); .col-xs-12.col-md-12 + if settings.markets_page.show_last_updated == true + div.font-weight-bold(style='margin-bottom:15px;') Market data last updated: + span.font-weight-normal=(last_updated == null || last_updated == '0' ? ' N/A' : ' ' + format_unixtime(last_updated)) if settings.markets.market_select_visible == true && settings.market_data.length > 1 .row .col-md-12.cardSpacer diff --git a/views/masternodes.pug b/views/masternodes.pug index be8be9c..fb3dbb8 100644 --- a/views/masternodes.pug +++ b/views/masternodes.pug @@ -93,8 +93,9 @@ block content .col-md-12.cardSpacer .text-center(style='margin-bottom:15px;') i The current listing of all masternodes known to be active on the network. - div.font-weight-bold(style='margin-bottom:15px;') Last updated: - span.font-weight-normal=(last_updated == null ? ' N/A' : ' ' + format_unixtime(last_updated)) + if settings.masternodes_page.show_last_updated == true + div.font-weight-bold(style='margin-bottom:15px;') Masternode list last updated: + span.font-weight-normal=(last_updated == null || last_updated == '0' ? ' N/A' : ' ' + format_unixtime(last_updated)) .card.card-default .card-header strong Masternodes diff --git a/views/movement.pug b/views/movement.pug index 5f5e191..b6c221d 100644 --- a/views/movement.pug +++ b/views/movement.pug @@ -75,6 +75,9 @@ block content }, 45000); }); .col-md-12 + if settings.movement_page.show_last_updated == true + div.font-weight-bold(style='margin-bottom:15px;') Blockchain data last updated: + span.font-weight-normal=(last_updated == null || last_updated == '0' ? ' N/A' : ' ' + format_unixtime(last_updated)) .card.card-default.border-0.cardSpacer .card-header strong="Latest Movement" diff --git a/views/network.pug b/views/network.pug index f02e010..6483763 100644 --- a/views/network.pug +++ b/views/network.pug @@ -1,10 +1,14 @@ extends layout block content + include ./includes/common.pug .col-md-12.cardSpacer .text-center(style='margin-bottom:15px;') i #{settings.locale.net_warning} .container + if settings.network_page.show_last_updated == true + div.font-weight-bold(style='margin-bottom:15px;') Network data last updated: + span.font-weight-normal=(last_updated == null || last_updated == '0' ? ' N/A' : ' ' + format_unixtime(last_updated)) ul.nav.nav-tabs(role='tablist') li.nav-item(role='presentation') a.nav-link.active(href='#connections', aria-controls='connections', role='tab', data-toggle='tab') #{settings.locale.net_connections} diff --git a/views/reward.pug b/views/reward.pug index 434a566..b4346ae 100644 --- a/views/reward.pug +++ b/views/reward.pug @@ -1,6 +1,7 @@ extends layout block content + include ./includes/common.pug script. $(document).ready(function() { $('.summary-table').dataTable({ @@ -17,6 +18,9 @@ block content }); .row(style='margin-left:0;margin-right:0;') .col-xs-12.col-md-12 + if settings.reward_page.show_last_updated == true + div.font-weight-bold(style='margin-bottom:15px;') Reward data last updated: + span.font-weight-normal=(last_updated == null || last_updated == '0' ? ' N/A' : ' ' + format_unixtime(last_updated)) .card.card-default.border-0.card-address-summary .card-header(style='position:relative;') strong #{settings.locale.heavy_title} diff --git a/views/richlist.pug b/views/richlist.pug index 9e65214..e0665ff 100644 --- a/views/richlist.pug +++ b/views/richlist.pug @@ -1,6 +1,7 @@ extends layout block content + include ./includes/common.pug if show_dist == true script. $(document).ready(function() { @@ -34,6 +35,9 @@ block content .row(style='margin-left:0;margin-right:0;') div(class=(show_dist == true ? 'col-xs-12 col-lg-8' : 'col-12')) .container + if settings.richlist_page.show_last_updated == true + div.font-weight-bold(style='margin-bottom:15px;') Top 100 data last updated: + span.font-weight-normal=(last_updated == null || last_updated == '0' ? ' N/A' : ' ' + format_unixtime(last_updated)) ul.nav.nav-tabs(role='tablist') li.nav-item(role='presentation') a.nav-link.active(href='#balance', aria-controls='balance', role='tab', data-toggle='tab') #{settings.locale.rl_current_balance}