Sunday, March 13, 2016

Sample strictly JavaScript Monthly Balance Calculator


Save this folder somewhere you can edit and play around with these files.

Some  of the learning :

1. how to set undeclared variables prohibited
2. handle arrays
3. playing around with dates in jacascript. new Date, setYear, setMonth, setDate, getDateParts, getFullYear, getMonth, getDate
Especially the formatting.
4. classes and objects in JavaScript
5. parseInt, parseFloat

and so on

balance.js:

"use strict"; //  undeclared variables prohibited.

// get element by id
var $ = function (id) { return document.getElementById(id); };

//creates the grid to display based on transList array available in memory ctrl F5 to start over.
var updateDisplay = function () {
    var html = "<tr><th>Date</th><th>Amount</th><th>Balance</th></tr>";
    var html = html.concat("<tr><td></td><td></td><td>0</td></tr>"); // initially balance is zero.

    var count = getTransaction();//get number of elements in transList array.
    var total = 0;

    //itrate transList to create actual transaction grid
    for (var i = 0; i < count; i++) {
        var trans = getTransaction(i);//get i+1 element in transList array.
        total = calculateBalance(trans["type"], trans["amount"], total);//get cumulative balance after current transaction.
        //system allows blank value in date, if blank is passed as input, itmeans current date. If type is withdrawal amount is displayed like, (amount).
        // date format id  MMM DD YYYY ( e.g. Feb 29 2016). future and past dates are allowed.
        html = html.concat("<tr><td>", trans["dateDisplay"], "</td><td>", trans["amountDisplay"], "</td><td>", formatTotal(total), "</td></tr>");
    }
    // set the html calculated to UI.
    $("transactions").innerHTML = html;

};

//if date input field is blank(noTrim) , it means user wants to enter current date
var getValidDateString = function () {
    var dtParts; //undefined
    //if user wants current date
    if ($("date").value === "") {
        dtParts = getDateParts();
    } else {
        dtParts = getDateParts($("date").value);
    }
    //if user wants current date, almost no chance that dtParts remains undefined
    //but if user enters some value it must be a valid date in format MM/DD/YYYY or else dtParts is undefined
    if (typeof dtParts !== "undefined") {
        //return valid date entered/ assumed in format MMM DD YYYY
        return months[dtParts[0] - 1] + " " + ((dtParts[1] < 10 ? "0" : "") + dtParts[1]) + " " + dtParts[2];
    }
    alert("Please enter date in format MM/DD/YYYY e.g. 02/28/2017. You may leave blank if transaction happened today.");// Instrictions
    $("date").focus(); // take user to date input field to re-enter.


};


// there is almost no chance of error here, since dropdown has only two options
var getValidType = function (type) {

    if ($("type").value === "deposit" || $("type").value === "withdrawal")
        return $("type").value;

    alert("Invalid Type.");
    $("type").focus();

}
//if user enters some random text or he tries to give negative values, this will throw alert.
var getValidateAmount = function () {

    //check if not a number.
    if (isNaN($("amount").value)) {
        alert("Invalid amount.");
        $("amount").focus();
        return;
    }
    //round to two decimals
    var returnValue = parseFloat($("amount").value, 10).toFixed(2);//string here to return
    //negative value not allowed, since it means withdrawal, denoted by transaction type
    if (parseFloat($("amount").value, 10) < 0) {
        alert("If it is a withdrawal, select withdrawal in Type dropdown and enter a positive amount here.");
        $("amount").focus();
        return;
    }
    return returnValue;
}

var add = function () {
    var cType = getValidType();
    var cAmount = getValidateAmount();
    var cDateString = getValidDateString();
    if (typeof cType === "undefined" || typeof cAmount === "undefined" || typeof cDateString === "undefined")
        return;
    //till this point we are ready with valid inputs(assumed/actual).
    addTransaction(cType, cAmount, cDateString);
    //now update display to include just added transaction.
    updateDisplay();
};

//bind add function to click of add button on load of window
window.onload = function () {
    $("add").onclick = add;
    updateDisplay();

};

library_balance.js

"use strict"; //  undeclared variables prohibited.

//no form input fields reffered here to make it look like a library.

var transList = []; // array to store transactions

// short form of months to be displayed. This is needed because old browsers don't support toLocaleDateString
//can't use substring on date string, because it contains "," which is not presented in date in screenshot
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

//transaction is the class will be used to insert objects in transList
function transaction(type, amount, date) {
    this.type = type; // deposit or withdrawal  - string
    this.amount = amount; //string
    this.amountDisplay = (type === "deposit") ? amount : "(" + amount + ")"; // display (amount) for withdrawal
    this.dateDisplay = date;//date is already validated and converted to format to be displayed

};


//if input is undefined, it will consider current date as input
//input is string/nothing
//outout is  01/01/2001 >  [ 1, 1, 2001] ie. [ month, day, year]
var getDateParts = function (dateValue) {

    if (typeof dateValue === "undefined") {
        var currentdate = new Date();
        //month is done + 1 because it returns one less number than actual month e.g. 2 for march
        return [currentdate.getMonth() + 1, currentdate.getDate(), currentdate.getFullYear()];
    }
    var datereg = /^\d{1,2}\/\d{1,2}\/\d{4}$/; //e.g.  99/99/1111
    if (dateValue.match(datereg)) {
        var splitDateInput = dateValue.split("/");
        var monthPart = parseInt(splitDateInput[0], 10);// e.g. 99
        var dayPart = parseInt(splitDateInput[1], 10);// e.g. 99
        var yrPart = parseInt(splitDateInput[2], 10);// e.g. 1111
        var date = new Date();// gives current date
        date.setYear(yrPart);
        date.setMonth(monthPart - 1);
        date.setDate(dayPart);
        // now test data ( 99/99/1111) gives date as Sat Jun 07 1119 00:00:00 GMT-0400 (Eastern Daylight Time)
        //so now compare with inputs again.
        //good thing is leap check is also done in below if condition, like 02/29/2015 will fail here.
        if (date.getFullYear() === yrPart && date.getMonth() + 1 === monthPart && date.getDate() === dayPart) {
            return [monthPart, dayPart, yrPart];
        }
        //if input was valid ,an array containing month, day , year numbers is returned
        //or else nothing - undefined

    }

}

//input integer/nothing
//output is a transaction object
var getTransaction = function (index) {
    if (typeof index === "undefined") {
        return transList.length;
    }
    else if (index < transList.length) {
        return transList[index];
    }



};

//now add transaction is simple, inputs must be valid, simply add an object of type transaction to transList
//input string, string , string
var addTransaction = function (type, amount, date) {

    transList.push(new transaction(type, amount, date));
};

//this adds amount to total for deposit
// and subtract amount from deposit for withdrawal
//input string, string, string
//output string
var calculateBalance = function (type, amount, total) {
    //amount = parseFloat(parseFloat(amount, 10).toFixed(2), 10);
    // total = parseFloat(parseFloat(total, 10).toFixed(2), 10);
    amount = parseFloat(amount, 10);
    total = parseFloat(total, 10);
    if (type === "deposit")
        return (total + amount).toFixed(2);
    else
        return (total - amount).toFixed(2);
};

//used to format any string like  -23.2222 to (23.22)
//or 23.2222 to 23.22
var formatTotal = function (am) {

    am = parseFloat(am, 10);

    return (am < 0) ? "(" + Math.abs(am).toFixed(2) + ")" : am.toFixed(2);


};









1 comment: