';
}
// Sort workers
function workerstats_SortWorkers (a, b) {
var aName = a.name.toLowerCase();
var bName = b.name.toLowerCase();
return ((aName < bName) ? -1 : ((aName > bName) ? 1 : 0));
}
// Render workers list
function workerstats_RenderWorkers (workersData, stats) {
workersData = workersData.sort(workerstats_SortWorkers);
var $workersRows = $(`#workersReport_rows_${stats.config.coin}`);
for (var i = 0; i < workersData.length; i++) {
var existingRow = document.getElementById(`workerRow${stats.config.coin}_${workerstats_GetWorkerRowId(workersData[i].name)}`);
if (!existingRow) {
$workersRows.empty();
break;
}
}
let have_avg_hr = false;
for (var i = 0; i < workersData.length; i++) {
var worker = workersData[i];
if (Date.now() / 1000 - parseInt(worker.lastShare) > 2 * 86400) continue;
if (!have_avg_hr && 'hashrate_1h' in worker) have_avg_hr = true;
var workerJson = JSON.stringify(worker);
var existingRow = document.getElementById(`workerRow${stats.config.coin}_${workerstats_GetWorkerRowId(worker.name)}`);
if (existingRow && existingRow.getAttribute('data-json') !== workerJson) {
$(existingRow)
.replaceWith(workerstats_GetWorkerRowElement(worker, workerJson, stats));
} else if (!existingRow) {
var workerElement = workerstats_GetWorkerRowElement(worker, workerJson, stats);
$workersRows.append(workerElement);
}
}
if (!have_avg_hr) $(`#workersReport${stats.config.coin} .avghr`)
.hide();
else $(`#workersReport${stats.config.coin} .avghr`)
.show();
}
/**
* Payments report
**/
// Parse payment data
function workerstats_ParsePayment (time, serializedPayment) {
var parts = serializedPayment.split(':');
return {
time: parseInt(time),
hash: parts[0],
amount: parts[1],
fee: parts[2],
mixin: parts[3],
recipients: parts[4]
};
}
// Get payment row element
function workerstats_GetPaymentRowElement (payment, jsonString, stats) {
var row = document.createElement('tr');
row.setAttribute('data-json', jsonString);
row.setAttribute('data-time', payment.time);
row.setAttribute('id', 'paymentRow' + stats.config.coin + payment.time);
row.innerHTML = workerstats_GetPaymentCells(payment, stats);
return row;
}
// Get payment cells
function workerstats_GetPaymentCells (payment, stats) {
return '
' + formatDate(payment.time) + '
' +
'
' + formatPaymentLink(payment.hash, stats) + '
' +
'
' + getReadableCoin(stats, payment.amount) + '
' +
'
' + payment.mixin + '
';
}
// Get summary row element
function workerstats_GetSummaryRowElement (summary, jsonString, stats) {
var row = document.createElement('tr');
row.setAttribute('data-json', jsonString);
row.setAttribute('data-date', summary.date);
row.setAttribute('id', 'summaryRow' + stats.config.coin + summary.date);
row.setAttribute('class', 'summary');
row.innerHTML = workerstats_GetSummaryCells(summary, stats);
return row;
}
// Get summary cells
function workerstats_GetSummaryCells (summary, stats) {
var text = getTranslation('paymentSummaryMulti') ? getTranslation('paymentSummaryMulti') : 'On %DATE% you have received %AMOUNT% in %COUNT% payments';
if (summary.count <= 1) text = getTranslation('paymentSummarySingle') ? getTranslation('paymentSummarySingle') : 'On %DATE% you have received %AMOUNT%';
text = text.replace(/%DATE%/g, summary.date);
text = text.replace(/%COUNT%/g, summary.count);
text = text.replace(/%AMOUNT%/g, getReadableCoin(stats, summary.amount));
return '
' + text + '
';
}
// Render payments
function workerstats_RenderPayments (paymentsResults, stats) {
var $paymentsRows = $(`#paymentsReport_rows_${stats.config.coin}`);
var lastPaymentDate = null;
var summaryData = {
date: null,
time: null,
count: 0,
amount: 0
};
for (var i = 0; i < paymentsResults.length; i += 2) {
var payment = workerstats_ParsePayment(paymentsResults[i + 1], paymentsResults[i]);
var paymentJson = JSON.stringify(payment);
var paymentElement = workerstats_GetPaymentRowElement(payment, paymentJson, stats);
var paymentDate = new Date(parseInt(payment.time) * 1000)
.toLocaleDateString();
if (!lastPaymentDate || lastPaymentDate && paymentDate != lastPaymentDate) {
summaryData = {
date: paymentDate,
time: payment.time,
count: 0,
amount: 0
};
}
var existingRow = document.getElementById(`paymentRow${stats.config.coin}${payment.time}`);
if (existingRow && existingRow.getAttribute('data-json') !== paymentJson) {
$(existingRow)
.replaceWith(workerstats_GetPaymentRowElement(payment, paymentJson, stats));
} else if (!existingRow) {
var inserted = false;
var rows = $paymentsRows.children()
.get();
for (var f = 0; f < rows.length; f++) {
var pTime = parseInt(rows[f].getAttribute('data-time'));
if (pTime && pTime < payment.time) {
inserted = true;
$(rows[f])
.before(paymentElement);
break;
}
}
if (!inserted) {
$paymentsRows.append(paymentElement);
}
}
summaryData.count++;
summaryData.amount += parseInt(payment.amount);
var summaryJson = JSON.stringify(summaryData);
var summaryElement = workerstats_GetSummaryRowElement(summaryData, summaryJson, stats);
var existingSummary = document.getElementById(`summaryRow${stats.config.coin}${summaryData.date}`);
if (existingSummary && existingSummary.getAttribute('data-json') !== summaryJson) {
$(existingSummary)
.replaceWith(summaryElement);
} else if (!existingSummary) {
var inserted = false;
var rows = $paymentsRows.children()
.get();
for (var f = 0; f < rows.length; f++) {
var pTime = parseInt(rows[f].getAttribute('data-time'));
if (pTime && pTime === summaryData.time) {
inserted = true;
$(rows[f])
.before(summaryElement);
break;
}
}
if (!inserted) {
$paymentsRows.append(summaryElement);
}
}
lastPaymentDate = paymentDate;
}
}
function workerstats_InitTemplate (ranOnce, addressTimeout, xhrAddressPoll, xhrGetPayments) {
let coin = lastStats.config.coin
if ($(`#blocksTabs li:contains(${coin})`)
.length === 0) {
let template = $('#siblingTabTemplate')
.html();
Mustache.parse(template)
let rendered = Mustache.render(template, {
coin: lastStats.config.coin,
symbol: `(${lastStats.config.symbol})`,
active: 'active'
});
$('#blocksTabs')
.append(rendered)
template = $('#siblingTemplate')
.html()
Mustache.parse(template)
rendered = Mustache.render(template, {
coin: coin,
active: 'active'
})
$('#tab-content')
.append(rendered)
workerstats_Setup(lastStats, api, addressTimeout, xhrAddressPoll, xhrGetPayments)
}
Object.keys(mergedStats)
.forEach(key => {
if ($(`#blocksTabs li:contains(${key})`)
.length === 0) {
coin = key
let template = $('#siblingTabTemplate')
.html();
Mustache.parse(template)
let rendered = Mustache.render(template, {
coin: mergedStats[key].config.coin,
symbol: `(${mergedStats[key].config.symbol})`
});
$('#blocksTabs')
.append(rendered)
template = $('#siblingTemplate')
.html()
Mustache.parse(template)
rendered = Mustache.render(template, {
coin: coin
})
$('#tab-content')
.append(rendered)
workerstats_Setup(mergedStats[key], mergedApis[key].api, addressTimeout, xhrAddressPoll, xhrGetPayments)
}
})
sortElementList($(`#blocksTabs`), $(`#blocksTabs>li`), mergedStats)
if (!ranOnce)
ranOnce = RunOnce()
}
/*
***************************************************************
workerstats methods
***************************************************************
*/
let home_GraphSettings = {
type: 'line',
width: '100%',
height: '140',
lineColor: '#03a9f4',
fillColor: 'rgba(3, 169, 244, .4)',
spotColor: null,
minSpotColor: null,
maxSpotColor: null,
highlightLineColor: '#236d26',
spotRadius: 3,
chartRangeMin: 0,
drawNormalOnTop: false,
tooltipFormat: '{{y}} – {{offset:names}}'
};
function home_CreateCharts (data) {
if (data.hasOwnProperty("charts")) {
var graphData = {
hashrate: {
data: [home_GetGraphData(data.charts.hashrate), home_GetGraphData(data.charts.hashrateSolo)],
options: {
lineColor: 'orange'
}
},
diff: {
data: [home_GetGraphData(data.charts.difficulty)]
},
miners: {
data: [home_GetGraphData(data.charts.miners), home_GetGraphData(data.charts.minersSolo)],
options: {
lineColor: 'orange'
}
},
workers: {
data: [home_GetGraphData(data.charts.workers), home_GetGraphData(data.charts.workersSolo)],
options: {
lineColor: 'orange'
}
},
};
for (var graphType in graphData) {
if (graphData[graphType].data[0].values.length > 1) {
var settings = jQuery.extend({}, home_GraphSettings);
settings.tooltipValueLookups = {
names: graphData[graphType].data[0].names
};
var $chart = $('[data-chart=' + graphType + '] .chart');
$chart.closest('.poolChart')
.show();
settings.tooltipFormat = graphData[graphType].data[1] ? 'PROP: {{y}} – {{offset:names}}' : '{{y}} – {{offset:names}}'
$chart.sparkline(graphData[graphType].data[0].values, settings);
if (graphData[graphType].data[1]) {
settings.composite = true
settings.lineColor = graphData[graphType].options.lineColor
settings.tooltipFormat = 'SOLO: {{y}} – {{offset:names}}'
$chart.sparkline(graphData[graphType].data[1].values, settings);
}
}
}
}
}
// Get chart data
function home_GetGraphData (rawData, fixValueToCoins) {
var graphData = {
names: [],
values: []
};
if (rawData) {
for (var i = 0, xy; xy = rawData[i]; i++) {
graphData.names.push(new Date(xy[0] * 1000)
.toLocaleString());
graphData.values.push(fixValueToCoins ? getReadableCoin(lastStats, xy[1], null, true) : xy[1]);
}
}
return graphData;
}
function home_GenerateNetworkStats (key, symbol) {
if ($(`#networkStats${key}`)
.length == 0) {
let template = $('#siblingTemplate')
.html()
if (template) {
Mustache.parse(template)
let rendered = Mustache.render(template, {
coin: key,
symbol: symbol
})
$(`#networkStats`)
.append(rendered)
}
}
}
function sortElementList (container, siblings, stats) {
let sorted = (a, b) => {
return ((a.id.toLowerCase() < b.id.toLowerCase()) ? -1 : ((a.id.toLowerCase() > b.id.toLowerCase()) ? 1 : 0))
}
if (stats && siblings.length - 1 === Object.keys(stats)
.length) {
siblings.sort(sorted)
.appendTo(container)
}
}
function home_InitTemplate (parentStats, siblingStats) {
$('#networkLastBlockFound')
.timeago('update', new Date(parentStats.lastblock.timestamp * 1000)
.toISOString());
let coin = parentStats.config.coin
let minerInfo = []
let efforts = []
if ($(`#networkStats${coin}`)
.length == 0) {
minerInfo.push({
blocks: parentStats.pool.totalBlocks.toString(),
blocksSolo: parentStats.pool.totalBlocksSolo.toString(),
coin: coin,
symbol: parentStats.config.symbol,
miners: parentStats.pool.miners.toString(),
minersSolo: parentStats.pool.minersSolo.toString()
})
efforts.push({
coin: coin,
effort: `${(parentStats.pool.roundHashes / parentStats.network.difficulty * 100).toFixed(1)}%`,
symbol: parentStats.config.symbol
})
let template = $('#siblingTemplate')
.html()
Mustache.parse(template)
let rendered = Mustache.render(template, {
coin: coin,
symbol: parentStats.config.symbol
})
$(`#networkStats`)
.append(rendered)
}
let lastBlockFound = null
if (parentStats.pool.lastBlockFound) {
lastBlockFound = parseInt(parentStats.pool.lastBlockFound);
}
updateText(`networkHashrate${coin}`, getReadableHashRateString(parentStats.network.difficulty / parentStats.config.coinDifficultyTarget) + '/sec');
updateText(`networkDifficulty${coin}`, formatNumber(parentStats.network.difficulty.toString(), ' '));
updateText(`blockchainHeight${coin}`, formatNumber(parentStats.network.height.toString(), ' '));
let rewardMinusNetworkFee = parentStats.lastblock.reward - (parentStats.lastblock.reward * (parentStats.config.networkFee ? parentStats.config.networkFee / 100 : 0))
updateText(`networkLastReward${coin}`, getReadableCoin(parentStats, rewardMinusNetworkFee));
Object.keys(siblingStats)
.forEach(key => {
home_GenerateNetworkStats(key, siblingStats[key].config.symbol)
minerInfo.push({
blocks: siblingStats[key].pool.totalBlocks.toString(),
blocksSolo: siblingStats[key].pool.totalBlocksSolo.toString(),
coin: key,
symbol: siblingStats[key].config.symbol,
miners: siblingStats[key].pool.miners.toString(),
minersSolo: siblingStats[key].pool.minersSolo.toString()
})
efforts.push({
coin: key,
effort: `${(siblingStats[key].pool.roundHashes / siblingStats[key].network.difficulty * 100).toFixed(1)}%`,
symbol: siblingStats[key].config.symbol
});
if (siblingStats[key].pool.lastBlockFound) {
let lastChildBlockFound = parseInt(siblingStats[key].pool.lastBlockFound)
if (lastChildBlockFound > lastBlockFound)
lastBlockFound = lastChildBlockFound
}
updateText(`networkHashrate${key}`, getReadableHashRateString(siblingStats[key].network.difficulty / siblingStats[key].config.coinDifficultyTarget) + '/sec');
updateText(`networkDifficulty${key}`, formatNumber(siblingStats[key].network.difficulty.toString(), ' '));
updateText(`blockchainHeight${key}`, formatNumber(siblingStats[key].network.height.toString(), ' '));
// updateText(`networkLastReward${key}`, getReadableCoin(siblingStats[key], siblingStats[key].lastblock.reward));
updateText(`poolMiners${key}`, `${siblingStats[key].pool.miners}/${siblingStats[key].pool.minersSolo}`);
updateText(`blocksTotal${key}`, `${siblingStats[key].pool.totalBlocks}/${siblingStats[key].pool.totalBlocksSolo}`);
updateText(`currentEffort${key}`, (siblingStats[key].pool.roundHashes / siblingStats[key].network.difficulty * 100)
.toFixed(1) + '%');
})
sortElementList($(`#networkStats`), $(`#networkStats>div`), siblingStats)
if ($(`#poolDetails > div`)
.length == 0) {
let template = $('#poolDetailTemplate')
.html()
Mustache.parse(template)
let rendered = Mustache.render(template, {
coin: parentStats.config.coin,
symbol: parentStats.config.symbol,
blocks: minerInfo
})
$(`#poolDetails`)
.append(rendered)
}
if ($(`#mainPoolStats > div`)
.length == 0) {
let template = $('#mainPoolTemplate')
.html()
Mustache.parse(template)
let rendered = Mustache.render(template, {
coin: parentStats.config.coin,
blocks: minerInfo,
efforts: efforts
})
$(`#mainPoolStats`)
.append(rendered)
}
if (lastBlockFound) {
$('#poolLastBlockFound')
.timeago('update', new Date(lastBlockFound)
.toISOString());
} else {
$('#poolLastBlockFound')
.removeAttr('title')
.data('ts', '')
.update('Never');
}
let lastHash = updateText('lastHash', parentStats.lastblock.hash)
if (lastHash)
lastHash.setAttribute('href', getBlockchainUrl(parentStats.lastblock.hash, parentStats));
updateText('poolHashrate', `PROP: ${getReadableHashRateString(parentStats.pool.hashrate)}/sec`);
updateText('poolHashrateSolo', `SOLO: ${getReadableHashRateString(parentStats.pool.hashrateSolo)}/sec`);
var hashPowerSolo = parentStats.pool.hashrateSolo / (parentStats.network.difficulty / parentStats.config.coinDifficultyTarget) * 100;
updateText('hashPowerSolo', hashPowerSolo.toFixed(2) + '%');
var hashPower = parentStats.pool.hashrate / (parentStats.network.difficulty / parentStats.config.coinDifficultyTarget) * 100;
updateText('hashPower', hashPower.toFixed(2) + '%');
updateText(`poolMiners${coin}`, `${parentStats.pool.miners}/${parentStats.pool.minersSolo}`);
updateText('blocksTotal', `${parentStats.pool.totalBlocks}/${parentStats.pool.totalBlocksSolo}`);
var totalFee = parentStats.config.fee;
if (Object.keys(parentStats.config.donation)
.length) {
var totalDonation = 0;
for (var i in parentStats.config.donation) {
totalDonation += parentStats.config.donation[i];
}
totalFee += totalDonation;
}
updateText('poolFee', (totalFee > 0 && totalFee != 100 ? floatToString(totalFee) : (totalFee == 100 ? '100' : '0')) + '%');
updateText('paymentsInterval', getReadableTime(parentStats.config.paymentsInterval));
updateText('paymentsMinimum', getReadableCoin(parentStats, parentStats.config.minPaymentThreshold));
updateText('blockSolvedTime', getReadableTime(parentStats.network.difficulty / parentStats.pool.hashrate));
updateText(`currentEffort${coin}`, (parentStats.pool.roundHashes / parentStats.network.difficulty * 100)
.toFixed(1) + '%');
}