From fb52438586fdc37dc758c2f01be1fac8c841d885 Mon Sep 17 00:00:00 2001 From: marcdeb1 Date: Wed, 10 Oct 2018 16:02:13 +0200 Subject: [PATCH 1/4] Added mining inflation chart --- src/Controller/MainController.php | 3 +- src/Template/Main/blocks.ctp | 231 +-------------------- src/Template/Main/stats.ctp | 29 ++- webroot/css/mining-inflation-chart.css | 8 + webroot/js/block-size-chart.js | 228 +++++++++++++++++++++ webroot/js/mining-inflation-chart.js | 265 +++++++++++++++++++++++++ 6 files changed, 526 insertions(+), 238 deletions(-) create mode 100644 webroot/css/mining-inflation-chart.css create mode 100644 webroot/js/block-size-chart.js create mode 100644 webroot/js/mining-inflation-chart.js diff --git a/src/Controller/MainController.php b/src/Controller/MainController.php index 5ac133c..cbb89ff 100644 --- a/src/Controller/MainController.php +++ b/src/Controller/MainController.php @@ -504,7 +504,8 @@ class MainController extends AppController { $richList = $this->Addresses->find()->where(['Address <>' => 'bHW58d37s1hBjj3wPBkn5zpCX3F8ZW3uWf'])->order(['Balance' => 'DESC'])->limit(500)->toArray(); $priceRate = 0; - $priceInfo = json_decode($this->redis->get(self::lbcPriceKey)); + //$priceInfo = json_decode($this->redis->get(self::lbcPriceKey)); + $priceInfo->price = 0.05; if (isset($priceInfo->price)) { $priceRate = $priceInfo->price; } diff --git a/src/Template/Main/blocks.ctp b/src/Template/Main/blocks.ctp index b662378..38b8543 100644 --- a/src/Template/Main/blocks.ctp +++ b/src/Template/Main/blocks.ctp @@ -134,236 +134,7 @@ - + end(); ?>
diff --git a/src/Template/Main/stats.ctp b/src/Template/Main/stats.ctp index 36e056b..e10122d 100644 --- a/src/Template/Main/stats.ctp +++ b/src/Template/Main/stats.ctp @@ -1,19 +1,34 @@ assign('title', 'Stats & Rich List') ?> -start('script'); ?> - -end(); ?> - element('header') ?> +start('script'); ?> + + + + + +end(); ?> + +start('css'); + echo $this->Html->css('/css/mining-inflation-chart.css'); + echo $this->Html->css('https://www.amcharts.com/lib/3/plugins/export/export.css'); + $this->end(); + ?>

LBRY Stats

-
+
+
+
+

Mining Inflation Chart

+
+
+
+

LBRY Rich List (Top 500)

diff --git a/webroot/css/mining-inflation-chart.css b/webroot/css/mining-inflation-chart.css new file mode 100644 index 0000000..92f1b34 --- /dev/null +++ b/webroot/css/mining-inflation-chart.css @@ -0,0 +1,8 @@ +.mining-inflation-chart-container { width: 1200px; margin: 0 auto 48px auto; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px 36px; position: relative; overflow: hidden } +.mining-inflation-chart-container .load-progress { position: absolute; top: 0; left: 0; width: 100%; height: 3px; background: #1e88e5; animation: indeterminate 4s linear infinite; } +.mining-inflation-chart-container .chart { height: 414px } +.mining-inflation-chart-container .btn-chart-export { position: absolute; right: 40px; bottom: 36px } +@keyframes indeterminate { + from { left: -70%; } + to { left: 100% } +} \ No newline at end of file diff --git a/webroot/js/block-size-chart.js b/webroot/js/block-size-chart.js new file mode 100644 index 0000000..24aba9a --- /dev/null +++ b/webroot/js/block-size-chart.js @@ -0,0 +1,228 @@ +var chart; +var chartData = []; +var chartLoadInProgress = false; +var minPeriod = 'hh'; +var validPeriods = ['24h', '72h', '168h', '30d', '90d', '1y']; +var defaultPeriod = (validPeriods.indexOf(localStorage.getItem('chartPeriod')) > -1) ? localStorage.getItem('chartPeriod') : '24h'; +var periodGridCounts = {'24h': 24, '72h': 24, '168h': 14, '30d': 30, '90d': 45, '1y': 12 }; +AmCharts.ready(function() { + chart = AmCharts.makeChart('block-size-chart', { + type: 'serial', + theme: 'light', + mouseWheelZoomEnabled: true, + categoryField: 'date', + synchronizeGrid: true, + dataProvider: chartData, + valueAxes: [ + { + id: 'v-block-size', + axisColor: '#1e88e5', + axisThickness: 2, + labelFunction: function(value) { + return (Math.round((value / 1000) * 100)/100).toFixed(2) + ' KB'; + } + }, + { + id: 'v-price', + axisColor: '#00e676', + offset: 75, + gridAlpha: 0, + axisThickness: 2, + labelFunction: function(value) { + return '$' + value.toFixed(2); + } + } + ], + categoryAxis: { + parseDates: true, + minPeriod: minPeriod, // DD for daily + autoGridCount: false, + minorGridEnabled: true, + minorGridAlpha: 0.04, + axisColor: '#dadada', + twoLineMode: true, + dateFormats: [{ + period: 'fff', + format: 'JJ:NN:SS' + }, { + period: 'ss', + format: 'JJ:NN:SS' + }, { + period: 'mm', + format: 'JJ:NN' + }, { + period: 'hh', + format: 'JJ:NN' + }, { + period: 'DD', + format: 'DD' + }, { + period: 'WW', + format: 'DD MMM' + }, { + period: 'MM', + format: 'MMM' + }, { + period: 'YYYY', + format: 'YYYY' + }] + }, + graphs: [ + { + id: 'g-block-size', + valueAxis: 'v-block-size', // we have to indicate which value axis should be used + title: 'Avg Block Size', + valueField: 'AvgBlockSize', + bullet: 'round', + bulletBorderThickness: 1, + bulletBorderAlpha: 1, + bulletColor: '#ffffff', + bulletSize: 5, + useLineColorForBulletBorder: true, + lineColor: '#1e88e5', + hideBulletsCount: 101, + balloonText: '[[AvgBlockSize]] KB', + switchable: false, + balloonFunction: function(item, graph) { + var result = graph.balloonText; + return result.replace('[[AvgBlockSize]]', (Math.round((item.dataContext.AvgBlockSize / 1000) * 100)/100).toFixed(2)); + } + }, + { + id: 'g-price', + valueAxis: 'v-price', + title: 'Average Price', + valueField: 'AvgUSD', + bullet: 'round', + bulletBorderThickness: 1, + bulletBorderAlpha: 1, + bulletColor: '#ffffff', + bulletSize: 5, + useLineColorForBulletBorder: true, + lineColor: '#00e676', + balloonText: '$[[AvgUSD]]', + balloonFunction: function(item, graph) { + var result = graph.balloonText; + if (!item.dataContext.AvgUSD) { + return ''; + } + return result.replace('[[AvgUSD]]', item.dataContext.AvgUSD.toFixed(2)); + }, + hideBulletsCount: 101, + labelFunction: function(value) { + return '$' + value; + }, + } + ], + chartCursor: { + cursorAlpha: 0.1, + fullWidth: true, + valueLineBalloonEnabled: true, + categoryBalloonColor: '#333333', + cursorColor: '#1e88e5', + categoryBalloonDateFormat: minPeriod === 'hh' ? 'D MMM HH:NN ' : 'D MMM' + }, + chartScrollbar: { + scrollbarHeight: 36, + color: '#888888', + gridColor: '#bbbbbb' + }, + legend: { + marginLeft: 110, + useGraphSettings: true, + valueAlign: 'right', + valueWidth: 60, + spacing: 64, + valueFunction: function(item, formatted) { + if (item.dataContext) { + var g = item.graph; + if (g.id === 'g-block-size' && item.dataContext.AvgBlockSize > 0) { + return g.balloonText.replace('[[AvgBlockSize]]', (Math.round((item.dataContext.AvgBlockSize / 1000) * 100)/100).toFixed(2) ); + } + if (g.id === 'g-price' && item.dataContext.AvgUSD) { + return g.balloonText.replace('[[AvgUSD]]', item.dataContext.AvgUSD.toFixed(2)); + } + } + + return formatted; + } + }, + export: { + enabled: true, + fileName: 'lbry-block-size-chart', + position: 'bottom-right', + divId: 'chart-export' + } + }); + + loadChartData(defaultPeriod); +}); + +var loadChartData = function(dataPeriod) { + var loadProgress = $('.block-size-chart-container .load-progress'); + // clear previous chart data + $.ajax({ + url: '/api/v1/charts/blocksize/' + dataPeriod, + type: 'get', + dataType: 'json', + beforeSend: function() { + chartLoadInProgress = true; + loadProgress.css({ display: 'block' }); + }, + success: function(response) { + if (response.success) { + chartData = []; + var data = response.data; + for (var period in data) { + if (data.hasOwnProperty(period)) { + chartData.push({ + date: Date.parse(period), + AvgBlockSize: data[period].AvgBlockSize, + AvgUSD: data[period].AvgUSD + }); + } + } + + // save selcted period to localStorage + localStorage.setItem('chartPeriod', dataPeriod); + + if (chart) { + var isHourly = (dataPeriod.indexOf('h') > -1); + var gridCount = periodGridCounts[dataPeriod]; + chart.categoryAxis.minPeriod = isHourly ? 'hh' : 'DD'; + chart.categoryAxis.dateFormats[4].format = isHourly ? 'DD MMM' : 'DD'; + chart.chartCursor.categoryBalloonDateFormat = isHourly ? 'D MMM HH:NN ' : 'D MMM YYYY'; + chart.categoryAxis.gridCount = gridCount; + chart.chartScrollbar.gridCount = gridCount; + chart.dataProvider = chartData; + chart.validateNow(); + chart.validateData(); + } + } + }, + complete: function() { + chartLoadInProgress = false; + loadProgress.css({ display: 'none' }); + } + }); +}; + +$(document).ready(function() { + $('.block-size-data-links a').on('click', function(evt) { + evt.preventDefault(); + if (chartLoadInProgress) { + return; + } + + var link = $(this); + if (link.hasClass('active')) { + return; + } + + link.addClass('active').siblings().removeClass('active'); + var period = link.attr('data-period'); + loadChartData(period); + }); + + $('a[data-period="' + defaultPeriod + '"]').addClass('active').siblings().removeClass('active'); +}); \ No newline at end of file diff --git a/webroot/js/mining-inflation-chart.js b/webroot/js/mining-inflation-chart.js new file mode 100644 index 0000000..e43411c --- /dev/null +++ b/webroot/js/mining-inflation-chart.js @@ -0,0 +1,265 @@ +function getReward(blockHeight) { + if (blockHeight == 0) { + return 400000000; + } + else if (blockHeight <= 5100) { + return 1; + } + else if (blockHeight <= 55000) { + return 1 + Math.floor((blockHeight - 5001) / 100); + } + else { + var level = Math.floor((blockHeight - 55001) / 32); + var reduction = Math.floor((Math.floor(Math.sqrt((8 * level) + 1)) - 1) / 2); + while(!(withinLevelBounds(reduction, level))) { + if(Math.floor((reduction * reduction + reduction) / 2) > level) { + reduction--; + } + else { + reduction++; + } + } + return Math.max(0, 500 - reduction); + } +} + +function withinLevelBounds(reduction, level) { + if(Math.floor((reduction * reduction + reduction) / 2) > level) { + return false; + } + reduction += 1; + if(Math.floor((reduction * reduction + reduction) / 2) <= level) { + return false; + } + return true; +} + +function getAverageBlockTime(blocks) { + var numBlocks = blocks.length; + var windowSize = 100; + var sum = 0; + for(i = numBlocks - windowSize; i < numBlocks; i++) { + sum += blocks[i].block_time - blocks[i-1].block_time; + } + return sum / windowSize; +} + +function buildChartData(blockData) { + var chartData = []; + var supply = 0; + var reward = 0; + var averageBlockTime = getAverageBlockTime(blockData); + var blockTime = 0; + var lastBlock = 4071017; // Last block with reward + var skip = 100; + var blocksPerYear = Math.floor((3600*24*365) / averageBlockTime); + var historicalSupply = {}; + var lastYearSupply = 0; + var lastYearBlock = 0; + var inflationRate = 0; + for(var i = 0; i < lastBlock; i++) { + reward = getReward(i); + supply += reward; + historicalSupply[i + 1] = supply; + if(i == 0) { // Reward for 1st block set to 0 for scale + reward = 0; + } + if(i < blockData.length) { + // Historical Data + var b = blockData[i]; + blockTime = b.block_time; + } + else { + // Future blocks + skip = 1000; + blockTime += averageBlockTime; + } + // Inflation Rate + if(i + 1 - blocksPerYear <= 0) { + lastYearBlock = 1; + } + else { + lastYearBlock = i + 1 - blocksPerYear; + } + lastYearSupply = historicalSupply[lastYearBlock]; + inflationRate = ((supply - lastYearSupply) / lastYearSupply) * 100; + if(i % skip == 0) { // Only push 1/ of all blocks to optimize data loading + chartData.push({ + date: new Date(blockTime * 1000), + date: new Date(blockTime * 1000), + AvailableSupply: supply, + RewardLBC: reward, + InflationRate: inflationRate, + BlockId: i + 1 + }); + } + } + return chartData; +} + +function loadChartData() { + var api_url = "https://chainquery.lbry.io/api/sql?query="; + var query = "SELECT id, block_time FROM block"; + var url = api_url + query; + var loadProgress = $('.mining-inflation-chart-container .load-progress'); + $.ajax({ + url: url, + type: 'get', + dataType: 'json', + beforeSend: function() { + chartLoadInProgress = true; + loadProgress.css({ display: 'block' }); + }, + success: function(response) { + if(response.success) { + chartData = buildChartData(response.data); + if(chart) { + chart.dataProvider = chartData; + chart.validateNow(); + chart.validateData(); + } + } + else { + console.log("Could not fetch block data."); + } + }, + complete: function() { + chartLoadInProgress = false; + loadProgress.css({ display: 'none' }); + } +}); +} + +var chart; +var chartData = []; +var chartLoadInProgress = false; +AmCharts.ready(function() { +chart = AmCharts.makeChart('mining-inflation-chart', { +type: 'serial', +theme: 'light', +mouseWheelZoomEnabled: true, +height: '100%', +categoryField: 'date', +synchronizeGrid: true, +dataProvider: chartData, +responsive: { + enabled: true, +}, +valueAxes: [ +{ + id: 'v-supply', + axisColor: '#1e88e5', + axisThickness: 2, + position: 'left', + labelFunction: function(value) { + return (Math.round((value / 1000000) * 1000000)/1000000).toFixed(2); + } +}, +{ + id: 'v-reward', + axisColor: '#0b7a06', + axisThickness: 2, + position: 'left', + offset: 75, +}, +{ + id: 'v-inflation-rate', + axisColor: '#ff9900', + axisThickness: 2, + position: 'right', + labelFunction: function(value) { + return value.toFixed(2); + } +}, +], +categoryAxis: { +parseDates: true, +autoGridCount: false, +minorGridEnabled: true, +minorGridAlpha: 0.04, +axisColor: '#dadada', +twoLineMode: true +}, +graphs: [ +{ + id: 'g-supply', + valueAxis: 'v-supply', // we have to indicate which value axis should be used + title: 'Available supply (millions LBC)', + valueField: 'AvailableSupply', + bullet: 'round', + bulletBorderThickness: 1, + bulletBorderAlpha: 1, + bulletColor: '#ffffff', + bulletSize: 5, + useLineColorForBulletBorder: true, + lineColor: '#1e88e5', + hideBulletsCount: 101, + balloonText: '[[AvailableSupply]]', + balloonFunction: function(item, graph) { + var result = graph.balloonText; + return result.replace('[[AvailableSupply]]', (Math.round((item.dataContext.AvailableSupply / 1000000) * 1000000)/1000000).toFixed(2)); + } +}, +{ + id: 'g-reward', + valueAxis: 'v-reward', + title: 'Block Reward (LBC)', + valueField: 'RewardLBC', + bullet: 'round', + bulletBorderThickness: 1, + bulletBorderAlpha: 1, + bulletColor: '#ffffff', + bulletSize: 5, + useLineColorForBulletBorder: true, + lineColor: '#0b7a06', + balloonText: '[[RewardLBC]] LBC
Block [[BlockId]]', + hideBulletsCount: 101 +}, +{ + id: 'g-inflation-rate', + valueAxis: 'v-inflation-rate', + title: 'Annualized Inflation Rate', + valueField: 'InflationRate', + bullet: 'round', + bulletBorderThickness: 1, + bulletBorderAlpha: 1, + bulletColor: '#ffffff', + bulletSize: 5, + useLineColorForBulletBorder: true, + lineColor: '#ff9900', + balloonText: '[[InflationRate]]%', + hideBulletsCount: 101, + balloonFunction: function(item, graph) { + var result = graph.balloonText; + return result.replace('[[InflationRate]]', item.dataContext.InflationRate.toFixed(2)); + } +} +], +chartCursor: { +cursorAlpha: 0.1, +fullWidth: true, +valueLineBalloonEnabled: true, +categoryBalloonColor: '#333333', +cursorColor: '#1e88e5' +}, +chartScrollbar: { +scrollbarHeight: 36, +color: '#888888', +gridColor: '#bbbbbb' +}, +legend: { +marginLeft: 110, +useGraphSettings: true, +valueText: "", +spacing: 64, + +}, +export: { +enabled: true, +fileName: 'lbry-supply-chart', +position: 'bottom-right', +divId: 'chart-export' +} +}); +loadChartData(); +}); \ No newline at end of file -- 2.43.4 From 11a9f535ff5d2107a0de48dbcd64e8187db060cb Mon Sep 17 00:00:00 2001 From: marcdeb1 Date: Wed, 10 Oct 2018 16:09:33 +0200 Subject: [PATCH 2/4] minor fix --- src/Controller/MainController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Controller/MainController.php b/src/Controller/MainController.php index cbb89ff..5ac133c 100644 --- a/src/Controller/MainController.php +++ b/src/Controller/MainController.php @@ -504,8 +504,7 @@ class MainController extends AppController { $richList = $this->Addresses->find()->where(['Address <>' => 'bHW58d37s1hBjj3wPBkn5zpCX3F8ZW3uWf'])->order(['Balance' => 'DESC'])->limit(500)->toArray(); $priceRate = 0; - //$priceInfo = json_decode($this->redis->get(self::lbcPriceKey)); - $priceInfo->price = 0.05; + $priceInfo = json_decode($this->redis->get(self::lbcPriceKey)); if (isset($priceInfo->price)) { $priceRate = $priceInfo->price; } -- 2.43.4 From d749f1f96c571d4d427a2f20549765bf2f5a9f28 Mon Sep 17 00:00:00 2001 From: marcdeb1 Date: Wed, 10 Oct 2018 16:54:23 +0200 Subject: [PATCH 3/4] Added logo to indicate addresses owned by LBRY --- src/Controller/MainController.php | 8 +++++--- src/Template/Main/stats.ctp | 5 +++++ webroot/img/lbry.png | Bin 0 -> 2651 bytes 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 webroot/img/lbry.png diff --git a/src/Controller/MainController.php b/src/Controller/MainController.php index 5ac133c..9d06e61 100644 --- a/src/Controller/MainController.php +++ b/src/Controller/MainController.php @@ -504,12 +504,13 @@ class MainController extends AppController { $richList = $this->Addresses->find()->where(['Address <>' => 'bHW58d37s1hBjj3wPBkn5zpCX3F8ZW3uWf'])->order(['Balance' => 'DESC'])->limit(500)->toArray(); $priceRate = 0; - $priceInfo = json_decode($this->redis->get(self::lbcPriceKey)); + //$priceInfo = json_decode($this->redis->get(self::lbcPriceKey)); + $priceInfo->price = 0.05; if (isset($priceInfo->price)) { $priceRate = $priceInfo->price; } - - // calculate percentages + + $lbryAddresses = ['rFLUohPG4tP3gZHYoyhvADCtrDMiaYb7Qd', 'r9PGXsejVJb9ZfMf3QVdDEJCzxkd9JLxzL', 'r9srwX7DEN7Mex3a8oR1mKSqQmLBizoJvi', 'bRo4FEeqqxY7nWFANsZsuKEWByEgkvz8Qt', 'bU2XUzckfpdEuQNemKvhPT1gexQ3GG3SC2', 'bay3VA6YTQBL4WLobbG7CthmoGeUKXuXkD', 'bLPbiXBp6Vr3NSnsHzDsLNzoy5o36re9Cz', 'bMvUBo1h5WS46ThHtmfmXftz3z33VHL7wc', 'bVUrbCK8hcZ5XWti7b9eNxKEBxzc1rr393', 'bZja2VyhAC84a9hMwT8dwTU6rDRXowrjxH', 'bMvUBo1h5WS46ThHtmfmXftz3z33VHL7wc', 'bMgqQqYfwzWWYBk5o5dBMXtCndVAoeqy6h']; $totalBalance = 0; $maxBalance = 0; $minBalance = 0; @@ -527,6 +528,7 @@ class MainController extends AppController { $this->set('richList', $richList); $this->set('rate', $priceRate); + $this->set('lbryAddresses', $lbryAddresses); } public function address($addr = null) { diff --git a/src/Template/Main/stats.ctp b/src/Template/Main/stats.ctp index e10122d..9adcab1 100644 --- a/src/Template/Main/stats.ctp +++ b/src/Template/Main/stats.ctp @@ -48,6 +48,11 @@
)?^2DlRU*CYjkw6ZH1XPpTZV6%=U3oOx;;5Uh4)i^2vC z(S}W%oWr@2rr&61;(8TX4PXQ?9O!m7>pZJdt)!%-v3LIg+}&yr77##Wcgs}{@p`dx z-FoIMUBqbOIjCl zb4gB3BgvY^?jL`|WHJ%r=S%;d;W!+27Japf?R)n8-aCry%kbD38rG`^kOI787{-=c zX(t*t`!+5i;t6yGJ_Oq47Zk8<*Y`~O@IPc_XH(156G;>m72(sqJ=0#8!h=nk;Ns$P z4u}Ib8-`K*S81z9Rcw1Dk~DAGa+zZX$v3jUW-VOgluMzcwH4>iU+3Gv6+{L|TH`!N zem#5E34V(t%bobJVcaevmq^PUXbp@4Jb<;p8pALy-ig-v{{R$ Date: Wed, 10 Oct 2018 16:56:49 +0200 Subject: [PATCH 4/4] Minor fix --- src/Template/Main/stats.ctp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Template/Main/stats.ctp b/src/Template/Main/stats.ctp index 9adcab1..84609c2 100644 --- a/src/Template/Main/stats.ctp +++ b/src/Template/Main/stats.ctp @@ -50,7 +50,7 @@
Address ?> + Address, $lbryAddresses)): ?> + + + + Tag) && strlen(trim($item->Tag)) > 0): ?>
TagUrl)) > 0): ?>Tag ?>Tag; endif; ?> diff --git a/webroot/img/lbry.png b/webroot/img/lbry.png new file mode 100644 index 0000000000000000000000000000000000000000..dc58a66ad3999721ac682b9dc7e4692bd54699d7 GIT binary patch literal 2651 zcmV-h3Z(UkP)O<|;?LPKBDr%z>k*C?beMqL^k*E6uEIy4`MeSA*Tk zn`Pyk1Ez@rP8=x5awx@HPC1ZMrics&5I6^(`$rtE1DY;1d;i+&eD6N{dA|L8-}k)l zHn>e82>`<|kmL$H48#M~fv*88kYgBz_$!AbZ%Lm>DlRB2(!nD~RT29oO_S98O44^5 z@TJ!iB%PC_T^W1TD=uEXkx`oR&o`Bmm**ssC+TBJ)o#c0java;Ar}L92$%x|+X@Q# zbny~CTeKLr8a426cPHOw!|rhK#K2hMV+T;*>pomvT><`4ao-ukFv|WE0#|6gE-(d{ z47e>%S<9TI2^>6nlqQWD5*OW{P8~Y1HvMZ-($YD5{yYu5yyzJcOhi~HT^{ZTkOM3R z<{5^u`_jp|)6$8nkW2Zo6DJk@PRn^Q7!I2_8#&errP-7mQVU7d|A>rn6I!nYj02tl z8m!;AiTPiyVEgXf)Tv#Y!TlnMkBy;$m)8wVO6JLvBwN#2la|i@!-u)2RxQkazC?tD z5*px-tBZ>rSPslL48wY*d~bHveI)%PsoYjjpfOWlmQRmfY8?=)fnz7==r6}rR#v8) zjq>tx<>u#WdGZ={jfhk$|6uuq_fq7DQQEL+voozo(g8_PHzchqfZr?WLrE$rEmcyg zRqf5)$l&7zhj;Extl1fWTOSS!n zJqn7Bm3Lr>y7h_Fn=?Mr-HO#4Qq|+}IJF5fD`NNvNh+5#5x`UdJ{oul@Hue!Fte8= zkYr86#W3g|9K?v&0r<3Uch~Gn@32tLW@Yi-r=MT7fK8H?0sR5f`=8GyVO0{g!b0w; zU7NRFoJwHVE>y2x{jP!EnX#AG-~WJ9zn-D2yc|nlz~uyh*q@JOGU1h1IdSSV9v&Wa z4-Vp;m!{rT;Q0jw%v$gTvlEu#R-*eZ?tiHNY!O7M9$ z8^?~H;E{H1|1|K2jviy`zuqGA#0hMLh4cX;{BrRq2C)7N*Pe^t;6cAUh|A zof)eFZI2=7!uWwVKM!=uWnr=k+^O>AIzSMt)KwA!-2`w70;SAS^Duu04J|W zpkcjwtXVjZ6)9_&F?T-6)-*P4-_C7;tSe&>TzsP8I?b%CBcQ;;odIIzQ z{Vq-KzyC6)`RloJOnmKiPMrP~DzG`q%6N9nXn?~&s>%7Ii;D~CTes1wSu?u%JaXAX zt5q}c>ZGR`9vef7HJv1D8rCga*t}y0mH>bHSS)l83iw0t>07rkXX!F_W@O;*=ElIj z5sVx>h(`76UzTSpEM!UIDi(c}$njIBY16U=W7uuz&dZVa#r$VZZrQmtxd zw#Ylktj;|nGyThSZ-kGjr$B#== zjwEjYL((pwb8$&2?|w3i^&2;lmv6)FuoLR<$CNQo(WGHRs#mYNi7;c%JXUSkK=y@P zY=s4M^Ld09#*d>-i{_L|Y}v7cm*1X_t+0^$>egjQzevUnkGtxBF68F&-lw0lBylyh zJZsXhUOir$JPE%podM1PZjx$ONG#njj4&sGy1?j)eYNAMGiO+}A(a&C@0C)mnx6Cs z2_({DA?UGgRg%cc&Sq`;H>^&zlCf_eCX)%XpD(3$JKt>k4i67^A}wJIi;cm%dGpKe zwL2UntV&|RiWM9?eu72~>JuM3fVk-XxYeiuun_n=(59jyEC!|;hH>mNUnHrvq)
Address ?> Address, $lbryAddresses)): ?> - + Tag) && strlen(trim($item->Tag)) > 0): ?> -- 2.43.4