btc-rpc-explorer/views/includes/shared-mixins.pug
Dan Janosik a678c8a6a9
-more styling tweaks/cleanup/consistency
-new fun item showing example of future-dated timelock (coinbase) tx
-special-case code for mainnet genesis coinbase tx, to avoid indicating that satoshi spent these unspendable coins
-tx details: skip displaying locktime when it's meaningless (the raw data is in the json if anyone wants it); show additional info about locktime meaning
-tx details: show byte-size in addition to vByte size, if applicable
-fix for recently broken donut chart on /mining-summary
-changelog
2021-12-06 16:43:10 -05:00

876 lines
36 KiB
Text

mixin themeCss
link(id="light-theme-link-tag", rel="stylesheet", href=`./style/light.css?v=${cacheId}`, integrity="sha384-G+XehzyDofsc9ZlqIF7hYXsaYijKshaTro74HxGnvEmvNiv2lRjGxO8g8HShNBQG")
link(id="dark-theme-link-tag", rel=(userSettings.uiTheme == "dark" ? "stylesheet" : false), href=`./style/dark.css?v=${cacheId}`, integrity="sha384-xjIBl325k0VYsT0wAem/8jzIxea9FDLsVmHZG8STGMhLu+rDq/xlWxo/iR6oJQ6w")
mixin sharedScriptTags
script(src=`./js/jquery.min.js?v=${cacheId}`, integrity="sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK")
script(src=`./js/bootstrap.bundle.min.js?v=${cacheId}`, integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p")
script(defer, src=`./js/fontawesome.min.js?v=${cacheId}`, integrity="sha384-haqrlim99xjfMxRP6EWtafs0sB1WKcMdynwZleuUSwJR0mDeRYbhtY+KPMr+JL6f")
script(src=`./js/site.js?v=${cacheId}`, integrity="sha384-RNCa5sCPh1hkJyKAu8z5Hai8r9IxpVBF2W6GFhSb2JSuSlECW8d85ufzyXBSRwLZ")
mixin pageTitle(text, subtext, copyableSubtext=false)
.d-flex.justify-content-between
h1.fw-light.word-wrap.mb-1
| #{text}
if (block)
.text-right
block
h4.fw-light.word-wrap.mb-4 #{subtext}
if (copyableSubtext)
small
+copyTextButton(subtext)
//hr.mb-3
mixin sectionTitleBlock
h3.h5.mb-1.fw-light
block
mixin sectionTitle(text, toggle=false, toggleUniqueClass, toggleUserSettingName, toggleOpen, tooltipText)
if (false)
pre
code.json #{JSON.stringify(session.userSettings)}
h3.h5.mb-1.fw-light(class=(toggle && !toggleOpen ? "mb-section" : false), class=(!toggle || toggleOpen ? "d-block" : "d-none"))
if (tooltipText)
span.border-dotted(title=tooltipText, data-bs-toggle="tooltip")
| #{text}
else
| #{text}
if (toggle)
small(title=`Toggle ${text}`, data-bs-toggle="tooltip")
a.text-card-highlight.fs-6(href=`./changeSetting?name=${toggleUserSettingName}&value=false`)
i.toggle-plus-minus.far.ms-2(class=(toggleOpen ? "fa-minus-square" : "fa-plus-square"))
mixin contentSection(title, toggleable=false, toggleUserSettingName, defaultOpen=true, cardUi=true)
if (toggleable)
- var toggleUniqueClass = `section-${utils.getRandomString(10, "aA#")}`;
- var toggleOpen = userSettings[toggleUserSettingName] == null ? defaultOpen : (userSettings[toggleUserSettingName] == "true" || userSettings[toggleUserSettingName] == true);
if (title)
+sectionTitle(title, toggleable, toggleUniqueClass, toggleUserSettingName, toggleOpen)
.mb-section(class=toggleUniqueClass, style=(toggleable && !toggleOpen ? "display: none;" : false))
if (cardUi)
.card.mb-section.shadow-sm
.card-body
block
else
block
mixin summaryRow(itemCount)
- locals.summaryItemCount = itemCount;
- locals.summaryItemIndex = 0;
- locals.colCounts = {"sm": 1, "md": Math.min(itemCount, 3), "lg": Math.min(itemCount, 4), "xl": Math.min(itemCount, 5)};
- locals.rowCountsArray = utils.objectProperties(locals.colCounts).map(x => [x, locals.colCounts[x]]).map(x => [x[0], (Math.floor(itemCount / x[1]) + ((itemCount % x[1] > 0) ? 1 : 0))]);
- locals.rowCounts = {};
- locals.rowCountsArray.forEach(x => locals.rowCounts[x[0]] = x[1]);
//h1 #{itemCount} #{JSON.stringify(locals.rowCounts)}
.row.row-cols-1(class=utils.objectProperties(locals.colCounts).map(x => `row-cols-${x}-${locals.colCounts[x]}`).join(" "))
block
mixin summaryTitle(title, titleDesc, subtitle, subtitleDesc)
span.fs-6.text-uppercase.fw-light.text-card-highlight(class=(titleDesc ? "border-dotted" : false), class=(subtitle ? "me-2" : false), title=titleDesc, data-bs-toggle="tooltip", data-bs-html="true") #{title}
if (subtitle)
small.text-card-highlight.fw-light
| (
span(class=(subtitleDesc ? "border-dotted" : false), title=subtitleDesc, data-bs-toggle="tooltip", data-bs-html="true") #{subtitle}
| )
mixin summaryItem(title, titleDesc, subtitle, subtitleDesc)
- var rowIndexes = utils.objectProperties(locals.colCounts).map(x => [x, locals.colCounts[x]]).map(x => [x[0], Math.floor(locals.summaryItemIndex / x[1])]);
.col(class=(locals.summaryItemIndex == (locals.summaryItemCount - 1) ? "mb-0" : "mb-3"), class=rowIndexes.map(x => `mb-${x[0]}-${(x[1] < (locals.rowCounts[x[0]] - 1) ? "4" : "0")}`))
//span.text-danger (#{JSON.stringify(locals.rowCounts)})
.text-start.text-md-center
+summaryTitle(title, titleDesc, subtitle, subtitleDesc)
.lead.text-start.text-md-center
block
- locals.summaryItemIndex++;
// options keys:
// - blockHeightsByTxid
// - sharedBlockHeight (int)
// - showDates (true|false)
// - highlightAddress (address)
// - addrGainsByTxid (map)
// - addrLossesByTxid (map)
// - mempoolDetailsByTxid (map)
// txBlockHeightsByTxid_orSharedBlockHeight, showDates=false, highlightAddress, addrGainsByTxid, addrLossesByTxid)
mixin txList(visibleTxs, txCount, limit, offset, inputsByTxid, options={})
if (txCount > limit && !options.hidePagination)
.rounded-0.rounded-top.bg-content.border
.p-3
h6.fw-normal.mb-2 #{offset.toLocaleString()} - #{Math.min(offset + limit - 1, txCount - 1).toLocaleString()} of #{txCount.toLocaleString()}
+pagination(limit, offset, null, txCount, paginationBaseUrl)
//.mb-4.mb-md-3
.pb-4.pb-md-3.bg-tx-separator
each tx, txIndex in visibleTxs
- var txBlockHeight = null;
if (options.blockHeightsByTxid)
- txBlockHeight = options.blockHeightsByTxid[tx.txid];
else if (options.sharedBlockHeight != undefined)
- txBlockHeight = options.sharedBlockHeight;
- var tx_height_param = (!global.txindexAvailable && txBlockHeight) ? `@${txBlockHeight}` : '';
.border.bg-content.p-3(class=(txIndex == 0 && txCount <= limit ? "rounded-top" : false), class=(txCount <= limit && txIndex == (visibleTxs.length - 1) ? "rounded-bottom" : false))
if (tx && tx.txid)
.clearfix
.float-start
.mb-3.word-wrap
span.badge.bg-primary.fw-normal.me-2(title=`Index in Block: #${(txIndex + offset).toLocaleString()}`, data-bs-toggle="tooltip")
span.d-inline.d-sm-none.me-1 tx
| ##{(txIndex + offset).toLocaleString()}
a.d-inline.d-md-none.small(href=`./tx/${tx.txid}${tx_height_param}`) #{utils.ellipsizeMiddle(tx.txid, 24)}
a.d-none.d-md-inline.d-lg-none(href=`./tx/${tx.txid}${tx_height_param}`) #{utils.ellipsizeMiddle(tx.txid, 24)}
a.d-none.d-lg-inline(href=`./tx/${tx.txid}${tx_height_param}`) #{utils.ellipsizeMiddle(tx.txid, 48)}
if (global.specialTransactions && global.specialTransactions[tx.txid])
a.ms-2(data-bs-toggle="tooltip", title=`${coinConfig.name} Fun! There's something special about this transaction…`)
+funItemIcon
- var hasInputAddresses = false;
if (tx && tx.vin && inputsByTxid && inputsByTxid[tx.txid])
- var txInputs = inputsByTxid[tx.txid];
each txVin, txVinIndex in tx.vin
if (!txVin.coinbase)
- var vout = null;
if (txInputs[txVinIndex])
- var voutAddresses = utils.getVoutAddresses(txInputs[txVinIndex]);
if (voutAddresses.length > 0)
- hasInputAddresses = true;
if (hasInputAddresses)
.float-end
.d-block.d-sm-none
a(href="javascript:void(0)", onclick=`$(this).find(".toggle-icon").toggleClass("fa-plus-square").toggleClass("fa-minus-square"); $(".tx-input-address-${tx.txid}").toggle();`)
i.toggle-icon.far.fa-plus-square.text-muted.text-end.fa-lg
.float-start.float-sm-end.mt-n2.mt-sm-0.mb-2.mb-sm-0
.me-5.me-sm-n2
if (options.mempoolDetailsByTxid && options.mempoolDetailsByTxid[tx.txid])
- var mempoolDetails = options.mempoolDetailsByTxid[tx.txid].entry;
- var feeRate = new Decimal(mempoolDetails.fee).times(coinConfig.baseCurrencyUnit.multiplier).dividedBy(mempoolDetails.vsize);
+lightBadge
| #{parseInt(feeRate).toLocaleString()}
span.text-tiny.ms-tiny sat/vB
if (options.addrGainsByTxid && options.addrGainsByTxid[tx.txid])
+successBadge
span.me-tiny +
+valueDisplay(options.addrGainsByTxid[tx.txid])
if (options.addrLossesByTxid && options.addrLossesByTxid[tx.txid])
span.badge.bg-danger.me-2
span.me-tiny -
+valueDisplay(options.addrLossesByTxid[tx.txid])
if (options.addrGainsByTxid && options.addrLossesByTxid && !options.addrGainsByTxid[tx.txid] && !options.addrLossesByTxid[tx.txid])
span.badge.bg-danger.me-2(data-bs-toggle="tooltip", title="See transaction details for spent-from-this-address amount")
span.fw-light …
if (options.showConfirmations)
+darkBadge
if (txBlockHeight)
span(title=`${(options.currentBlockHeight - txBlockHeight + 1).toLocaleString()} confirmation${(options.currentBlockHeight - txBlockHeight > 1) ? "s" :""}`, data-bs-toggle="tooltip")
i.fas.fa-lock.fa-sm.me-1
- var confCount = options.currentBlockHeight - txBlockHeight + 1;
- var confData = utils.formatLargeNumber(confCount);
span.fw-light
if (confCount < 1000)
| #{confCount}
else
| #{parseInt(confData[0])}#{confData[1].abbreviation}
else
span(title=`Unconfirmed`, data-bs-toggle="tooltip")
i.fas.fa-lock.fa-sm.me-1
span.fw-light.text-danger 0
if (options.showDates && tx.time)
- var utcMoment = moment.utc(new Date(parseInt(tx.time) * 1000));
- var titleStr = `${utcMoment.format("Y-MM-DD HH:mm:ss")} utc`;
.d-none.d-md-inline
+darkBadge
i.far.fa-clock.fa-sm.me-1
span.fw-light(title=titleStr, data-bs-toggle="tooltip")
+timeAgo(tx.time, true)
hr.mb-4.mx-n3
.d-none.d-lg-block.border-end.mt-n4.mb-2(style="border-color: currentColor !important; height: 10px; width: 50%; opacity: 0.25;")
div
- var txInputs = inputsByTxid[tx.txid];
- var blockHeight = txBlockHeight != undefined ? txBlockHeight : -1;
- options.maxTxOutputDisplayCount = maxTxOutputDisplayCount;
if (options.highlightAddress)
- var txIOHighlightAddress = options.highlightAddress;
include ./transaction-io-details.pug
if (txIndex < (visibleTxs.length - 1) || txCount > limit)
.pb-4.pb-md-3.bg-tx-separator
if (txCount > limit && !options.hidePagination)
.rounded-0.rounded-bottom.bg-content.border
.p-3
if (!crawlerBot && txCount > limit)
h6.fw-normal.mb-2 #{offset.toLocaleString()} - #{Math.min(offset + limit - 1, txCount - 1).toLocaleString()} of #{txCount.toLocaleString()}
+pagination(limit, offset, null, txCount, paginationBaseUrl)
mixin pageTabs(names)
ul.nav.nav-tabs#page-tabs
each name, nameIndex in names
li.nav-item.page-tab(style=(nameIndex == 0 ? "margin-left: 10px;" : false))
a.nav-link(class=(nameIndex == 0 ? "active" : false), data-bs-toggle="tab", data-default-tab=(nameIndex == 0 ? "true" : false) href=`#${name.replace(" ", "-")}`, role="tab") #{name}
.bg-gradient-body-to-main.pb-4.mb-2
mixin pillTabs(names)
ul.nav.nav-pills.mb-3
each name, nameIndex in names
li.nav-item
a.nav-link(class=(nameIndex == 0 ? "active" : false), data-bs-toggle="tab", href=`#${name.replace(" ", "-")}`, role="tab") #{name}
mixin pageTab(name, defaultActive=false)
div.tab-pane(id=`${name.replace(" ", "-")}`, class=(defaultActive ? "active" : false), role="tabpanel")
block
mixin valueDisplay(val)
if (val > 0)
if (userSettings.displayCurrency == "btc")
+_valueDisplayBtc(val)
else if (userSettings.displayCurrency == "sat")
+_valueDisplaySat(val, 1000000)
else if (userSettings.displayCurrency == "local")
+_valueDisplayLocal(val, 1000000)
else
span 0
mixin _valueDisplayBtc(val)
- var parts = utils.formatCurrencyAmount(val, userSettings.displayCurrency);
//span #{JSON.stringify(parts)}
//span.d-inline.d-sm-none.fw-light.me-tiny ₿
span #{parts.val}
if (parts.lessSignificantDigits)
span.text-small.text-darken.ms-1 #{parts.lessSignificantDigits}
if (exchangeRates)
- var localCurFormatData = utils.getExchangedCurrencyFormatData(val, userSettings.localCurrency);
span.xs-hidden.d-none.d-sm-inline.text-tiny.text-tight-spacing.border-dotted.fw-light.ms-1(data-bs-toggle="tooltip", title=`<span class="me-tiny">${localCurFormatData.symbol}</span><span>${localCurFormatData.value}</span>`, data-bs-html="true") #{parts.currencyUnit}
else
span.xs-hidden.d-none.d-sm-inline.text-tiny.text-tight-spacing.fw-light.ms-1 #{parts.currencyUnit}
mixin _valueDisplaySat(val, summarizeMin=Infinity, summarizeDecimals=3)
- var parts = utils.formatCurrencyAmount(val, localCurrencyDisplay ? userSettings.localCurrency : userSettings.displayCurrency);
//span #{JSON.stringify(parts)}
if (parts.intVal >= summarizeMin)
//span #{JSON.stringify(parts)}
- var largeNumberData = utils.formatLargeNumberSignificant(parts.intVal, summarizeDecimals >= 0 ? summarizeDecimals : 3);
//span #{JSON.stringify(largeNumberData)} #{parts.intVal}
span(title=parts.intVal.toLocaleString(), data-bs-toggle="tooltip") #{largeNumberData[0]}
span.ms-1 #{largeNumberData[1].textDesc}
else
span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{parts.intVal.toLocaleString()}
if (exchangeRates)
- var localCurFormatData = utils.getExchangedCurrencyFormatData(val, userSettings.localCurrency);
span.xs-hidden.d-none.d-sm-inline.text-tiny.text-tight-spacing.border-dotted.fw-light.ms-1(data-bs-toggle="tooltip", title=`<span class="me-tiny">${localCurFormatData.symbol}</span><span>${localCurFormatData.value}</span>`, data-bs-html="true") #{parts.currencyUnit}
else
span.xs-hidden.d-none.d-sm-inline.text-tiny.text-tight-spacing.fw-light.ms-1 #{parts.currencyUnit}
mixin _valueDisplayLocal(val, summarizeMin=Infinity, summarizeDecimals=3)
- var localCurrencyDisplay = userSettings.displayCurrency == "local";
- var parts = utils.formatCurrencyAmount(val, userSettings.localCurrency);
//span #{JSON.stringify(parts)}
span.fw-light.me-tiny #{global.currencySymbols[userSettings.localCurrency]}
if (parts.intVal >= summarizeMin)
- var largeNumberData = utils.formatLargeNumberSignificant(parts.intVal, summarizeDecimals >= 0 ? summarizeDecimals : 3);
//span #{JSON.stringify(largeNumberData)} #{parts.intVal}
span.border-dotted(title=`${global.currencySymbols[userSettings.localCurrency]}${parts.intVal.toLocaleString()}<br/>${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC`, data-bs-toggle="tooltip", data-bs-html="true")
span #{largeNumberData[0]}
span.ms-1 #{largeNumberData[1].textDesc}
else
span.border-dotted(title=`${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC`, data-bs-toggle="tooltip") #{parts.val}
mixin valueDisplaySpecial(val, maxDecimals=-1, summarizeAlways=false, summarizeMin=Infinity, summarizeDecimals=-1)
if (maxDecimals == -1 && summarizeAlways == false && summarizeMin == undefined)
+valueDisplay(val)
else
if (val > 0)
- var localCurrencyDisplay = userSettings.displayCurrency == "local";
if (maxDecimals > 0)
- var parts = utils.formatCurrencyAmountWithForcedDecimalPlaces(val, localCurrencyDisplay ? userSettings.localCurrency : userSettings.displayCurrency, maxDecimals);
else
- var parts = utils.formatCurrencyAmount(val, localCurrencyDisplay ? userSettings.localCurrency : userSettings.displayCurrency);
//span #{JSON.stringify(parts)}
if (localCurrencyDisplay)
span.me-1 #{global.currencySymbols[userSettings.localCurrency]}
//| #{JSON.stringify(parts)}
if (summarizeAlways || parts.intVal >= summarizeMin)
//span #{JSON.stringify(parts)}
- var largeNumberData = utils.formatLargeNumberSignificant(parts.intVal, summarizeDecimals >= 0 ? summarizeDecimals : 2);
//span #{JSON.stringify(largeNumberData)} #{parts.intVal}
span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{largeNumberData[0]} #{largeNumberData[1].textDesc}
else if (maxDecimals == 0)
span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{parts.intVal.toLocaleString()}
else if (maxDecimals > 0)
span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{parts.val}
else
span(class=(localCurrencyDisplay ? "border-dotted" : false), title=(localCurrencyDisplay ? `${utils.formatCurrencyAmount(val, coinConfig.defaultCurrencyUnit.name.toLowerCase()).simpleVal} BTC` : false), data-bs-toggle=(localCurrencyDisplay ? "tooltip" : false)) #{parts.val}
if (parts.lessSignificantDigits)
span.text-small.text-muted.ms-1 #{parts.lessSignificantDigits}
if (userSettings.displayCurrency != "local")
if (exchangeRates)
- var localCurFormatData = utils.getExchangedCurrencyFormatData(val, userSettings.localCurrency);
span.text-tiny.text-tight-spacing.border-dotted.fw-light.ms-1(data-bs-toggle="tooltip", title=`<span class="me-tiny">${localCurFormatData.symbol}</span><span>${localCurFormatData.value}</span>`, data-bs-html="true") #{parts.currencyUnit}
else
span.text-tiny.text-tight-spacing.fw-light.ms-1 #{parts.currencyUnit}
else if (userSettings.displayCurrency == "local")
else
span 0
mixin confirmations(confirmations, abbreviated=true)
if (confirmations < 6)
span.text-warning.border-dotted(title="Fewer than 6 confirmations is generally considered <b>unsettled</b> for high-value transactions because block <b>re-organizations</b> that could affect them have reasonable probability.", data-bs-toggle="tooltip", data-bs-html="true") #{confirmations.toLocaleString()}
i.fas.fa-unlock-alt.fa-sm.ms-1
else
span.border-dotted.text-success(title="6 confirmations is generally considered <b>settled</b>. High-value transactions may require more;<br/>Low-value transactions may require fewer.", data-bs-toggle="tooltip", data-bs-html="true")
| #{abbreviated && confirmations > 1000 ? `${Math.floor(confirmations/1000)}k` : confirmations.toLocaleString()}
i.fas.fa-check.ms-1
mixin cardHighlightBadge(text)
span.small.badge.bg-card-highlight-badge.border.border-card-highlight-badge.text-reset.mb-tiny.me-1 #{text ? text : ""}
if (block)
block
mixin hexDataDisplay(hex, order="utf8,ascii,hex")
- var items = order.split(",");
- var uniqueId = utils.getRandomString(12, "aA");
.d-inline-block.small.border.rounded-1.p-1.px-2.card-highlight.word-wrap
+cardHighlightBadge(items[0])
span.small #{utils.formatHex(hex, items[0])}
if (items[0] == "hex")
.d-none.d-md-inline
+copyTextButton(hex)
div(id=`hex-display-expanded-${uniqueId}`, style="display: none;")
each item, itemIndex in items
if (itemIndex > 0)
if (itemIndex > 1)
br
+cardHighlightBadge(item)
span.small #{utils.formatHex(hex, item)}
if (item == "hex")
.d-inline.d-md-inline
+copyTextButton(hex)
a.text-muted.ms-2(href="javascript:void(0)", onclick=`$('#hex-display-expanded-${uniqueId}').toggle(); $(this).find(".toggle-plus-minus").toggleClass("fa-plus-square"); $(this).find(".toggle-plus-minus").toggleClass("fa-minus-square"); return false;`, title="Toggle More Encodings")
i.toggle-plus-minus.far.fa-plus-square.text-card-highlight
mixin timestamp(timestamp, includeAgo=true, formatString)
- var tzDiff = -parseFloat(browserTzOffset);
if (userTzOffset != "unset")
- tzDiff = parseFloat(userTzOffset);
- var tzMoment = moment.utc(new Date(parseInt(timestamp) * 1000)).add(tzDiff, "hours");
- var utcMoment = moment.utc(new Date(parseInt(timestamp) * 1000));
- var momentObj = (uiTimezone == "utc" ? utcMoment : tzMoment);
- var nowMoment = (uiTimezone == "utc" ? moment.utc(new Date()) : moment.utc(new Date()).add(tzDiff, "hours"));
- var yearStr = momentObj.format("Y");
- var nowYearStr = nowMoment.format("Y");
- var dateStr = momentObj.format("Y-M-D");
- var nowDateStr = nowMoment.format("Y-M-D");
if (tzDiff != 0 && uiTimezone == "local")
- var titleStr = `${tzMoment.format("Y-MM-DD, h:mm:ss A")}<br/>(${utcMoment.format("Y-MM-DD, HH:mm:ss")} UTC)`;
else
- var titleStr = `${utcMoment.format("Y-MM-DD, HH:mm:ss")} utc`;
if (formatString)
span.border-dotted(title=titleStr, data-bs-toggle="tooltip", data-bs-html="true")
span.date-text(id=`xyz-${utils.getRandomString(10, "aA1")}`, data-timestamp-utc=`${parseInt(timestamp)}`, data-date-format=formatString, data-date-ago=includeAgo.toString()) #{momentObj.format(formatString)}
if (["H", "m", "s"].some(str => formatString.includes(str)))
span.text-tiny.text-tight-spacing.text-muted.ms-tiny UTC
else if (dateStr == nowDateStr)
span.border-dotted(title=titleStr, data-bs-toggle="tooltip", data-bs-html="true")
if (tzDiff != 0 && uiTimezone == "local")
span.date-text(id=`xyz-${utils.getRandomString(10, "aA1")}`, data-timestamp-utc=`${parseInt(timestamp)}`, data-date-ago=includeAgo.toString()) #{momentObj.format("h:mm")}
span.text-tiny.text-tight-spacing.fw-light.ms-tiny #{momentObj.format("A")}
else
span #{momentObj.format("HH:mm")}
span.text-tiny.text-tight-spacing.fw-light.ms-tiny UTC
else if (yearStr == nowYearStr)
span.border-dotted(title=titleStr, data-bs-toggle="tooltip", data-bs-html="true")
if (tzDiff != 0 && uiTimezone == "local")
span.date-text(id=`xyz-${utils.getRandomString(10, "aA1")}`, data-timestamp-utc=`${parseInt(timestamp)}`, data-date-ago=includeAgo.toString()) #{momentObj.format("M/D, h:mm")}
span.text-tiny.text-tight-spacing.fw-light.ms-tiny #{momentObj.format("A")}
else
span #{momentObj.format("M/D, HH:mm")}
span.text-tiny.text-tight-spacing.fw-light.ms-tiny UTC
else
span.border-dotted(title=titleStr, data-bs-toggle="tooltip", data-bs-html="true")
if (tzDiff != 0 && uiTimezone == "local")
span.date-text(id=`xyz-${utils.getRandomString(10, "aA1")}`, data-timestamp-utc=`${parseInt(timestamp)}`, data-date-ago=includeAgo.toString()) #{momentObj.format("YYYY-MM-DD")}
else
span #{momentObj.format("YYYY-MM-DD")}
if (includeAgo)
- var timeAgo = moment.duration(moment.utc(new Date()).diff(moment.utc(new Date(parseInt(timestamp) * 1000))));
if (timeAgo.asSeconds() < 0)
- timeAgo = moment.duration(moment.utc(new Date(parseInt(timestamp) * 1000)).diff(moment.utc(new Date())));
small.text-muted.ms-2 (+
| #{timeAgo.format()}
span )
else
small.text-muted.ms-2 (
+timeAgo(timestamp, true)
span ago)
mixin darkBadge(text)
span.badge.bg-dark.border.border-dark.me-2 #{text ? text : ""}
if (block)
block
mixin lightBadge(text)
span.badge.bg-light.text-dark.border.me-2 #{text ? text : ""}
if (block)
block
mixin successBadge(text)
span.badge.bg-success.me-2 #{text ? text : ""}
if (block)
block
mixin primaryBadge(text, endMargin=true)
span.badge.bg-primary(class=(endMargin ? "me-2" : false)) #{text ? text : ""}
if (block)
block
mixin funAlert(html, text, referenceUrl)
.alert.alert-primary.shadow-sm.pb-0(role="alert", style="padding-left: 4rem;")
i.fas.fa-flag.fa-2x.fontawesome-shadow.text-warning.d-inline-block(style="position: absolute; top: 0; left: 0; width: 40px; margin-left: 0.8rem; margin-top: 1.2rem;")
h5.mb-2.fw-light #{coinConfig.name} Fun
if (html)
p
span !{html}
if (referenceUrl && referenceUrl.trim().length > 0 && html.indexOf(referenceUrl) == -1)
span &nbsp;
a(href=referenceUrl) Read more
else if (text)
p
span #{text}
if (referenceUrl && referenceUrl.trim().length > 0)
span &nbsp;
a(href=referenceUrl) Read more
else if (block)
p
block
mixin funItemIcon
i.fas.fa-flag.text-warning
mixin timeAgo(timeAgoTime, only1Element=false)
- var timeAgo = moment.duration(moment.utc(new Date()).diff(moment.utc(new Date(parseInt(timeAgoTime) * 1000))));
if (timeAgo.asHours() < 1)
if (timeAgo.asMinutes() < 1)
span #{timeAgo.seconds()}s
else
span #{timeAgo.minutes()}m
else
if (timeAgo.asHours() >= 1 && timeAgo.asHours() < 24)
span #{timeAgo.hours()}h
if (!only1Element && timeAgo.minutes() > 0)
span.ms-1 #{timeAgo.minutes()}m
else
if (only1Element)
span #{utils.shortenTimeDiff(timeAgo.format()).split(/,?\s+/)[0]}
else
span #{utils.shortenTimeDiff(timeAgo.format())}
mixin blockCountdown(title, blocksLeft)
- var countdownId = utils.getRandomString(10, "aA");
.alert.alert-success
.d-flex.justify-content-between
div
h5.mb-0
span.me-2 #{title}:
span.lead(id=`countdown-time-${countdownId}`)
| #{blocksLeft.toLocaleString()} block#{blocksLeft > 1 ? "s" : ""}
- var secondsToGo = 10 * 60 * blocksLeft;
- var minutesToGo = secondsToGo / 60;
- var hoursToGo = minutesToGo / 60;
- var daysToGo = hoursToGo / 24;
span.ms-1 (
span ~
if (daysToGo > 2)
| #{parseInt(daysToGo)} days
- var hours = 24 * (daysToGo - Math.floor(daysToGo));
if (hours > 1)
span.ms-1 #{parseInt(hours)} hour#{parseInt(hours) > 1 ? "s" :""}
else if (daysToGo > 1)
| 1 day
- var hours = 24 * (daysToGo - Math.floor(daysToGo));
if (hours > 1)
span.ms-1 #{parseInt(hours)} hour#{parseInt(hours) > 1 ? "s" :""}
else if (hoursToGo > 2)
| #{parseInt(hoursToGo)} hours
- var minutes = 60 * (hoursToGo - Math.floor(hoursToGo));
if (minutes > 1)
span.ms-1 #{parseInt(minutes)} minute#{parseInt(minutes) > 1 ? "s" :""}
else if (hoursToGo >= 1)
| 1 hour
- var minutes = 60 * (hoursToGo - Math.floor(hoursToGo));
if (minutes > 1)
span.ms-1 #{parseInt(minutes)} minute#{parseInt(minutes) > 1 ? "s" :""}
else
- var minutes = 60 * (hoursToGo - Math.floor(hoursToGo));
span #{parseInt(minutes)} minute#{parseInt(minutes) > 1 ? "s" :""}
span )
if (block)
a.text-body(href="javascript:void(0);", onclick=`$("#countdown-block-${countdownId}").toggle(); $(".countdown-togglers-${countdownId}").toggle();`)
i.far.fa-plus-square(class=`countdown-togglers-${countdownId}`)
i.far.fa-minus-square(class=`countdown-togglers-${countdownId}`, style="display: none;")
if (block)
div(id=`countdown-block-${countdownId}`, style="display: none;")
.mt-2
block
mixin copyTextButton(text)
small.ms-2
if (false)
a(href="javascript:void(0)", title="Copy", data-clipboard-text=text, data-bs-toggle="tooltip", onclick=`copyTextToClipboard("${text}"); $(".icon-copy").toggle(); $(this).find(".icon-copied").toggle(); setTimeout(() => { $(this).find(".icon-copy").toggle(); $(this).find(".icon-copied").toggle(); }, 2000); return false;`)
i.far.fa-copy.text-info.icon-copy
i.fas.fa-check.text-success.icon-copied(style="display: none;")
a(href="javascript:void(0)", title="Copy", data-clipboard-text=text, data-bs-toggle="tooltip", onclick=`copyTextToClipboard("${text}"); $(this).attr("title", "Copied!").tooltip("_fixTitle").tooltip("show"); $(this).mouseleave(function() { $(this).tooltip("hide"); $(this).attr("data-bs-original-title", "Copy"); });`)
i.far.fa-copy.text-info
mixin dismissableInfoAlert(userSettingDismissedName, textToShowWhenClosed)
- var hiddenByEnvVar = (config.site.hideInfoNotes == "true");
if (!hiddenByEnvVar && (!userSettings.hideInfoNotes || userSettings.hideInfoNotes == "false"))
if (userSettings[userSettingDismissedName] != "true")
.alert.alert-primary.alert-dismissible.shadow-sm.mb-4.ps-3.ps-md-7(role="alert")
i.d-none.d-md-inline.fas.fa-info-circle.fa-lg.d-inline-block(style="position: absolute; top: 0; left: 0; width: 36px; margin-left: 0.4rem; margin-top: 1.3rem;")
block
a.btn-close(href=`./changeSetting?name=${userSettingDismissedName}&value=true`, aria-label="Close", style="text-decoration: none;", title="Hide this note", data-bs-toggle="tooltip")
//button.btn-close.text-muted(type="button", data-bs-dismiss="alert", aria-label="Close", title="Hide this note", data-bs-toggle="tooltip")
else if (textToShowWhenClosed)
.alert.alert-primary.shadow-sm.mb-4(role="alert")
.clearfix
.float-start
i.fas.fa-info-circle.fa-lg.me-3
| #{textToShowWhenClosed}
.float-end
a.text-muted(href=`./changeSetting?name=${userSettingDismissedName}&value=false`, aria-label="Close", style="text-decoration: none;", title="Open this note", data-bs-toggle="tooltip")
i.far.fa-plus-square.fa-lg
mixin dismissableAlert(userSettingDismissedName, textToShowWhenClosed)
if (!userSettings.hideInfoNotes || userSettings.hideInfoNotes == "false")
if (userSettings[userSettingDismissedName] != "true")
.alert.alert-primary.alert-dismissible.shadow-sm.mb-4(role="alert")
block
a.btn-close(href=`./changeSetting?name=${userSettingDismissedName}&value=true`, aria-label="Close", style="text-decoration: none;", title="Hide this note", data-bs-toggle="tooltip")
//button.btn-close.text-muted(type="button", data-bs-dismiss="alert", aria-label="Close", title="Hide this note", data-bs-toggle="tooltip")
else if (textToShowWhenClosed)
.alert.alert-primary.shadow-sm.mb-4(role="alert")
.clearfix
.float-start
i.fas.fa-info-circle.fa-lg.me-3
| #{textToShowWhenClosed}
.float-end
a.text-muted(href=`./changeSetting?name=${userSettingDismissedName}&value=false`, aria-label="Close", style="text-decoration: none;", title="Open this note", data-bs-toggle="tooltip")
i.far.fa-plus-square.fa-lg
mixin warningAlert
.alert.alert-warning.shadow-sm.mb-4.ps-3.ps-md-7(role="alert")
i.d-none.d-md-inline.fas.fa-exclamation-triangle.fa-lg.d-inline-block(style="position: absolute; top: 0; left: 0; width: 36px; margin-left: 0.4rem; margin-top: 1.3rem;")
block
mixin numWithMutedDecimals(numString)
if (numString.indexOf(".") >= 0)
- var n = numString.substring(0, numString.indexOf("."));
- var dec = numString.substring(numString.indexOf("."));
span #{n}
small.text-muted #{dec}
else
span #{numString}
mixin blockRangeFilters
.clearfix
.float-start.me-4
div(id="time-range-buttons")
div.mb-2
span.border-dotted.text-uppercase.fw-light.text-card-highlight(title="Analyze blocks over the last N days (approximated using average block time).", data-bs-toggle="tooltip") Time Range
div.btn-group
a.btn.btn-primary.block-count-btn(href="javascript:void(0)", data-blockCount="144") 1 day
a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="432") 3 day
a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="1008") 7 day
a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="4320") 30 day
.float-start.me-4
div(id="block-selections-buttons")
div.mb-2
span.border-dotted.text-uppercase.fw-light.text-card-highlight(title="Choose from a list of pre-configured 'interesting' historical block ranges.", data-bs-toggle="tooltip") Interesting History
div.dropdown
button.btn.btn-outline-primary.dropdown-toggle(type="button", id="preconfigured-dropdown", data-bs-toggle="dropdown", aria-haspopup="true", aria-expanded="false") Selections
div.dropdown-menu(aria-labelledby="preconfigured-dropdown")
a.dropdown-item(href="javascript:void(0)", data-blocks="0-199") First 200 Blocks
a.dropdown-item(href="javascript:void(0)", data-blocks="209900-210100") First Halving ±100
a.dropdown-item(href="javascript:void(0)", data-blocks="419900-420100") Second Halving ±100
a.dropdown-item(href="javascript:void(0)", data-blocks="629900-630100") Third Halving ±100
a.dropdown-item(href="javascript:void(0)", data-blocks="481724-481924") SegWit Activation ±100
.float-start
div(id="time-range-buttons")
div.mb-2
span.border-dotted.text-uppercase.fw-light.text-card-highlight(title="Manually enter a range of blocks to analyze.", data-bs-toggle="tooltip") Custom Block Range
form.form-inline.me-3(id="custom-range-form")
div.input-group
input.form-control(type="text", id="custom-range-start", placeholder="min height", style="width: 125px;")
input.form-control(type="text", id="custom-range-end", placeholder="max height", style="width: 125px;")
button.btn.btn-primary(type="submit") Go
mixin pagination(limit, offset, sort, itemCount, baseUrl, justification="start", large=false)
- var pageNumber = offset / limit + 1;
- var pageCount = Math.floor(itemCount / limit);
- if (pageCount * limit < itemCount) {
- pageCount++;
- }
- var paginationUrlFunction = function(x) {
- return `${baseUrl}?limit=${limit}&offset=${(x - 1) * limit}${sort ? ("&sort=" + sort) : ""}`
//- return baseUrl + "?limit=" + limit + "&offset=" + ((x - 1) * limit);
//- return baseUrl + "?limit=" + limit + "&offset=" + ((x - 1) * limit + "&sort=" + sort);
- }
- var pageNumbers = [];
- for (var x = 1; x <= pageCount; x++) {
- pageNumbers.push(x);
- }
nav(aria-label="Page navigation")
ul.pagination.flex-wrap.mb-0(class=`justify-content-${justification}`, class=(large ? "pagination-lg" : false))
li.page-item(class=(pageNumber == 1 ? "disabled" : false))
a.page-link(href=(pageNumber == 1 ? "javascript:void(0)" : paginationUrlFunction(pageNumber - 1)), aria-label="Previous")
span(aria-hidden="true") &laquo;
each x, xIndex in pageNumbers
if (x >= (pageNumber - 4) && x <= (pageNumber + 4) || xIndex == 0 || xIndex == (pageNumbers.length - 1))
li.page-item(class=(x == pageNumber ? "active" : false))
a.page-link(href=(paginationUrlFunction(x))) #{x}
if (x == 1 && pageNumber > 6)
li.page-item.disabled
a.page-link(href="javascript:void(0)") ...
else if (x == (pageCount - 1) && pageNumber < (pageCount - 5))
li.page-item.disabled
a.page-link(href="javascript:void(0)") ...
li.page-item(class=(pageNumber == pageCount ? "disabled" : false))
a.page-link(href=(pageNumber == pageCount ? "javascript:void(0)" : paginationUrlFunction(pageNumber + 1)), aria-label="Next")
span(aria-hidden="true") &raquo;
mixin graphPageScriptSetup(dateAdapterNeeded=false)
script(src=`./js/chart.min.js?v=${cacheId}`, integrity="sha384-ovAl4wbIXnEbY76lSb6GrprFBkoYeu4RKYYNMsADThIn2AweWPOyJqoyR/8/kgML")
if (dateAdapterNeeded)
script(src=`./js/moment.min.js?v=${cacheId}`, integrity="sha384-CJyhAlbbRZX14Q8KxKBt0na1ad4KBs9PklAiNk2Efxs9sgimbIZm9kYLJQeNMUfM")
script(src=`./js/chartjs-adapter-moment.min.js?v=${cacheId}`, integrity="sha384-Z9r2EsEmivx0l8T8TvYoqqGcpO0cCjKbqVXB8tYUa0hIWKtGVl0TmaF263CjS6XR")
if (userSettings.uiTheme && userSettings.uiTheme == "dark")
script.
Chart.defaults.color='white';
var gridLineColor = "#1a4280";
else
script.
Chart.defaults.color='black';
var gridLineColor = "#dddddd";
mixin quote(quote, quoteIndex=-1)
blockquote.blockquote
span.fs-4.font-serif "
if (quoteIndex >= 0)
a.fs-5.font-serif.text-reset(href=`./quote/${quoteIndex}`) #{quote.text}
else
span.fs-5.font-serif #{quote.text}
span.fs-4.font-serif "
br
.ms-8.mt-2
if (quote.speaker.indexOf("https://twitter.com") >= 0)
- var account = quote.speaker.substring("https://twitter.com/".length);
| —
a.fs-5(href=quote.speaker, target="_blank") @#{account}
else
span.fs-5 — #{quote.speaker}
if (quote.date)
small.fw-light , #{quote.date}
if (quote.url)
small.text-muted.ms-2 (
a(href=quote.url, target="_blank") ref
i.fas.fa-external-link-alt.fa-sm.ms-1
| )
if (quote.context)
div
i.fs-6.text-muted.ms-2 (#{quote.context})