@@ -20,9 +20,12 @@ const windowScales = ['ticket-price', 'pow-difficulty', 'missed-votes']
2020const lineScales = [ 'ticket-price' ]
2121// index 0 represents y1 and 1 represents y2 axes.
2222const yValueRanges = { 'ticket-price' : [ 1 ] }
23+ var chainworkUnits = [ 'exahash' , 'zettahash' , 'yottahash' ]
24+ var hashrateUnits = [ 'Th/s' , 'Ph/s' , 'Eh/s' ]
2325var ticketPoolSizeTarget , premine , stakeValHeight , stakeShare
2426var baseSubsidy , subsidyInterval , subsidyExponent , windowSize , avgBlockTime
2527var rawCoinSupply , rawPoolValue
28+ var yFormatter , legendEntry , legendMarker , legendElement
2629
2730function usesWindowUnits ( chart ) {
2831 return windowScales . indexOf ( chart ) > - 1
@@ -57,20 +60,9 @@ function axesToRestoreYRange (chartName, origYRange, newYRange) {
5760 return axes
5861}
5962
60- function formatHashRate ( value , displayType ) {
61- value = parseInt ( value )
62- if ( value <= 0 ) return value
63- var shortUnits = [ 'Th' , 'Ph' , 'Eh' ]
64- var labelUnits = [ 'terahash/s' , 'petahash/s' , 'exahash/s' ]
65- for ( var i = 0 ; i < labelUnits . length ; i ++ ) {
66- var quo = Math . pow ( 1000 , i )
67- var max = Math . pow ( 1000 , i + 1 )
68- if ( ( value > quo && value <= max ) || i + 1 === labelUnits . length ) {
69- var data = intComma ( Math . floor ( value / quo ) )
70- if ( displayType === 'axis' ) return data + '' + shortUnits [ i ]
71- return data + ' ' + labelUnits [ i ]
72- }
73- }
63+ function withBigUnits ( v , units ) {
64+ var i = v === 0 ? 0 : Math . floor ( Math . log10 ( v ) / 3 )
65+ return ( v / Math . pow ( 1000 , i ) ) . toFixed ( 3 ) + ' ' + units [ i ]
7466}
7567
7668function blockReward ( height ) {
@@ -80,74 +72,30 @@ function blockReward (height) {
8072 return 0
8173}
8274
83- function legendFormatter ( data ) {
84- var html = ''
85- if ( data . x == null ) {
86- let dashLabels = data . series . reduce ( ( nodes , series ) => {
87- return `${ nodes } <div class="pr-2">${ series . dashHTML } ${ series . labelHTML } </div>`
88- } , '' )
89- html = `<div class="d-flex flex-wrap justify-content-center align-items-center">
90- <div class="pr-3">${ this . getLabels ( ) [ 0 ] } : N/A</div>
91- <div class="d-flex flex-wrap">${ dashLabels } </div>
92- </div>`
93- } else {
94- var i = data . dygraph . getOption ( 'legendIndex' )
95- var extraHTML = ''
96- // The circulation chart has an additional legend entry showing percent
97- // difference.
98- if ( data . series . length === 2 && data . series [ 0 ] . label . toLowerCase ( ) . includes ( 'coin supply' ) ) {
99- let inflation = data . dygraph . getOption ( 'inflation' )
100- if ( i < inflation . length ) {
101- let actual = data . series [ 0 ] . y
102- let predicted = inflation [ i ]
103- let unminted = predicted - actual
104- let change = ( ( unminted / predicted ) * 100 ) . toFixed ( 2 )
105- extraHTML = `<div class="pr-2"> Unminted: ${ intComma ( unminted ) } DCR (${ change } %)</div>`
106- }
107- }
108-
109- let yVals = data . series . reduce ( ( nodes , series ) => {
110- if ( ! series . isVisible ) return nodes
111- let yVal = series . yHTML
112- switch ( series . label . toLowerCase ( ) ) {
113- case 'ticket pool value' :
114- case 'inflation limit' :
115- case 'coin supply' :
116- yVal = intComma ( series . y ) + ' DCR'
117- break
118-
119- case 'total fee' :
120- case 'ticket price' :
121- yVal = series . y + ' DCR'
122- break
123-
124- case 'hashrate' :
125- yVal = formatHashRate ( series . y )
126- break
127-
128- case 'stake participation' :
129- yVal = series . y . toFixed ( 4 ) + '%'
130- break
131- }
132- let result = `${ nodes } <div class="pr-2">${ series . dashHTML } ${ series . labelHTML } : ${ yVal } </div>`
75+ function addLegendEntryFmt ( div , series , fmt ) {
76+ div . appendChild ( legendEntry ( `${ series . dashHTML } ${ series . labelHTML } : ${ fmt ( series . y ) } ` ) )
77+ }
13378
134- if ( series . label . toLowerCase ( ) === 'stake participation' && rawCoinSupply . length === rawPoolValue . length &&
135- rawPoolValue . length !== 0 && i !== null ) {
136- result += `<div class="pr-2"><div class="dygraph-legend-line"></div> Ticket Pool Value: ${ intComma ( rawPoolValue [ i ] ) } DCR</div>
137- <div class="pr-2"><div class="dygraph-legend-line"></div> Coin Supply: ${ intComma ( rawCoinSupply [ i ] ) } DCR</div>`
138- }
79+ function addLegendEntry ( div , series ) {
80+ div . appendChild ( legendEntry ( `${ series . dashHTML } ${ series . labelHTML } : ${ series . yHTML } ` ) )
81+ }
13982
140- return result
141- } , '' )
83+ function defaultYFormatter ( div , data ) {
84+ addLegendEntry ( div , data . series [ 0 ] )
85+ }
14286
143- html = `<div class="d-flex flex-wrap justify-content-center align-items-center">
144- <div class="pr-3">${ this . getLabels ( ) [ 0 ] } : ${ data . xHTML } </div>
145- <div class="d-flex flex-wrap"> ${ yVals } </div>
146- </div>${ extraHTML } `
147- }
87+ function customYFormatter ( fmt ) {
88+ return ( div , data ) => addLegendEntryFmt ( div , data . series [ 0 ] , fmt )
89+ }
14890
149- dompurify . sanitize ( html , { FORBID_TAGS : [ 'svg' , 'math' ] } )
150- return html
91+ function legendFormatter ( data ) {
92+ if ( data . x == null ) return legendElement . classList . add ( 'd-hide' )
93+ legendElement . classList . remove ( 'd-hide' )
94+ var div = document . createElement ( 'div' )
95+ div . appendChild ( legendEntry ( `${ data . dygraph . getLabels ( ) [ 0 ] } : ${ data . xHTML } ` ) )
96+ yFormatter ( div , data , data . dygraph . getOption ( 'legendIndex' ) )
97+ dompurify . sanitize ( div , { IN_PLACE : true , FORBID_TAGS : [ 'svg' , 'math' ] } )
98+ return div . innerHTML
15199}
152100
153101function nightModeOptions ( nightModeOn ) {
@@ -349,7 +297,9 @@ export default class extends Controller {
349297 'ticketsPurchase' ,
350298 'ticketsPrice' ,
351299 'vSelector' ,
352- 'binSize'
300+ 'binSize' ,
301+ 'legendEntry' ,
302+ 'legendMarker'
353303 ]
354304 }
355305
@@ -364,6 +314,25 @@ export default class extends Controller {
364314 subsidyExponent = parseFloat ( this . data . get ( 'mulSubsidy' ) ) / parseFloat ( this . data . get ( 'divSubsidy' ) )
365315 windowSize = parseInt ( this . data . get ( 'windowSize' ) )
366316 avgBlockTime = parseInt ( this . data . get ( 'blockTime' ) ) * 1000
317+ legendElement = this . labelsTarget
318+
319+ // Prepare the legend element generators.
320+ var lm = this . legendMarkerTarget
321+ lm . remove ( )
322+ lm . removeAttribute ( 'data-target' )
323+ legendMarker = ( ) => {
324+ let node = document . createElement ( 'div' )
325+ node . appendChild ( lm . cloneNode ( ) )
326+ return node . innerHTML
327+ }
328+ var le = this . legendEntryTarget
329+ le . remove ( )
330+ le . removeAttribute ( 'data-target' )
331+ legendEntry = s => {
332+ let node = le . cloneNode ( )
333+ node . innerHTML = s
334+ return node
335+ }
367336
368337 this . settings = TurboQuery . nullTemplate ( [ 'chart' , 'zoom' , 'scale' , 'bin' , 'axis' ] )
369338 this . query . update ( this . settings )
@@ -405,7 +374,7 @@ export default class extends Controller {
405374 pointSize : 0.25 ,
406375 legend : 'always' ,
407376 labelsSeparateLines : true ,
408- labelsDiv : this . labelsTarget ,
377+ labelsDiv : legendElement ,
409378 legendFormatter : legendFormatter ,
410379 highlightCircleSize : 4 ,
411380 ylabel : 'Ticket Price' ,
@@ -449,6 +418,7 @@ export default class extends Controller {
449418 }
450419 rawPoolValue = [ ]
451420 rawCoinSupply = [ ]
421+ yFormatter = defaultYFormatter
452422 var xlabel = data . t ? 'Date' : 'Block Height'
453423
454424 switch ( chartName ) {
@@ -465,6 +435,7 @@ export default class extends Controller {
465435 valueRange : [ 0 , windowSize * 20 * 8 ] ,
466436 axisLabelFormatter : ( y ) => Math . round ( y )
467437 }
438+ yFormatter = customYFormatter ( y => y . toFixed ( 8 ) + ' DCR' )
468439 break
469440
470441 case 'ticket-pool-size' : // pool size graph
@@ -479,18 +450,25 @@ export default class extends Controller {
479450 color : '#888'
480451 }
481452 }
453+ yFormatter = customYFormatter ( y => `${ intComma ( y ) } tickets (network target ${ intComma ( ticketPoolSizeTarget ) } )` )
482454 break
483455
484456 case 'stake-participation' :
485457 d = percentStakedFunc ( data )
486458 assign ( gOptions , mapDygraphOptions ( d , [ xlabel , 'Stake Participation' ] , true ,
487459 'Stake Participation (%)' , true , false ) )
460+ yFormatter = ( div , data , i ) => {
461+ addLegendEntryFmt ( div , data . series [ 0 ] , y => y . toFixed ( 4 ) + '%' )
462+ div . appendChild ( legendEntry ( `${ legendMarker ( ) } Ticket Pool Value: ${ intComma ( rawPoolValue [ i ] ) } DCR` ) )
463+ div . appendChild ( legendEntry ( `${ legendMarker ( ) } Coin Supply: ${ intComma ( rawCoinSupply [ i ] ) } DCR` ) )
464+ }
488465 break
489466
490467 case 'ticket-pool-value' : // pool value graph
491468 d = zip2D ( data , data . poolval , atomsToDCR )
492469 assign ( gOptions , mapDygraphOptions ( d , [ xlabel , 'Ticket Pool Value' ] , true ,
493470 'Ticket Pool Value (DCR)' , true , false ) )
471+ yFormatter = customYFormatter ( y => intComma ( y ) + ' DCR' )
494472 break
495473
496474 case 'block-size' : // block size graph
@@ -527,6 +505,16 @@ export default class extends Controller {
527505 }
528506 }
529507 gOptions . inflation = d . inflation
508+ yFormatter = ( div , data , i ) => {
509+ addLegendEntryFmt ( div , data . series [ 0 ] , y => intComma ( y ) + ' DCR' )
510+ var change = 0
511+ if ( i < d . inflation . length ) {
512+ let predicted = d . inflation [ i ]
513+ let unminted = predicted - data . series [ 0 ] . y
514+ change = ( ( unminted / predicted ) * 100 ) . toFixed ( 2 )
515+ div . appendChild ( legendEntry ( `${ legendMarker ( ) } Unminted: ${ intComma ( unminted ) } DCR (${ change } %)` ) )
516+ }
517+ }
530518 break
531519
532520 case 'fees' : // block fee graph
@@ -544,12 +532,14 @@ export default class extends Controller {
544532 d = zip2D ( data , data . work )
545533 assign ( gOptions , mapDygraphOptions ( d , [ xlabel , 'Cumulative Chainwork (exahash)' ] ,
546534 false , 'Cumulative Chainwork (exahash)' , true , false ) )
535+ yFormatter = customYFormatter ( y => withBigUnits ( y , chainworkUnits ) )
547536 break
548537
549538 case 'hashrate' : // Total chainwork over time
550- d = zip2D ( data , data . rate , 1 , data . offset )
551- assign ( gOptions , mapDygraphOptions ( d , [ xlabel , 'Network Hashrate (terahash/s)' ] ,
552- false , 'Network Hashrate (terahash/s)' , true , false ) )
539+ d = zip2D ( data , data . rate , 1e-3 , data . offset )
540+ assign ( gOptions , mapDygraphOptions ( d , [ xlabel , 'Network Hashrate (petahash/s)' ] ,
541+ false , 'Network Hashrate (petahash/s)' , true , false ) )
542+ yFormatter = customYFormatter ( y => withBigUnits ( y * 1e3 , hashrateUnits ) )
553543 break
554544
555545 case 'missed-votes' :
0 commit comments