Files
peya-nodejs-pool/website_example/js/common.js
Codex Bot 80eecceb79
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 27s
Fix pool icon loop and config panel styling
2026-03-24 15:04:41 +01:00

2411 lines
85 KiB
JavaScript

/**
* Common javascript code for cryptonote-nodejs-pool
* Author: Daniel Vandal
* GitHub: https://github.com/dvandal/cryptonote-nodejs-pool
**/
/**
* Layout
**/
// Collapse menu on load for mobile devices
$('#menu-content').collapse('hide');
const lucideIconMap = {
'fa-bars': 'menu',
'fa-home': 'house',
'fa-dashboard': 'activity',
'fa-rocket': 'rocket',
'fa-cubes': 'blocks',
'fa-link': 'link-2',
'fa-money': 'wallet',
'fa-trophy': 'trophy',
'fa-circle-o-notch': 'loader-circle',
'fa-github': 'code-2',
'fa-search': 'search',
'fa-comments': 'messages-square',
'fa-cloud': 'cloud',
'fa-key': 'key-round',
'fa-server': 'server',
'fa-wrench': 'wrench',
'fa-history': 'history',
'fa-line-chart': 'chart-column',
'fa-unlock-alt': 'lock-open',
'fa-dollar': 'badge-dollar-sign',
'fa-group': 'users',
'fa-percent': 'percent',
'fa-clock-o': 'clock-3',
'fa-check': 'check',
'fa-times': 'x',
'fa-spinner': 'loader-circle',
'fa-refresh': 'refresh-cw',
'fa-tachometer': 'gauge',
'fa-cloud-upload': 'cloud-upload',
'fa-user': 'user-round',
'fa-bank': 'landmark',
'fa-star': 'sparkles',
'fa-sliders': 'sliders-horizontal',
'fa-sort': 'arrow-up-down',
'fa-exchange': 'arrow-left-right',
'fa-book': 'book-open',
'fa-car': 'cpu',
'fa-download': 'download',
'fa-file-code-o': 'file-code-2',
'fa-hashtag': 'hash',
'fa-question-circle': 'circle-help',
'fa-users': 'users',
'fa-bar-chart-o': 'chart-column',
'fa-eye': 'eye',
'fa-gears': 'settings-2',
'fa-sign-out': 'log-out'
};
let lucideRenderingActive = false;
function renderLucideIcons(root) {
const scope = root || document;
const legacyIcons = scope.querySelectorAll('i[class*="fa"], span[class*="fa"]');
legacyIcons.forEach(function(node) {
const classes = Array.from(node.classList);
const faClass = classes.find(function(cls) {
return cls.indexOf('fa-') === 0 && cls !== 'fa-spin' && cls !== 'fa-2x';
});
const iconName = faClass ? lucideIconMap[faClass] : null;
if (!iconName) return;
node.setAttribute('data-lucide', iconName);
classes.forEach(function(cls) {
if (cls === 'fa' || cls === 'fa-spin' || cls === 'fa-2x' || cls.indexOf('fa-') === 0) {
node.classList.remove(cls);
}
});
if (classes.indexOf('fa-spin') !== -1) {
node.classList.add('animate-spin');
}
});
if (window.lucide && typeof window.lucide.createIcons === 'function') {
lucideRenderingActive = true;
try {
window.lucide.createIcons({
icons: window.lucide.icons,
attrs: { 'stroke-width': 1.9 },
nameAttr: 'data-lucide'
});
} finally {
lucideRenderingActive = false;
}
}
}
function toggleSingleTabNav(selector) {
const $tabs = $(selector);
if (!$tabs.length) return;
$tabs.toggle($tabs.children('li').length > 1);
}
let lucideRenderScheduled = false;
function scheduleLucideRender() {
if (lucideRenderScheduled || lucideRenderingActive) return;
lucideRenderScheduled = true;
requestAnimationFrame(function() {
lucideRenderScheduled = false;
if (!lucideRenderingActive) {
renderLucideIcons(document.body);
}
});
}
$(function() {
const pageRoot = document.getElementById('page');
if (!pageRoot || typeof MutationObserver === 'undefined') return;
const observer = new MutationObserver(function(mutations) {
if (lucideRenderingActive) return;
const hasChildChanges = mutations.some(function(mutation) {
return mutation.type === 'childList' && (mutation.addedNodes.length || mutation.removedNodes.length);
});
if (hasChildChanges) {
scheduleLucideRender();
}
});
observer.observe(pageRoot, {
childList: true,
subtree: true
});
});
/**
* Cookies handler
**/
var docCookies = {
getItem: function (sKey) {
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
},
setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; }
var sExpires = "";
if (vEnd) {
switch (vEnd.constructor) {
case Number:
sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd;
break;
case String:
sExpires = "; expires=" + vEnd;
break;
case Date:
sExpires = "; expires=" + vEnd.toUTCString();
break;
}
}
document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
return true;
},
removeItem: function (sKey, sPath, sDomain) {
if (!sKey || !this.hasItem(sKey)) { return false; }
document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : "");
return true;
},
hasItem: function (sKey) {
return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
}
};
/**
* Pages routing
**/
// Current page
var currentPage;
// Handle hash change
window.onhashchange = function(){
routePage();
};
// Route to page
var xhrPageLoading;
function routePage(loadedCallback) {
if (currentPage) currentPage.destroy();
$('#page').html('');
$('#loading').show();
if (xhrPageLoading) {
xhrPageLoading.abort();
}
$('.hot_link').parent().removeClass('active');
var $link = $('a.hot_link[href="' + (window.location.hash || '#') + '"]');
$link.parent().addClass('active');
var page = $link.data('page');
loadTranslations();
xhrPageLoading = $.ajax({
url: 'pages/' + page,
cache: false,
success: function (data) {
$('#menu-content').collapse('hide');
$('#loading').hide();
$('#page').show().html(data);
loadTranslations();
if (currentPage) {
currentPage.update();
}
if (loadedCallback) {
loadedCallback();
}
renderLucideIcons(document.body);
}
});
}
/**
* Strings
**/
// Add .update() custom jQuery function to update text content
$.fn.update = function(txt){
var el = this[0];
if (el && el.textContent !== txt)
el.textContent = txt;
return this;
};
// Update Text classes
function updateTextClasses(className, text){
var els = document.getElementsByClassName(className);
if (els) {
for (var i = 0; i < els.length; i++){
var el = els[i];
if (el && el.textContent !== text)
el.textContent = text;
}
}
}
// Update Text content
function updateText(elementId, text){
var el = document.getElementById(elementId);
if (el && el.textContent !== text){
el.textContent = text;
}
return el;
}
// Convert float to string
function floatToString(float) {
return float.toFixed(6).replace(/[0\.]+$/, '');
}
// Format number
function formatNumber(number, delimiter){
if(number != '') {
number = number.split(delimiter).join('');
var formatted = '';
var sign = '';
if(number < 0){
number = -number;
sign = '-';
}
while(number >= 1000){
var mod = number % 1000;
if(formatted != '') formatted = delimiter + formatted;
if(mod == 0) formatted = '000' + formatted;
else if(mod < 10) formatted = '00' + mod + formatted;
else if(mod < 100) formatted = '0' + mod + formatted;
else formatted = mod + formatted;
number = parseInt(number / 1000);
}
if(formatted != '') formatted = sign + number + delimiter + formatted;
else formatted = sign + number;
return formatted;
}
return '';
}
// Format date
function formatDate(time){
if (!time) return '';
return new Date(parseInt(time) * 1000).toLocaleString();
}
// Format percentage
function formatPercent(percent) {
if (!percent && percent !== 0) return '';
return percent + '%';
}
// Get readable time
function getReadableTime(seconds){
var units = [ [60, 'second'], [60, 'minute'], [24, 'hour'],
[7, 'day'], [4, 'week'], [12, 'month'], [1, 'year'] ];
function formatAmounts(amount, unit){
var rounded = Math.round(amount);
var unit = unit + (rounded > 1 ? 's' : '');
if (getTranslation(unit)) unit = getTranslation(unit);
return '' + rounded + ' ' + unit;
}
var amount = seconds;
for (var i = 0; i < units.length; i++){
if (amount < units[i][0]) {
return formatAmounts(amount, units[i][1]);
}
amount = amount / units[i][0];
}
return formatAmounts(amount, units[units.length - 1][1]);
}
// Get readable hashrate
function getReadableHashRateString(hashrate){
var i = 0;
var byteUnits = [' H', ' KH', ' MH', ' GH', ' TH', ' PH' ];
while (hashrate > 1000){
hashrate = hashrate / 1000;
i++;
}
if (typeof hashrate != 'number')
hashrate = 0;
return hashrate.toFixed(2) + byteUnits[i];
}
function getCoinDecimalPlace(stats)
{
if (typeof coinDecimalPlaces != "undefined") return coinDecimalPlaces;
else if (stats.config.coinDecimalPlaces) return stats.config.coinDecimalPlaces;
else stats.config.coinUnits.toString().length - 1;
}
function getReadableCoin(stats, coins, digits, withoutSymbol) {
let coinDecimalPlaces = getCoinDecimalPlace(stats)
let amount = parseFloat((parseInt(coins || 0) / stats.config.coinUnits).toFixed(digits || coinDecimalPlaces))
return amount.toString() + (withoutSymbol ? '' : (' ' + stats.config.symbol));
}
// Format payment link
function formatPaymentLink(hash, stats){
return '<a target="_blank" href="' + getTransactionUrl(hash, stats) + '">' + hash + '</a>';
}
// Format difficulty
function formatDifficulty(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
}
// Format luck / current effort
function formatLuck(difficulty, shares, solo=false) {
// Only an approximation to reverse the calculations done in pool.js, because the shares with their respective times are not recorded in redis
// Approximation assumes equal pool hashrate for the whole round
// Could potentially be replaced by storing the sum of all job.difficulty in the redis db.
if (lastStats.config.slushMiningEnabled) {
// Uses integral calculus to calculate the average of a dynamic function
var accurateShares = 1/lastStats.config.blockTime * ( // 1/blockTime to get the average
shares * lastStats.config.weight * ( // Basically calculates the 'area below the graph' between 0 and blockTime
1 - Math.pow(
Math.E,
((- lastStats.config.blockTime) / lastStats.config.weight) // blockTime is equal to the highest possible result of (dateNowSeconds - scoreTime)
)
)
);
}
else {
var accurateShares = shares;
}
var percent = Math.round(accurateShares / difficulty * 100);
if(!percent){
return `<span class="luckGood">?</span>` + (solo === true ? `<span class="fa fa-user luckGood" title="Solo Mined"></span>` : ``);
}
else if(percent <= 100){
return `<span class="luckGood">${percent}%&nbsp;</span>` + (solo === true ? `<span class="fa fa-user luckGood" title="Solo Mined"></span>` : ``);
}
else if(percent >= 101 && percent <= 150){
return `<span class="luckMid">${percent}%&nbsp;</span>` + (solo === true ? `<span class="fa fa-user luckMid" title="Solo Mined"></span>` : ``);
}
else{
return `<span class="luckBad">${percent}%&nbsp;</span>` + (solo === true ? `<span class="fa fa-user luckBad" title="Solo Mined"></span>` : ``);
}
}
/**
* URLs
**/
// Return pool host
function getPoolHost() {
if (typeof poolHost != "undefined") return poolHost;
if (lastStats.config.poolHost) return lastStats.config.poolHost;
else return window.location.hostname;
}
// Return transaction URL
function getTransactionUrl(id, stats) {
if (stats && blockExplorers){
return blockExplorers[stats.config.coin].transactionExplorer.replace('{symbol}', stats.config.symbol.toLowerCase()).replace('{id}', id);
}
}
// Return blockchain explorer URL
function getBlockchainUrl(id, stats) {
if (stats && blockExplorers){
return blockExplorers[stats.config.coin].blockchainExplorer.replace('{symbol}', stats.config.symbol.toLowerCase()).replace('{id}', id);
}
}
/**
* Tables
**/
// Sort table cells
function sortTable() {
var table = $(this).parents('table').eq(0),
rows = table.find('tr:gt(0)').toArray().sort(compareTableRows($(this).index()));
this.asc = !this.asc;
if(!this.asc) {
rows = rows.reverse()
}
for(var i = 0; i < rows.length; i++) {
table.append(rows[i])
}
}
// Compare table rows
function compareTableRows(index) {
return function(a, b) {
var valA = getCellValue(a, index), valB = getCellValue(b, index);
if (!valA) { valA = 0; }
if (!valB) { valB = 0; }
return $.isNumeric(valA) && $.isNumeric(valB) ? valA - valB : valA.toString().localeCompare(valB.toString())
}
}
// Get table cell value
function getCellValue(row, index) {
return $(row).children('td').eq(index).data("sort")
}
/**
* Translations
**/
if (typeof langs == "undefined") {
var langs = { en: 'English' };
}
if (typeof defaultLang == "undefined") {
var defaultLang = 'en';
}
var langCode = defaultLang;
var langData = null;
function getTranslation(key) {
if (!langData || !langData[key]) return null;
return langData[key];
}
var translate = function(data) {
$("html")[0].lang = langCode;
langData = data;
$("[data-tkey]").each(function(index) {
var strTr = data[$(this).attr('data-tkey')];
$(this).html(strTr);
});
$("[data-tplaceholder]").each(function(index) {
var strTr = data[$(this).attr('data-tplaceholder')];
$(this).attr('placeholder', strTr)
});
$("[data-tvalue]").each(function(index) {
var strTr = data[$(this).attr('data-tvalue')];
$(this).attr('value', strTr)
});
}
// Get language code from URL
const $_GET = {};
const args = location.search.substr(1).split(/&/);
for (var i=0; i<args.length; ++i) {
const tmp = args[i].split(/=/);
if (tmp[0] != "") {
$_GET[decodeURIComponent(tmp[0])] = decodeURIComponent(tmp.slice(1).join("").replace("+", " "));
langCode = $_GET['lang'];
}
}
// Load language
function loadTranslations() {
if (langData) {
translate(langData);
}
else if (langs && langs[langCode]) {
$.getJSON('lang/'+langCode+'.json', translate);
$.getScript('lang/timeago/jquery.timeago.'+langCode+'.js');
} else {
$.getJSON('lang/'+defaultLang+'.json', translate);
$.getScript('lang/timeago/jquery.timeago.'+defaultLang+'.js');
}
}
// Language selector
function renderLangSelector() {
// Desktop
var html = '';
var numLangs = 0;
if (langs) {
html += '<select id="newLang" class="form-control form-control-sm">';
for (var lang in langs) {
var selected = lang == langCode ? ' selected="selected"' : '';
html += '<option value="' + lang + '"' + selected + '>' + langs[lang] + '</option>';
numLangs ++;
}
html += '</select>';
}
if (html && numLangs > 1) {
$('#langSelector').html(html);
$('#newLang').each(function(){
$(this).change(function() {
var newLang = $(this).val();
var url = '?lang=' + newLang;
if (window.location.hash) url += window.location.hash;
window.location.href = url;
});
});
}
// Mobile
var html = '';
var numLangs = 0;
if (langs) {
html += '<select id="mNewLang" class="form-control form-control-sm">';
for (var lang in langs) {
var selected = lang == langCode ? ' selected="selected"' : '';
html += '<option value="' + lang + '"' + selected + '>' + langs[lang] + '</option>';
numLangs ++;
}
html += '</select>';
}
if (html && numLangs > 1) {
$('#mLangSelector').html(html);
$('#mNewLang').each(function(){
$(this).change(function() {
var newLang = $(this).val();
var url = '?lang=' + newLang;
if (window.location.hash) url += window.location.hash;
window.location.href = url;
});
});
}
}
/*
***************************************************************
pool_block methods
***************************************************************
*/
function poolBlocks_GenerateChart(data, displayedChart) {
if (displayedChart[data.config.coin] || !data.charts.blocks || data.charts.blocks === "undefined" || !data.charts.blocksSolo || data.charts.blocksSolo === "undefined") return ;
let chartDays = data.config.blocksChartDays || null;
let title = getTranslation('poolBlocks') ? getTranslation('poolBlocks') : 'Blocks found';
if (chartDays) {
if (chartDays === 1) title = getTranslation('blocksFoundLast24') ? getTranslation('blocksFoundLast24') : 'Blocks found in the last 24 hours';
else title = getTranslation('blocksFoundLastDays') ? getTranslation('blocksFoundLastDays') : 'Blocks found in the last {DAYS} days';
title = title.replace('{DAYS}', chartDays);
}
updateText(`blocksChartTitle${data.config.coin}`, title);
let labels = [];
let values = [];
let valuesSolo = [];
for (let key in data.charts.blocks) {
let label = key;
if (chartDays && chartDays === 1) {
let keyParts = key.split(' ');
label = keyParts[1].replace(':00', '');
}
labels.push(label);
values.push(data.charts.blocks[key]);
}
for (let key in data.charts.blocksSolo) {
valuesSolo.push(data.charts.blocksSolo[key]);
}
let $chart = $(`blocksChartObj${data.config.coin}`);
let bgcolor = null, bordercolor = null, borderwidth = null;
let colorelem = $chart.siblings('a.chart-style');
if (colorelem.length == 1) {
bgcolor = colorelem.css('background-color');
bordercolor = colorelem.css('border-left-color');
borderwidth = parseFloat(colorelem.css('width'));
}
if (bgcolor === null) bgcolor = 'rgba(3, 169, 244, .4)';
if (bordercolor === null) bordercolor = '#03a9f4';
if (borderwidth === null || isNaN(borderwidth)) borderwidth = 1;
let chartElement = document.getElementById(`blocksChartObj${data.config.coin}`)
if (!chartElement) return
let chart = new Chart(chartElement, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Prop Blocks',
data: values,
fill: false,
backgroundColor: bgcolor,
borderColor: bordercolor,
borderWidth: borderwidth
},
{
label: 'Solo Blocks',
data: valuesSolo,
fill: false,
backgroundColor: 'rgba(0, 230, 64, 1)',
borderColor: bordercolor,
borderWidth: borderwidth
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: { display: false },
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
userCallback: function(label, index, labels) {
if (Math.floor(label) === label) return label;
}
}
}],
},
layout: {
padding: { top: 0, left: 0, right: 0, bottom: 0 }
}
}
});
$(`#blocksChart${data.config.coin}`).show();
displayedChart[data.config.coin] = true;
}
// Parse block data
function poolBlocks_ParseBlock(height, serializedBlock, stats){
var parts = serializedBlock.split(':');
let block = {}
if (parts[0].includes('solo') || parts[0].includes('prop')){
block = {
height: parseInt(height),
solo: parts[0] === 'solo',
address: parts[1],
hash: parts[2],
time: parts[3],
difficulty: parseInt(parts[4]),
shares: parseInt(parts[5]),
orphaned: parts[6],
reward: parts[7]
};
}else{
block = {
height: parseInt(height),
solo: false,
address: '',
hash: parts[0],
time: parts[1],
difficulty: parseInt(parts[2]),
shares: parseInt(parts[3]),
orphaned: parts[4],
reward: parts[5]
};
}
var toGo = stats.config.depth - (stats.network.height - block.height - 1);
if(toGo > 1){
block.maturity = toGo + ' to go';
}
else if(toGo == 1){
block.maturity = "<i class='fa fa-spinner fa-spin'></i>";
}
else if(toGo <= 0){
block.maturity = "<i class='fa fa-unlock-alt'></i>";
}
switch (block.orphaned){
case '0':
block.status = 'unlocked';
block.maturity = "<i class='fa fa-unlock-alt'></i>";
break;
case '1':
block.status = 'orphaned';
block.maturity = "<i class='fa fa-times'></i>";
block.reward = 0;
break;
default:
block.status = 'pending';
break;
}
return block;
}
// Get block row element
function getBlockRowElement(block, jsonString, stats){
function formatBlockLink(hash, stats){
return '<a target="_blank" href="' + getBlockchainUrl(hash, stats) + '">' + hash + '</a>';
}
var blockStatusClasses = {
'pending': 'pending',
'unlocked': 'unlocked',
'orphaned': 'orphaned'
};
var row = document.createElement('tr');
row.setAttribute(`data-json`, jsonString);
row.setAttribute(`data-height`, block.height);
row.setAttribute('id', `blockRow${stats.config.coin}${block.height}`);
row.setAttribute('title', block.status);
row.className = blockStatusClasses[block.status];
var reward = "";
if(typeof block.reward == "undefined"){
reward = "Waiting...";
}
else{
reward = getReadableCoin(stats, block.reward, null, true);
}
var columns =
'<td class="col1">' + formatDate(block.time) + '</td>' +
'<td class="col2">' + reward + '</td>' +
'<td class="col3">' + block.height + '</td>' +
'<td class="col4">' + block.difficulty + '</td>' +
'<td class="col5">' + formatBlockLink(block.hash, stats) + '</td>' +
'<td class="col5" title="Miners Address">' + block.address + '</td>' +
'<td class="col6" align="right" title="' + block.shares + ' shares submitted">' + formatLuck(block.difficulty, block.shares, block.solo) + '</td>' +
'<td class="col7">' + block.maturity + '</td>';
row.innerHTML = columns;
return row;
}
// Render blocks
function poolBlocks_RenderBlocks(blocksResults, stats){
var $blocksRows = $(`#blocksReport${stats.config.coin}_rows`);
for (var i = 0; i < blocksResults.length; i += 2){
var block = poolBlocks_ParseBlock(blocksResults[i + 1], blocksResults[i], stats);
var blockJson = JSON.stringify(block);
var existingRow = document.getElementById(`blockRow${stats.config.coin}${block.height}`);
if (existingRow && existingRow.getAttribute(`data-json`) !== blockJson){
$(existingRow).replaceWith(getBlockRowElement(block, blockJson, stats));
}
else if (!existingRow){
var blockElement = getBlockRowElement(block, blockJson, stats);
var inserted = false;
var rows = $blocksRows.children().get();
for (var f = 0; f < rows.length; f++) {
var bHeight = parseInt(rows[f].getAttribute(`data-height`));
if (bHeight < block.height){
inserted = true;
$(rows[f]).before(blockElement);
break;
}
}
if (!inserted){
$blocksRows.append(blockElement);
}
}
}
}
// Load more blocks button
function poolBlocks_Setup(api, stats, xhrGetBlocks) {
$(`#loadMoreBlocks${stats.config.coin}`).click(function(xhrGetBlocks){
if (xhrGetBlocks[stats.config.coin]) xhrGetBlocks[stats.config.coin].abort();
xhrGetBlocks[stats.config.coin] = $.ajax({
url: api + '/get_blocks',
data: {
height: $(`#blocksReport${stats.config.coin}_rows`).children().last().data(`height`)
},
dataType: 'json',
cache: 'false',
success: function(data){
poolBlocks_RenderBlocks(data, stats);
}
});
});
}
function poolBlocks_InitTemplate(ranOnce, displayedChart, xhrGetBlocks) {
let coin = lastStats.config.coin
if ($(`#blocksTabs li:contains(${coin})`).length == 0) {
let template1 = $('#siblingTemplate').html()
Mustache.parse(template1)
let rendered1 = Mustache.render(template1, {coin:lastStats.config.coin, active:'active'})
$('#tab-content').append(rendered1)
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)
poolBlocks_Setup(api, lastStats, xhrGetBlocks)
}
updateText(`blocksTotal${coin}`, lastStats.pool.totalBlocks.toString());
if (lastStats.pool.lastBlockFound) {
var d = new Date(parseInt(lastStats.pool.lastBlockFound)).toISOString();
$(`#lastBlockFound${coin}`).timeago('update', d);
}
else {
$(`#lastBlockFound${coin}`).removeAttr('title').data('ts', '').update('Never');
}
updateText(`blocksTotalSolo${coin}`, lastStats.pool.totalBlocksSolo.toString());
if (lastStats.pool.lastBlockFoundSolo) {
var d = new Date(parseInt(lastStats.pool.lastBlockFoundSolo)).toISOString();
$(`#lastBlockFoundSolo${coin}`).timeago('update', d);
}
else {
$(`#lastBlockFoundSolo${coin}`).removeAttr('title').data('ts', '').update('Never');
}
updateText(`blocksMaturityCount${coin}`, lastStats.config.depth.toString());
$(`#averageLuck${coin}`).html(formatLuck(lastStats.pool.totalDiff, lastStats.pool.totalShares));
displayedChart[lastStats.config.coin] = false
if (lastStats.charts.blocks) {
poolBlocks_GenerateChart(lastStats, displayedChart);
}
poolBlocks_RenderBlocks(lastStats.pool.blocks, lastStats);
toggleSingleTabNav('#blocksTabs');
renderLucideIcons(document.body);
if (!ranOnce)
ranOnce = RunOnce()
}
/*
***************************************************************
top10miners methods
***************************************************************
*/
function top10Miners_GetMinerCells(position, data){
var miner = data.miner;
var hashrate = data.hashrate ? data.hashrate : 0;
var lastShare = data.lastShare ? data.lastShare : 0;
var hashes = (data.hashes || 0).toString();
return '<td class="col1" data-sort="' + position + '">' + position + '</td>' +
'<td class="col2" data-sort="' + miner + '">' + miner + '</td>' +
'<td class="col3" data-sort="' + hashrate + '">' + getReadableHashRateString(hashrate) + '/sec</td>' +
'<td class="col4" data-sort="' + lastShare + '">' + (lastShare ? $.timeago(new Date(parseInt(lastShare) * 1000).toISOString()) : 'Never') + '</td>' +
'<td class="col5" data-sort="' + hashes + '">' + hashes + '</td>';
}
// Update top10 miners report
function top10Miners_UpdateTop10(xhrGetMiners, endPoint, key) {
if (xhrGetMiners[key])
xhrGetMiners[key].abort()
$( `#top10miners_rows${key}`).empty();
xhrGetMiners[key] = $.ajax({
url: `${endPoint}/get_top10miners`,
data: {
time: $(`#top10_rows${key}`).children().last().data('time')
},
dataType: 'json',
cache: 'false',
success: function(data){
if (!data) return;
for (var i=0; i<data.length; ++i) {
$( `#top10miners_rows${key}`).append('<tr>' + top10Miners_GetMinerCells(i+1, data[i]) + '</tr>');
}
}
});
}
function top10Miners_InitTemplate(xhrGetMiners, ranOnce) {
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)
}
top10Miners_UpdateTop10(xhrGetMiners, api, coin);
if (!ranOnce)
ranOnce = RunOnce()
}
/*
***************************************************************
settings methods
***************************************************************
*/
function settings_Setup(api, stats) {
var address = getCurrentAddress(stats.config.coin);
if (address) {
if ($(`#yourAddress${stats.config.coin}`).length) {
$(`#yourAddress${stats.config.coin}`).val(address);
}
settings_GetPayoutLevel(api, address, stats);
if ($(`#yourEmail${stats.config.coin}`).length) {
settings_GetEmailAddress(api, address, stats);
}
}
// Handle click on Set button
$(`#payoutSetButton${stats.config.coin}`).off('click').click(function(){
var address = $(`#yourAddress${stats.config.coin}`).length
? $(`#yourAddress${stats.config.coin}`).val().trim()
: getCurrentAddress(stats.config.coin);
if (!address || address == '') {
settings_ShowError('noMinerAddress', 'No miner address specified', '', stats);
return;
}
var ip = $(`#yourIP${stats.config.coin}`).val().trim();
if (!ip || ip == '') {
settings_ShowError('noMinerIP', 'No miner IP address specified', '', stats);
return;
}
var level = $(`#yourPayoutRate${stats.config.coin}`).val().trim();
if (!level || level < 0) {
settings_ShowError('noPayoutLevel', 'No payout level specified', '', stats);
return;
}
settings_SetPayoutLevel(api, address, ip, level, stats);
});
// Handle click on Enable button
$(`#enableButton${stats.config.coin}`).off('click').click(function(){
var address = $(`#yourAddress${stats.config.coin}`).val().trim();
var ip = $(`#yourIP${stats.config.coin}`).val().trim();
var email = $(`#yourEmail${stats.config.coin}`).val();
settings_SetEmailNotifications(stats, api, email, address, ip, true);
});
// Handle click on Disable button
$(`#disableButton${stats.config.coin}`).off('click').click(function(){
var address = $(`#yourAddress${stats.config.coin}`).val().trim();
var ip = $(`#yourIP${stats.config.coin}`).val().trim();
var email = $(`#yourEmail${stats.config.coin}`).val();
settings_SetEmailNotifications(stats, api, email, address, ip, false);
});
}
/**
* Error Message
**/
function settings_ShowError(id, message, extra, stats) {
if (getTranslation(id)) message = getTranslation(id);
message = message.trim();
if (extra) message += ' ' + extra;
$(`#action_update_message${stats.config.coin}`).text(message);
$(`#action_update_message${stats.config.coin}`).removeClass().addClass('alert alert-danger');
}
/**
* Success Message
**/
function settings_ShowSuccess(id, message, stats) {
if (getTranslation(id)) message = getTranslation(id);
$(`#action_update_message${stats.config.coin}`).text(message);
$(`#action_update_message${stats.config.coin}`).removeClass().addClass('alert alert-success');
}
/**
* Payout level
**/
// Get current payout level
function settings_GetPayoutLevel(api, address, stats) {
if (!address || address == '')
return;
$.ajax({
url: `${api}/get_miner_payout_level`,
data: {
address: address
},
dataType: 'json',
cache: 'false'
}).done(function(data){
if (data.level != undefined) {
$(`#yourPayoutRate${stats.config.coin}`).val(data.level);
}
});
}
// Set payout level
function settings_SetPayoutLevel(api, address, ip, level, stats) {
let params = {
address: address,
ip: ip,
level: level
}
$.ajax({
url: `${api}/set_miner_payout_level`,
data: params,
dataType: 'json',
cache: 'false'
}).done(function(data){
if (data.status == 'done') {
settings_ShowSuccess('minerPayoutSet', 'Done! Your minimum payout level was set', stats);
} else {
settings_ShowError('Error:', data.status, null, stats);
}
});
}
/**
* Email Notifications
**/
// Check if specified value is a valid email
function settings_IsEmail(email) {
var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
return regex.test(email);
}
// Get current email address for notifications
function settings_GetEmailAddress(endPoint, address, stats) {
if (!address || address == '') return;
$.ajax({
url: `${endPoint}/get_email_notifications`,
data: {
address: address
},
dataType: 'json',
cache: 'false'
}).done(function(data){
if (data.email != undefined) {
$(`#yourEmail${stats.config.coin}`).val(data.email);
}
});
}
// Set email address for notifications
function settings_SetEmailNotifications(stats, endPoint, email, address, ip, enable) {
var address = $(`#yourAddress${stats.config.coin}`).val().trim();
if (!address || address == '') {
settings_ShowError('noMinerAddress', 'No miner address specified', null, stats);
return;
}
var ip = $(`#yourIP${stats.config.coin}`).val().trim();
if (!ip || ip == '') {
settings_ShowError('noMinerIP', 'No miner IP address specified', null, stats);
return;
}
var email = $(`#yourEmail${stats.config.coin}`).val().trim();
if (enable && !email) {
settings_ShowError('noEmail', 'No email address specified', null, stats);
return;
}
if (enable && !settings_IsEmail(email)) {
settings_ShowError('invalidEmail', 'Invalid email address specified', null, stats);
return;
}
$.ajax({
url: `${endPoint}/set_email_notifications`,
data: {
address: address,
ip: ip,
email: email,
action: enable ? 'enable' : 'disable'
},
dataType: 'json',
cache: 'false'
}).done(function(data){
if (data.status == "done") {
if (enable) {
settings_ShowSuccess('notificationEnabled', 'Done! Email notifications have been enabled', stats);
} else {
settings_ShowSuccess('notificationDisabled', 'Done! Email notifications have been disabled', stats);
}
} else {
settings_ShowError('error', 'Error:', data.status, stats);
}
});
}
function settings_InitTemplate(ranOnce) {
if (!lastStats.config.sendEmails) $(`#emailNotifications${lastStats.config.coin}`).hide();
let coin = lastStats.config.coin
let template = $('#siblingTemplate').html()
if ($(`#blocksTabs li:contains(${coin})`).length === 0) {
Mustache.parse(template)
let rendered = Mustache.render(template, {coin:coin, active:'active'})
$('#tab-content').append(rendered)
template = $('#siblingTabTemplate').html();
Mustache.parse(template)
rendered = Mustache.render(template, {coin:lastStats.config.coin, symbol:`(${lastStats.config.symbol})`, active:'active'});
$('#blocksTabs').append(rendered)
settings_Setup(api, lastStats)
}
if (!ranOnce)
ranOnce = RunOnce()
}
/*
***************************************************************
payments methods
***************************************************************
*/
// Parse payment data
function payments_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 cells
function payments_GetPaymentCells(payment, stats){
return '<td class="col1">' + formatDate(payment.time) + '</td>' +
'<td class="col2">' + formatPaymentLink(payment.hash, stats) + '</td>' +
'<td class="col3">' + (getReadableCoin(stats, payment.amount)) + '</td>' +
'<td class="col4">' + (getReadableCoin(stats, payment.fee)) + '</td>' +
'<td class="col5">' + payment.mixin + '</td>' +
'<td class="col6">' + payment.recipients + '</td>';
}
// Get payment row element
function payments_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 = payments_GetPaymentCells(payment, stats);
return row;
}
// Render payments data
function payments_renderPayments(paymentsResults, stats){
var $paymentsRows = $(`#paymentsReport${stats.config.coin}_rows`);
for (var i = 0; i < paymentsResults.length; i += 2){
var payment = payments_ParsePayment(paymentsResults[i + 1], paymentsResults[i]);
var paymentJson = JSON.stringify(payment);
var existingRow = document.getElementById(`paymentRow${stats.config.coin}${payment.time}`);
if (existingRow && existingRow.getAttribute(`data-json`) !== paymentJson){
$(existingRow).replaceWith(payments_GetPaymentRowElement(payment, paymentJson, stats));
}
else if (!existingRow){
var paymentElement = payments_GetPaymentRowElement(payment, paymentJson, stats);
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 < payment.time){
inserted = true;
$(rows[f]).before(paymentElement);
break;
}
}
if (!inserted) {
$paymentsRows.append(paymentElement);
}
}
}
}
// Load more payments button
function payments_Setup(xhrGetPayments, api, stats) {
$(`#loadMorePayments${stats.config.coin}`).click(function(){
if (xhrGetPayments[stats.config.coin]) xhrGetPayments[stats.config.coin].abort();
xhrGetPayments[stats.config.coin] = $.ajax({
url: api + '/get_payments',
data: {
time: $(`#paymentsReport${stats.config.coin}_rows`).children().last().data(`time`)
},
dataType: 'json',
cache: 'false',
success: function(data){
payments_renderPayments(data, stats);
}
});
});
}
function payments_InitTemplate(xhrGetPayments, ranOnce) {
let coin = lastStats.config.coin
if ($(`#blocksTabs li:contains(${coin})`).length === 0) {
let template1 = $('#siblingTemplate').html()
Mustache.parse(template1)
let rendered1 = Mustache.render(template1, {coin:coin, active:'active'})
$('#tab-content').append(rendered1)
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)
payments_Setup(xhrGetPayments, api, lastStats)
}
updateText(`paymentsTotal${coin}`, lastStats.pool.totalPayments.toString());
updateText(`paymentsTotalPaid${coin}`, lastStats.pool.totalMinersPaid.toString());
updateText(`paymentsInterval${coin}`, getReadableTime(lastStats.config.paymentsInterval));
updateText(`paymentsMinimum${coin}`, getReadableCoin(lastStats, lastStats.config.minPaymentThreshold));
updateText(`paymentsDenomination${coin}`, getReadableCoin(lastStats, lastStats.config.denominationUnit, 3));
payments_renderPayments(lastStats.pool.payments, lastStats);
toggleSingleTabNav('#blocksTabs');
if (!ranOnce)
ranOnce = RunOnce()
}
/*
***************************************************************
market methods
***************************************************************
*/
function market_LoadMarketData(api, stats, loadedData, currencyPairs, xhrMarketGets, marketPrices) {
if (loadedData[stats.config.coin]) return ;
if (typeof marketCurrencies !== 'undefined' && marketCurrencies.length > 0){
let intervalMarketPolling = setInterval(market_UpdateMarkets(api, stats, currencyPairs, xhrMarketGets, marketPrices), 300000);
market_UpdateMarkets(api, stats, currencyPairs, xhrMarketGets, marketPrices);
} else {
$(`#marketInfos${stats.config.coin}`).hide();
}
loadedData[stats.config.coin] = true;
}
// Market data polling (poll data every 5 minutes)
function market_UpdateMarkets(api, stats, currencyPairs, xhrMarketGets, marketPrices){
if (typeof marketCurrencies === 'undefined' || marketCurrencies.length === 0) return ;
if (typeof exchangeName === 'undefined' || exchangeName === 0) { let exchangeName = "exbitron" } ;
currencyPairs[stats.config.coin] = []
for (let i = 0; i < marketCurrencies.length; i++){
currencyPairs[stats.config.coin].push(marketCurrencies[i].replace('{symbol}', stats.config.symbol).toUpperCase());
}
if (xhrMarketGets[stats.config.coin]) xhrMarketGets[stats.config.coin].abort()
xhrMarketGets[stats.config.coin] = $.ajax({
url: api + '/get_market',
data: { tickers: currencyPairs[stats.config.coin], exchange: exchangeName },
dataType: 'json',
cache: 'false',
success: function(data) {
if (!data || data.length === 0) {
$(`#marketInfos${stats.config.coin}`).hide();
return;
}
$(`#marketInfos${stats.config.coin}`).empty();
for (let i in data) {
if (!data[i] || !data[i].ticker) continue;
let ticker = data[i].ticker;
let tickerParts = ticker.split('-');
let tickerBase = tickerParts[0] || null;
let tickerTarget = tickerParts[1] || null;
let price = data[i].price;
if (!price || price === 0) continue;
let dataSource = data[i].source;
market_RenderMarketPrice(tickerBase, tickerTarget, price, dataSource, stats, marketPrices);
}
$(`#marketInfos${stats.config.coin}`).show();
},
error: function() {
$(`#marketInfos${stats.config.coin}`).hide();
}
});
}
// Render market price
function market_RenderMarketPrice(base, target, price, source, stats, marketPrices) {
let icon = 'fa-money';
if (target == 'BTC') icon = 'fa-btc';
if (target == 'BCH') icon = 'fa-btc';
if (target == 'USD') icon = 'fa-dollar';
if (target == 'USDT') icon = 'fa-dollar';
if (target == 'CAD') icon = 'fa-dollar';
if (target == 'EUR') icon = 'fa-eur';
if (target == 'GBP') icon = 'fa-gbp';
if (target == 'JPY') icon = 'fa-jpy';
if (base == stats.config.symbol.toUpperCase()) {
marketPrices[stats.config.coin][target] = price;
}
if (target == 'USD' || target == 'USDT' || target == 'CAD' || target == 'EUR' || target == 'GBP' || target == 'JPY') {
price = price.toFixed(4);
} else {
price = price.toFixed(8);
}
let sourceURL = null;
if (source == 'coinex') sourceURL = 'https://www.coinex.com/';
else if (source == 'mexc') sourceURL = 'https://www.mexc.com/';
else if (source == 'nonkyc') sourceURL = 'https://nonkyc.io/';
else if (source == 'exbitron') sourceURL = 'https://app.exbitron.com/';
else if (source == 'klingex') sourceURL = 'https://klingex.io/';
else if (source == 'anonex') sourceURL = 'https://anonex.io/';
else if (source == 'coingecko') sourceURL = 'https://www.coingecko.com/';
else if (source == 'coinpaprika') sourceURL = 'https://coinpaprika.com/';
source = source.charAt(0).toUpperCase() + source.slice(1);
if (sourceURL) source = '<a href="'+sourceURL+'" target="_blank" rel="nofollow">'+source+'</a>';
$(`#marketInfos${stats.config.coin}`).append(
'<div class="col-lg-3 col-md-4 col-sm-6 marketTicker"><div class="infoBox hoverExpandEffect">' +
'<div class="icon"><span class="fa '+ icon + '"></span></div>' +
'<div class="content">' +
'<div class="text">' + base + ' to ' + target + '</div>' +
'<div class="value">' + price + '</div>' +
'<div class="source">Source: ' + source + '</div>' +
'</div>' +
'</div>'
);
}
/**
* Market Charts
**/
// Create charts
function market_CreateCharts(stats) {
if (!stats || !stats.charts) return ;
let data = stats.charts;
let graphData = {
price: market_GetGraphData(data.price),
profit: market_GetGraphData(data.profit)
};
for(let graphType in graphData) {
if (graphData[graphType].values.length > 1) {
let $chart = $(`#chart${stats.config.coin}_${graphType}`);
let bgcolor = null, bordercolor = null, borderwidth = null;
let colorelem = $chart.siblings('a.chart-style');
if (colorelem.length == 1) {
bgcolor = colorelem.css('background-color');
bordercolor = colorelem.css('border-left-color');
borderwidth = parseFloat(colorelem.css('width'));
}
if (bgcolor === null) bgcolor = 'rgba(3, 169, 244, .4)';
if (bordercolor === null) bordercolor = '#03a9f4';
if (borderwidth === null || isNaN(borderwidth)) borderwidth = 1;
let chartObj = new Chart(document.getElementById(`chart${stats.config.coin}_${graphType}`), {
type: 'line',
data: {
labels: graphData[graphType].names,
datasets: [{
data: graphData[graphType].values,
dataType: graphType,
fill: true,
backgroundColor: bgcolor,
borderColor: bordercolor,
borderWidth: borderwidth
}]
},
options: {
animation: false,
responsive: true,
maintainAspectRatio: false,
legend: { display: false },
elements: { point: { radius: 0, hitRadius: 10, hoverRadius: 5 } },
scales: {
xAxes: [{
display: false,
ticks: { display: false },
gridLines: { display: false }
}],
yAxes: [{
display: false,
ticks: {
display: false,
beginAtZero: true,
userCallback: function(label, index, labels) {
if (Math.floor(label) === label) return label;
}
},
gridLines: { display: false }
}]
},
layout: {
padding: { top: 5, left: 10, right: 10, bottom: 10 }
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
let dataType = data.datasets[tooltipItem.datasetIndex].dataType || '';
let label = tooltipItem.yLabel;
if (dataType == 'price') label = parseFloat(tooltipItem.yLabel).toFixed(4);
else if (dataType == 'profit') label = parseFloat(tooltipItem.yLabel).toFixed(10);
return ' ' + label;
}
}
}
}
});
$chart.closest('.marketChart').show();
}
}
}
// Get chart data
function market_GetGraphData(rawData) {
let graphData = {
names: [],
values: []
};
if(rawData) {
for (let i = 0, xy; xy = rawData[i]; i++) {
graphData.names.push(new Date(xy[0]*1000).toLocaleString());
graphData.values.push(xy[1]);
}
}
return graphData;
}
// Calculate current estimation
function market_CalcEstimateProfit(marketPrices){
let rateUnit = Math.pow(1000,parseInt($('#calcHashUnit').data('mul')));
let hashRate = parseFloat($('#calcHashRate').val()) * rateUnit;
let coin = lastStats.config.coin
try {
if ($(`#calcHashAmount${coin}`).length == 0) {
let template = $(`#calcHashResultTemplate`).html()
Mustache.parse(template)
let rendered = Mustache.render(template, {coin:coin})
$(`#calcHashHolder`).append(rendered)
}
let profit = (hashRate * 86400 / lastStats.network.difficulty) * lastStats.lastblock.reward;
if (profit) {
updateText(`calcHashAmount${coin}1`, getReadableCoin(lastStats, profit));
updateText(`calcHashAmount${coin}2`, market_GetCurrencyPriceText(lastStats, profit, marketPrices));
//return;
} else {
updateText(`calcHashAmount${coin}1`, '');
updateText(`calcHashAmount${coin}2`, '');
}
}
catch (e){
updateText(`calcHashAmount${coin}1`, '');
updateText(`calcHashAmount${coin}2`, '');
}
}
// Get price in specified currency
function market_GetCurrencyPriceText(stats, coinsRaw, marketPrices) {
if (!priceCurrency || !marketPrices[stats.config.coin] || !marketPrices[stats.config.coin][priceCurrency]) return ;
let priceInCurrency = (Math.trunc(getReadableCoin(stats, coinsRaw, 2, true) * marketPrices[stats.config.coin][priceCurrency] * 100) / 100);
return priceInCurrency + ' ' + priceCurrency;
}
function market_InitTemplate(ranOnce, chartsInitialized, loadedData, marketPrices, intervalChartsUpdate, currencyPairs, xhrMarketGets) {
priceSource = lastStats.config.priceSource || 'cryptonator';
priceCurrency = lastStats.config.priceCurrency || 'USD';
let coin = lastStats.config.coin
if ($(`#blocksTabs li:contains(${coin})`).length == 0) {
chartsInitialized[coin] = false
loadedData[coin] = false
marketPrices[coin] = {}
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)
let template1 = $('#siblingMarketTemplate').html()
Mustache.parse(template1)
let rendered1 = Mustache.render(template1, {coin:coin, active:'active'})
$(`#tab-content`).append(rendered1)
let template2 = $('#siblingCalculatorTemplate').html()
Mustache.parse(template2)
let rendered2 = Mustache.render(template2, {coin:coin})
$(`#calculator`).append(rendered2)
updateText(`priceChartCurrency${lastStats.config.coin}`, priceCurrency);
updateText(`profitChartProfit${lastStats.config.coin}`, priceCurrency);
if (lastStats.charts && !chartsInitialized[coin]) {
intervalChartsUpdate[coin] = setInterval(market_CreateCharts(lastStats), 60*1000);
market_CreateCharts(lastStats);
chartsInitialized[coin] = true;
}
}
market_LoadMarketData(api, lastStats, loadedData, currencyPairs, xhrMarketGets, marketPrices);
market_CalcEstimateProfit(marketPrices);
if (!ranOnce)
ranOnce = RunOnce()
}
/*
***************************************************************
workerstats methods
***************************************************************
*/
function workerstats_Setup(stats, api, addressTimeout, xhrAddressPoll, xhrGetPayments ) {
// Enable time ago on last submitted share
$(`#yourLastShare${stats.config.coin}`).timeago();
$(`#lookUp${stats.config.coin}`).click(function(){
var address = $(`#yourStatsInput${stats.config.coin}`).val().trim();
if (getCurrentAddress(stats.config.coin) != address) {
docCookies.setItem(`mining_address_${stats.config.coin}`, address, Infinity);
var urlWalletAddress = location.search.split('walletMerged=')[1] || 0;
if (urlWalletAddress){
window.location.href = "/#worker_stats";
return ;
}
else {
docCookies.setItem(`mining_address_${stats.config.coin}`, address, Infinity);
loadLiveStats(true);
}
}
$(`#addressError${stats.config.coin}, .yourStats${stats.config.coin}, .yourWorkers${stats.config.coin}, .userChart${stats.config.coin}`).hide();
$(`#workersReport_rows_${stats.config.coin}`).empty();
$(`#paymentsReport_rows_${stats.config.coin}`).empty();
$(`#lookUp${stats.config.coin} > span:first-child`).hide();
$(`#lookUp${stats.config.coin} > span:last-child`).show();
if (addressTimeout[stats.config.coin]) clearTimeout(addressTimeout[stats.config.coin]);
if (xhrAddressPoll[stats.config.coin])
xhrAddressPoll[stats.config.coin].abort();
$(`#lookUp${stats.config.coin} > span:last-child`).hide();
$(`#lookUp${stats.config.coin} > span:first-child`).show();
if (!address){
$(`#yourStatsInput${stats.config.coin}`).focus();
return;
}
workerstats_FetchAddressStats(false, stats, api, xhrAddressPoll);
});
var address = getCurrentAddress(stats.config.coin);
if (address){
$(`#yourStatsInput${stats.config.coin}`).val(address);
$(`#lookUp${stats.config.coin}`).click();
}
else {
$(`#lookUp${stats.config.coin} > span:last-child`).hide();
$(`#lookUp${stats.config.coin} > span:first-child`).show();
$(`#addressError${stats.config.coin}, .yourStats${stats.config.coin}, .yourWorkers${stats.config.coin}, .userChart${stats.config.coin}`).hide();
}
$(`#yourStatsInput${stats.config.coin}`).keyup(function(e){
if(e.keyCode === 13)
$(`#lookUp${stats.config.coin}`).click();
});
// Handle sort on workers table
//$(`#workersReport${stats.config.coin} th.sort`).on('click', sortTable);
$(`.workerStats th.sort`).on('click', sortTable);
// Load more payments button
$(`#loadMorePayments${stats.config.coin}`).click(function(xhrGetPayments){
if (xhrGetPayments[stats.config.coin])
xhrGetPayments[stats.config.coin].abort();
xhrGetPayments[stats.config.coin] = $.ajax({
url: `${api}/get_payments`,
data: {
time: $(`#paymentsReport_rows_${stats.config.coin}`).children().last().data('time'),
address: address
},
dataType: 'json',
cache: 'false',
success: function(data){
workerstats_RenderPayments(data, stats);
}
});
});
}
/**
* Miner statistics
**/
// Load current miner statistics
function workerstats_FetchAddressStats(longpoll, stats, api, xhrAddressPoll){
let address = getCurrentAddress(stats.config.coin)
xhrAddressPoll[stats.config.coin] = $.ajax({
url: `${api}/stats_address`,
data: {
address: address,
longpoll: longpoll
},
dataType: 'json',
cache: 'false',
success: function(data){
if (!data.stats){
$(`.yourStats${stats.config.coin}, .yourWorkers${stats.config.coin}, .userChart${stats.config.coin}`).hide();
$(`#addressError${stats.config.coin}`).text(data.error).show();
docCookies.setItem(`mining_address_${stats.config.coin}`, '', Infinity);
loadLiveStats(true);
return;
}
$(`#addressError${stats.config.coin}`).hide();
if (data.stats.lastShare) {
$(`#yourLastShare${stats.config.coin}`).timeago('update', new Date(parseInt(data.stats.lastShare) * 1000).toISOString());
} // AQUÍ
else {
updateText(`yourLastShare${stats.config.coin}`, 'Never');
}
updateText(`yourHashrateHolder${stats.config.coin}`, (getReadableHashRateString(data.stats.hashrate) || '0 H') + '/sec');
if ('hashrate_1h' in data.stats) {
$(`#minerAvgHR${stats.config.coin}`).show();
updateText(`yourHR1h${stats.config.coin}`, (getReadableHashRateString(data.stats.hashrate_1h) || '0 H') + '/s');
updateText(`yourHR6h${stats.config.coin}`, (getReadableHashRateString(data.stats.hashrate_6h) || '0 H') + '/s');
updateText(`yourHR24h${stats.config.coin}`, (getReadableHashRateString(data.stats.hashrate_24h) || '0 H') + '/s');
} else {
$(`#minerAvgHR${stats.config.coin}`).hide();
}
let totalCoins = data.stats.paid;
let last24hCoins = 0;
let last7dCoins = 0;
for (let i = 0; i < data.payments.length; i += 2) {
let payment = workerstats_ParsePayment(data.payments[i + 1], data.payments[i]);
let paymentDate = new Date(parseInt(payment.time) * 1000);
let daysDiff = moment().diff(moment(paymentDate), 'days');
if (daysDiff < 1) {
last24hCoins = last24hCoins + parseInt(payment.amount);
}
if (daysDiff < 7) {
last7dCoins = last7dCoins + parseInt(payment.amount);
}
}
// $.getJSON(`https://api.coingecko.com/api/v3/coins/${stats.config.coin.toLowerCase()}?sparkline=true`, function() {})
// .done(data => {
// let paidTotalUSD = getReadableCoin(stats, totalCoins, 2, true) * data.market_data.current_price.usd;
// let paid24hUSD = getReadableCoin(stats, last24hCoins, 2, true) * data.market_data.current_price.usd;
// let paid7dUSD = getReadableCoin(stats, last7dCoins, 2, true) * data.market_data.current_price.usd;
// updateText(`yourPaid${stats.config.coin}`, `${getReadableCoin(stats, totalCoins)} - $${paidTotalUSD.toFixed(2)}`);
// updateText(`paid24h${stats.config.coin}`, `${getReadableCoin(stats, last24hCoins)} - $${paid24hUSD.toFixed(2)}`);
// updateText(`paid7d${stats.config.coin}`, `${getReadableCoin(stats, last7dCoins)} - $${paid7dUSD.toFixed(2)}`);
// })
// .fail(() => {
updateText(`yourPaid${stats.config.coin}`, getReadableCoin(stats, totalCoins));
updateText(`paid24h${stats.config.coin}`, getReadableCoin(stats, last24hCoins));
updateText(`paid7d${stats.config.coin}`, getReadableCoin(stats, last7dCoins));
// })
updateText(`yourHashes${stats.config.coin}`, (data.stats.hashes || 0).toString());
//updateText(`yourPaid${stats.config.coin}`, getReadableCoin(stats, data.stats.paid));
updateText(`yourPendingBalance${stats.config.coin}`, getReadableCoin(stats, data.stats.balance));
let userRoundHashes = parseInt(data.stats.roundHashes || 0);
let poolRoundHashes = parseInt(stats.pool.roundHashes || 0);
let userRoundScore = parseFloat(data.stats.roundScore || 0);
let poolRoundScore = parseFloat(stats.pool.roundScore || 0);
let lastReward = parseFloat(stats.lastblock.reward || 0);
let poolFee = stats.config.fee;
if (Object.keys((stats.config.donation)).length) {
let totalDonation = 0;
let ldon = stats.config.donation;
for(let i in ldon) {
totalDonation += ldon[i];
}
poolFee += totalDonation;
}
let transferFee = stats.config.transferFee;
let share_pct = userRoundHashes * 100 / poolRoundHashes;
let score_pct = userRoundScore * 100 / poolRoundScore;
updateText(`yourRoundShareProportion${stats.config.coin}`, Math.round(share_pct * 1000) / 1000);
updateText(`yourRoundScoreProportion${stats.config.coin}`, Math.round(score_pct * 1000) / 1000);
if (!lastStats.config.slushMiningEnabled) {
$(`#slush_round_info${stats.config.coin}`).hide();
}
let payoutEstimatePct = parseFloat(userRoundHashes * 100 / poolRoundHashes)
let payoutEstimate = Math.round(lastReward * (payoutEstimatePct / 100));
if (transferFee) payoutEstimate = payoutEstimate - transferFee;
if (payoutEstimate < 0)
payoutEstimate = 0;
updateText(`yourPayoutEstimate${stats.config.coin}`, getReadableCoin(stats, payoutEstimate));
workerstats_RenderPayments(data.payments, stats);
if (data.workers && data.workers.length > 0) {
workerstats_RenderWorkers(data.workers, stats);
$(`.yourWorkers${stats.config.coin}`).show();
}
$(`.yourStats${stats.config.coin}`).show();
workerstats_CreateCharts(data, stats);
},
error: function(e){
if (e.statusText === 'abort') return;
$(`#addressError${stats.config.coin}`).text('Connection error').show();
if (addressTimeout[stats.config.coin])
clearTimeout(addressTimeout[stats.config.coin]);
addressTimeout[stats.config.coin] = setTimeout(function(){
workerstats_FetchAddressStats(false, stats, api);
}, 2000);
}
});
}
/**
* Charts
**/
let workerChartInstances = {};
let homeChartInstances = {};
function destroyApexChart(store, key) {
if (store[key]) {
try {
store[key].destroy();
} catch (e) {}
delete store[key];
}
}
function destroyApexChartsByPrefix(store, prefix) {
Object.keys(store).forEach(function(key) {
if (key.indexOf(prefix) === 0) {
destroyApexChart(store, key);
}
});
}
function createModernAreaChart(containerSelector, chartKey, categories, values, options) {
const chartContainer = document.querySelector(containerSelector);
if (!chartContainer || typeof ApexCharts === 'undefined') {
return;
}
destroyApexChart(options.store, chartKey);
chartContainer.innerHTML = '';
const chart = new ApexCharts(chartContainer, {
chart: {
type: 'area',
height: options.height || 220,
toolbar: { show: false },
zoom: { enabled: false },
foreColor: '#9db7b2',
animations: {
enabled: true,
easing: 'easeinout',
speed: 350
}
},
series: [{
name: options.label,
data: values
}],
xaxis: {
categories: categories,
labels: {
rotate: 0,
trim: true,
style: {
colors: '#59727a',
fontSize: '11px'
}
},
axisBorder: { show: false },
axisTicks: { show: false }
},
yaxis: {
labels: {
style: {
colors: '#59727a',
fontSize: '11px'
},
formatter: options.yFormatter
}
},
dataLabels: { enabled: false },
stroke: {
curve: 'smooth',
width: 3
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.34,
opacityTo: 0.04,
stops: [0, 95, 100]
}
},
colors: [options.color],
grid: {
borderColor: 'rgba(136, 255, 225, 0.08)',
strokeDashArray: 4
},
tooltip: {
theme: 'dark',
x: {
formatter: function(value, ctx) {
return categories[ctx.dataPointIndex];
}
},
y: {
formatter: options.tooltipFormatter || options.yFormatter
}
},
legend: { show: false }
});
chart.render();
options.store[chartKey] = chart;
}
// Create charts
function workerstats_CreateCharts(data, stats) {
if (!data.hasOwnProperty("charts")) {
return;
}
var graphData = {
hashrate: workerstats_GetGraphData(stats, data.charts.hashrate),
payments: workerstats_GetGraphData(stats, data.charts.payments, true)
};
var chartMeta = {
hashrate: {
label: 'Hashrate',
color: '#4cc8ff',
yFormatter: function(value) {
return getReadableHashRateString(value || 0) + '/s';
}
},
payments: {
label: 'Payments',
color: '#42f5c8',
yFormatter: function(value) {
return Number(value || 0).toFixed(4) + ' ' + stats.config.symbol;
}
}
};
for (var graphType in graphData) {
var graph = graphData[graphType];
var chartKey = stats.config.coin + '_' + graphType;
var selector = `[data-chart=user_${graphType}_${stats.config.coin}] .chart`;
if (graph.values.length > 1) {
$(`[data-chart=user_${graphType}_${stats.config.coin}]`).show();
createModernAreaChart(selector, chartKey, graph.names, graph.values, {
store: workerChartInstances,
label: chartMeta[graphType].label,
color: chartMeta[graphType].color,
yFormatter: chartMeta[graphType].yFormatter,
height: 180
});
} else {
destroyApexChart(workerChartInstances, chartKey);
$(`[data-chart=user_${graphType}_${stats.config.coin}]`).hide();
}
}
}
// Get chart data
function workerstats_GetGraphData(stats, 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(stats, xy[1], null, true) : xy[1]);
}
}
return graphData;
}
/**
* Workers report
**/
// Get worker row id
function workerstats_GetWorkerRowId(workerName){
var id = btoa(workerName);
id = id.replace(/=/, '');
return id;
}
// Get worker row element
function workerstats_GetWorkerRowElement(worker, jsonString, stats){
var row = document.createElement('tr');
row.setAttribute('data-json', jsonString);
row.setAttribute('data-name', worker.name);
row.setAttribute('id', 'workerRow' + stats.config.coin + '_' + workerstats_GetWorkerRowId(worker.name));
row.innerHTML = workerstats_GetWorkerCells(worker);
return row;
}
// Get worker cells
function workerstats_GetWorkerCells(worker){
let hashrate = worker.hashrate ? worker.hashrate : 0;
let hashrate1h = worker.hashrate_1h || 0;
let hashrate6h = worker.hashrate_6h || 0;
let hashrate24h = worker.hashrate_24h || 0;
let lastShare = worker.lastShare ? worker.lastShare : 0;
let hashes = (worker.hashes || 0).toString();
let status = (hashrate <= 0) ? 'error' : 'ok';
return '<td class="col1" data-sort="' + status + '"><i class="fa fa-' + (status == 'ok' ? 'check status-ok' : 'times status-error') + '"></i></td>' +
'<td class="col2" data-sort="' + (worker.name != 'undefined' ? worker.name : '') + '">' + (worker.name != 'undefined' ? worker.name : '<em>Undefined</em>') + '</td>' +
'<td class="col3" data-sort="' + hashrate + '">' + getReadableHashRateString(hashrate) + '/s</td>' +
'<td class="col4 avghr" data-sort="' + hashrate1h + '">' + getReadableHashRateString(hashrate1h) + '/s</td>' +
'<td class="col5 avghr" data-sort="' + hashrate6h + '">' + getReadableHashRateString(hashrate6h) + '/s</td>' +
'<td class="col6 avghr" data-sort="' + hashrate24h + '">' + getReadableHashRateString(hashrate24h) + '/s</td>' +
'<td class="col4" data-sort="' + lastShare + '">' + (lastShare ? $.timeago(new Date(parseInt(lastShare) * 1000).toISOString()) : 'Never') + '</td>' +
'<td class="col5" data-sort="' + hashes + '">' + hashes + '</td>';
}
// 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 '<td class="col1">' + formatDate(payment.time) + '</td>' +
'<td class="col2">' + formatPaymentLink(payment.hash, stats) + '</td>' +
'<td class="col3">' + getReadableCoin(stats, payment.amount) + '</td>' +
'<td class="col4">' + payment.mixin + '</td>';
}
// 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 '<td colspan="4">' + text + '</td>';
}
// 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)
settings_Setup(api, lastStats)
}
toggleSingleTabNav('#blocksTabs');
if (!ranOnce)
ranOnce = RunOnce()
}
/*
***************************************************************
workerstats methods
***************************************************************
*/
function home_CreateCharts(data) {
if (!data.hasOwnProperty("charts")) {
return;
}
var graphData = {
hashrate: home_GetGraphData(data.charts.hashrate),
diff: home_GetGraphData(data.charts.difficulty),
miners: home_GetGraphData(data.charts.miners),
workers: home_GetGraphData(data.charts.workers)
};
var chartMeta = {
hashrate: {
label: 'Hashrate',
color: '#4cc8ff',
yFormatter: function(value) {
return getReadableHashRateString(value || 0) + '/s';
}
},
diff: {
label: 'Difficulty',
color: '#d4ff69',
yFormatter: function(value) {
return formatNumber(Math.round(value || 0).toString(), ' ');
}
},
miners: {
label: 'Miners',
color: '#42f5c8',
yFormatter: function(value) {
return Math.round(value || 0).toString();
}
},
workers: {
label: 'Workers',
color: '#ff9eb8',
yFormatter: function(value) {
return Math.round(value || 0).toString();
}
}
};
for (var graphType in graphData) {
var graph = graphData[graphType];
if (graph.values.length > 1) {
var selector = '[data-chart=' + graphType + '] .chart';
var chartKey = 'home_' + graphType;
$(selector).closest('.poolChart').show();
createModernAreaChart(selector, chartKey, graph.names, graph.values, {
store: homeChartInstances,
label: chartMeta[graphType].label,
color: chartMeta[graphType].color,
yFormatter: chartMeta[graphType].yFormatter,
height: 220
});
} else {
destroyApexChart(homeChartInstances, 'home_' + graphType);
$('[data-chart=' + graphType + ']').closest('.poolChart').hide();
}
}
}
// 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) {
$('#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(), ' '));
updateText(`networkLastReward${coin}`, getReadableCoin(parentStats, parentStats.lastblock.reward));
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}`);
renderLucideIcons(document.body);
var totalFee = parentStats.config.fee;
var soloFee = parentStats.config.soloFee;
if (Object.keys(parentStats.config.donation).length) {
var totalDonation = 0;
for(var i in parentStats.config.donation) {
totalDonation += parentStats.config.donation[i];
}
totalFee += totalDonation;
soloFee += totalDonation;
}
updateText('poolFee', (totalFee > 0 && totalFee != 100 ? floatToString(totalFee) : (totalFee == 100 ? '100' : '0')) + '%/' + soloFee + '%');
updateText('finderReward', parentStats.config.finderReward + '%');
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) + '%');
}