This is Part 12 of a series of posts describing how to use the Steem Javascript API to view data from the Steem blockchain in a web browser. The previous 11 posts were:
- Part 1: Text editors and minimal HTML
- Part 2: Metadata, Unicode, and inline CSS
- Part 3: Metadata, nav and footer bars, divs, and HTML entities
- Part 4: Forms, scripts, and the developer console
- Part 5: Using HTML id attributes to modify HTML elements using Javascript
- Part 6: Using the Steem API CDN and the getAccounts() function, and examining the account data model
- Part 7: HTML tables, javascript for/in loop, CSS in the HTML style element
- Part 8: HTML, CSS, and Javascript comments; Javascript functions and MVC
- Part 9: Compute Voting Power and Reputation Score, Javascript "const" and "let" with block scope and loop scope
- Part 10: Calculating the Steem Power and Javascript asynchronous vs. synchronous execution
- Part 11: Javascript "switch" statement and computing owned, delegated, and received Steem Power
The previous post described a function that computes different types of Steem Power using a Javascript "switch" statement. The HTML document shown in that post is a bit unwieldy as is the code in the callback to the Steem Javascript API getAccounts() function. In this post, I will first clean up the code in the callback by writing a new function that generates the output table Object which is called "dataToTable{}" in the previous post and then we will break out some of the code into other files in a directory structure and link to those files in the HTML document.
Write a function that assembles the table data
Because our code in the steem.api.getAccounts() callback in the previous post is not structured properly, let's write a new function to fill the "dataToTable{}" Object that is passed to the function that writes the table's HTML markup ("generateTable()"). We will take all the instructions for generating the table Object out of the getAccounts() callback and put them in their own function. The function looks like this:
function generateTableData(userAccountData,steemPerVests){
var userData = userAccountData;
var dataToTable = {
User_Name:userData.name,
STEEM_Balance: userData.balance,
SBD_Balance: userData.sbd_balance,
Voting_Power: 0,
Reputation_Score: 0,
Reputation_Score_Formatter: 0,
Steem_Power: 99999,
Steem_Power_Delegated: 99999,
Steem_Power_Received: 99999,
Steem_Power_Effective: 99999
};
dataToTable.Steem_Power = computeSteemPower(userAccountData,steemPerVests,'vested').toFixed(3);
dataToTable.Steem_Power_Delegated = computeSteemPower(userAccountData,steemPerVests,'delegated').toFixed(3);
dataToTable.Steem_Power_Received = computeSteemPower(userAccountData,steemPerVests,'received').toFixed(3);
dataToTable.Steem_Power_Effective = computeSteemPower(userAccountData,steemPerVests,'effective').toFixed(3);
dataToTable.Voting_Power = computeVotingPower(userData);
dataToTable.Reputation_Score_Formatter = steem.formatter.reputation(userData.reputation);
dataToTable.Reputation_Score = computeReputationScore(userData);
return dataToTable;
}
"generateTableData()" takes the "userAccountData{}" Object and the "steemPerVests" value as parameters and returns an Object containing the keys and values of the items to put in the HTML table. First we declare the "dataToTable{}" Object with values from the userAccountData and specify keys and placeholders for values that need to be calculated. Then we calculate the values that need to be calculated using other functions that we have written like "computeSteemPower()", "computeVotingPower()", and "computeReputationScore()". Then "generateTableData()" returns the "dataToTable{}" Object. Notice that generateTableData() uses the javascript Number.prototype.toFixed() method to format the SP.
Our "showUserData()" function now looks like this:
function showUserData() {
getElementReferences(['nameForm','dataView']);
getUserInput(htmlElements.nameForm.elements);
steem.api.getDynamicGlobalProperties(function(err,result){
computeSPV(result);
steem.api.getAccounts([userInputs.sUser], function(err,result){
var userData = result[0];
var dataToTable = generateTableData(userData,computedValues.steemPerVests);
var theText = generateTable('Metric', 'Value',dataToTable);
htmlElements.dataView.innerHTML = theText;
});
});
}
This is much better than before.
Splitting the HTML file into multiple files
The HTML file has gotten quite long so we are now going to rearrange the code by breaking it into several files in a directory structure. We will make a new file folder named after the project. I call my file folder “steemAPIDemo”. Inside of that folder, will be an HTML file called “index.html”. This is the main html file that will be loaded into the browser. Then I make two folders. One is called “scripts” and the other is called “stylesheets”. I will make three files in the “scripts” folder called “controlFuncs.js”, “modelFuncs.js”, and “viewFuncs.js” and one file in the “stylesheets” folder called “steemAPIDemo.css”. The files in the “scripts” folder will contain javascript functions and the file in the stylesheets folder will contain css styles for the html document. The file directory structure looks like this:
Javascript files
The names of the javascript files follow the MVC framework discussed in part 8 of this series . The functions and object declarations that will be put in the files are grouped according to whether they describe or fill a data model (modelFuncs.js), act on the HTML view (viewFuncs.js), or take some data and do a calculation on it (controlFuncs.js). modelFuncs.js will contain the "computedValues{}" Object declaration and the "generateTableData()" function. viewFuncs.js will contain the "htmlElements{}" and "userInputs{}" Object declarations and the "generateTable()", "getElementReferences()", and "getUserInput()" functions. controlFuncs.js contains the "computeVotingPower()", "computeReputationScore()", "amount()", "computeSteemPower()", and "computeSPV()" functions. The functions in controlFuncs.js all use data from one data model (e.g. userAccounts and/or dynamic global properties), compute a value, and pass the value to another data model (e.g. dataToTable).
modelFuncs.js file:
// modelFunc.js
var computedValues = {vestsDelegated: 0,steemPerVests: 0.005};
function generateTableData(userAccountData){
var userData = userAccountData;
var dataToTable = {
User_Name:userData.name,
STEEM_Balance: userData.balance,
SBD_Balance: userData.sbd_balance,
Voting_Power: 0,
Reputation_Score: 0,
Reputation_Score_Formatter: 0,
Steem_Power: 99999,
Steem_Power_Delegated: 99999,
Steem_Power_Received: 99999,
Steem_Power_Effective: 99999
};
dataToTable.Steem_Power = computeSteemPower(userAccountData,computedValues.steemPerVests,'vested').toFixed(3);
dataToTable.Steem_Power_Delegated = computeSteemPower(userAccountData,computedValues.steemPerVests,'delegated').toFixed(3);
dataToTable.Steem_Power_Received = computeSteemPower(userAccountData,computedValues.steemPerVests,'received').toFixed(3);
dataToTable.Steem_Power_Effective = computeSteemPower(userAccountData,computedValues.steemPerVests, 'effective').toFixed(3);
dataToTable.Voting_Power = computeVotingPower(userData);
dataToTable.Reputation_Score_Formatter = steem.formatter.reputation(userData.reputation);
dataToTable.Reputation_Score = computeReputationScore(userData);
return dataToTable;
}
viewFuncs.js file:
//viewFuncs.js
var htmlElements = {};
var userInputs = {};
function generateTable(header1,header2,objectToTable){
var tableText = "<table>";
tableText = tableText+"<tr><th>"+header1+"</th><th>"+header2+"</th></tr>";
for (var key in objectToTable){
var keyStr = key.replace(/_/g," ");
tableText = tableText+"<tr><td>"+keyStr+"</td><td>"+objectToTable[key]+"</td></tr>";
}
tableText = tableText+"</table>";
return tableText;
}
function getElementReferences(elementNames){
for (index in elementNames){
var name = elementNames[index];
htmlElements[name] = document.getElementById(name);
}
}
function getUserInput(formElements){
for( var i=0;i<formElements.length;i++){
if(formElements[i].name !== ""){
userInputs[formElements[i].name]=formElements[i].value;
}
}
}
controlFuncs.js file :
//controlFuncs.js
function computeVotingPower(userAccountData){
var lastVoteTime = userAccountData.last_vote_time;
var votingPower = userAccountData.voting_power;
var timeBeforeNow = (Date.now() - new Date(lastVoteTime+"Z"))/1000;
var votingPowerNow = Math.min(votingPower/100+timeBeforeNow*20/86400,100).toFixed(2);
return votingPowerNow+"%";
}
function computeReputationScore(userAccountData){
var repScore = Math.sign(userAccountData.reputation)*userAccountData.reputation;
repScore = Math.log10(repScore);
if(isNaN(repScore)){repScore = 0};
repScore = Math.max(repScore-9,0);
repScore = Math.sign(repScore)*repScore;
repScore = (repScore*9)+25;
repScore = repScore.toFixed(2);
return repScore;
}
function amount(labeledValue){
return parseFloat(labeledValue.split(" ")[0]);
}
function computeSteemPower(userAccountData,steemPerVests,powerType){
switch(powerType){
case 'vested':
return amount(userAccountData['vesting_shares'])*steemPerVests;
break;
case 'delegated':
return amount(userAccountData['delegated_vesting_shares'])*steemPerVests;
break;
case 'received':
return amount(userAccountData['received_vesting_shares'])*steemPerVests;
break;
case 'effective':
return (amount(userAccountData['vesting_shares'])+
amount(userAccountData['received_vesting_shares'])-
amount(userAccountData['delegated_vesting_shares']))*steemPerVests;
break;
default:
return 9999;
}
}
function computeVestsDelegated(gvdRecords){
for(var recNum in gvdRecords){
var vestingShares = amount(gvdRecords[recNum]['vesting_shares']);
computedValues.vestsDelegated = computedValues.vestsDelegated + vestingShares;
}
}
function computeSPV(gdp){
var tvfs = amount(gdp['total_vesting_fund_steem']);
var tvs = amount(gdp['total_vesting_shares']);
computedValues.steemPerVests = tvfs/tvs;
}
CSS file
The .css file in the stylesheets directory will contain a copy and paste of the selectors and their instructions already in the <style> tags in the <head> of the HTML document.
steemAPIDemo.css file:
/* steemAPIDemo.css */
td,th,footer,nav {
border: 1px solid black;
}
table {
width: 100%;
}
div {
padding: 10px;
margin: 5px;
background-color: pink;
}
#dataView {
background-color: lightsalmon;
overflow: auto;
height: 200px;
}
#formDiv {
background-color: lightblue;
}
Modifying the HTML document to use external files
To use the javascript files in the HTML document, we replace their code (which has been copied and pasted into the files) with links in <script> tags in the HTML document like so:
<script src="scripts/modelFuncs.js"></script>
<script src="scripts/viewFuncs.js"></script>
<script src="scripts/controlFuncs.js"></script>
The functions in the files will be loaded when the HTML document is loaded and are therefore available anywhere in the document but it is good practice to put them above the "showUserData()" function which depends on them.
To use the css file in the HTML document, we replace the <style> tags in the <head> section with a <link> tag that links to the css file like so:
<link rel="stylesheet" type="text/css" href="stylesheets/steemAPIdemo.css">
The linking to the .css and .js files depends on the directory structure and won't work unless the stylesheets and scripts directories are in the folder with the HTML document.
Now, our entire HTML document looks like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Show a Steemit User's Account Data">
<title>Show User's Data</title>
<script src="https://cdn.steemjs.com/lib/latest/steem.min.js"></script>
<link rel="stylesheet" type="text/css" href="stylesheets/steemAPIdemo.css">
</head>
<body>
<nav>
</nav>
<br>
<div id="formDiv">
<br>
<form action="#" onsubmit="showUserData();return false;" id="nameForm">
Enter your steemit userName: <input type="text" name="sUser" id="userNid">
<input type="submit" value="Submit">
</form>
<br>
</div>
<div id="dataView">
<br>
<p></p>
<br>
</div>
<br>
<footer>
</footer>
<script src="scripts/modelFuncs.js"></script>
<script src="scripts/viewFuncs.js"></script>
<script src="scripts/controlFuncs.js"></script>
<script>
function showUserData() {
getElementReferences(['nameForm','dataView']);
getUserInput(htmlElements.nameForm.elements);
steem.api.getDynamicGlobalProperties(function(err,result){
computeSPV(result);
steem.api.getAccounts([userInputs.sUser], function(err,result){
var userData = result[0];
var dataToTable = generateTableData(userData);
var theText = generateTable('Metric', 'Value',dataToTable);
htmlElements.dataView.innerHTML = theText;
});
});
}
</script>
</body>
</html>
You got a 3.43% upvote from @postpromoter courtesy of @numberjocky!
Want to promote your posts too? Check out the Steem Bot Tracker website for more info. If you would like to support the development of @postpromoter and the bot tracker please vote for @yabapmatt for witness!