1
\$\begingroup\$

About & Purpose


Purpose: As said by the title this is just a simple web application for use of a SINGLE user. It has the features of calculating total hours, clocking in, and clocking out. It also has some functions that helps with the calculations. This is for OFFLINE USE ONLY.

This is by no means for commercial use.


List of Functions and their Purpose


Main Functions

initialize() - Sole purpose is to make sure that the user is in the proper state. If they already started their shift, then it will lock the CLOCK IN button and enable the CLOCK OUT button and vice-versa. (TL;DR Check session state.)

populateTimeCard() - Populates the template with datas in the timeCardDatas (localStorage). This is done using Template-Engine.

login() - returns true if the user types in the correct password. This is done by passing the input through the md5(input) function. Afterwards compares it to the MD5 Hash that is hardcoded.

reset() - Simply removes the startShift. This is triggered by pressing the Clock Out button.

calculateHours(single) - Takes param single. This is for calculating single values instead of an entire array. Ex: (mill1 - mill2) => 50hr. General purpose is to add up all the time and return that.

markTime() - Gets the current time and marks it as start or end of shift depending on user session state.

Utility Functions:

militaryToStandard(time) - Converts military time to standard time. dowToWord(dow) - Turns time.getDay() to word. time.getDay() returns day of the week as int.

padZero(number) - As the function names says gives padding zeroes for number less than 9 (1 digit numbers).

constructTime(withDOW) - Constructs date in a clear format. (MM / DD / YYYY [DAY_OF_THE_WEEK]). Takes a param that either adds in Day of the Week or not.


Codes


index.html

<!DOCTYPE html>

<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <title>Time Card</title>

        <link rel="stylesheet" type="text/css" href="css/main.css"/>
        <link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>

        <style>
            html, body {
                margin : 0;
                height: 100%;
            }

            .container-fluid {
                height: 100%;
            }

            .row {
                height: 100%;
            }

            .box-header {
                background-color: #6C7E8F;
                text-align: center;
                padding: 8px;
                color: #FFF;
            }

        </style>
    </head>

    <body style="background-color: #2c3e50;">
        <div class="container-fluid">
            <div class="row">
                <div class="col-sm-8" style="padding: 0; border-right: 1px solid black; height: 100%;">
                    <div class="card" style="margin: 16px; height: 95%; ">
                        <h3 class="card-header">Interface</h3>
                        <div class="card-block">
                            <h4 class="card-title">Instructions</h4>
                            <p class="card-text">Press the <b>Clock In</b> button when starting shift. This will get the current time and add it in your <i>Time Card</i>.</p>
                            <p class="card-text">Press the <b>Clock Out</b> button when ending shift. This will get the current time and add it in your <i>Time Card</i>.</p>

                            <!-- <hr>
                            <h4>Datas</h4>
                            <p>This is just showing all the datas available. Will be removed after final build. [For development purposes only.]</p>
                            <br>
                            <b style="color: blue">Date </b> <span id="clockInDate">N/A</span><br>
                            <b style="color: green">Clock In </b> <span id="clockInData">N/A</span><br>
                            <b style="color: red">Clock Out </b> <span id="clockOutData">N/A</span> -->
                            <hr>

                            <h4>Password</h4>
                            <p>Please enter your password before pressing either <b>Clock In</b> or <b>Clock Out</b>.
                            <div class="form-group" id="password-group">
                                <input class="form-control" type="password" id="password" required/>
                            </div>
                        </div>
                        <div class="card-footer text-muted">
                            <button id="clockIn" class="btn btn-primary btn-block">Clock In</button>
                            <button id="clockOut" class="btn btn-danger btn-block">Clock Out</button>
                        </div>
                    </div>
                </div>

                <div class="col-sm-4" style="background-color: #34495e; padding: 0;">
                    <!-- <div class="header-container">
                        <h4 class="box-header">Time Card</h4>
                    </div>
                    <div class="datas-container">
                        <span id="time"></span>
                    </div> -->
                    <div class="card" style="margin: 16px; height: 95%; ">
                        <h3 class="card-header">Time Card</h3>
                        <div class="card-block" style="padding: 0">
                            <div id="accordion" role="tablist" aria-multiselectable="true">
                            </div>
                        </div>
                        <div class="card-footer">
                            <b>Total Hours: </b> <span id="totalHours"></span>
                        </div>
                    </div>
                        <!-- <div class="card-footer text-muted">
                            <button id="clockIn" class="btn btn-primary btn-block">Clock In</button>
                            <button id="clockOut" class="btn btn-danger btn-block">Clock Out</button>
                        </div> -->
                </div>
            </div>
        </div>
        <script src="js/jquery.min.js"></script>
        <script src="js/tether.js"></script>
        <script src="js/bootstrap.js"></script>
        <script src="js/en.js"></script>
        <script src="js/tmpl.min.js"></script>
        <script src="js/timecard.js"></script>

        <!-- TEMPLATE -->
        <script type="text/x-tmpl" id="tmpl-demo">
            <div class="card">
                <div class="card-header" role="tab" id="heading{%= o.id %}">
                        <h5 class="{%= o.id %}">
                        <a class="collapsed" data-toggle="collapse" data-parent="#accordion" href="#collapse{%= o.id %}" aria-expanded="true" aria-controls="collapse{%= o.id %}">
                            {%= o.title %}
                        </a>
                        </h5>
                </div>

                <div id="collapse{%= o.id %}" class="collapse" role="tabpanel" aria-labelledby="heading{%= o.id %}">
                        <div class="card-block">
                            <b>Date: </b> {%= o.title %}<br>
                            <b style="color: green">Clock In: </b> {%= o.clockIn %}<br>
                            <b style="color: red">Clock Out: </b> {%= o.clockOut %}<br>
                            <hr>
                            <b style="color: blue">Total: </b> {%= o.total %}<br>
                        </div>
                </div>
            </div>
        </script>
        <script type="text/javascript">
            $(document).ready(function(){
                if (localStorage.getItem("timeCardDatas") != null) {
                    populateTimeCard();
                    calculateHours();
                }

                $("#totalHours").text(localStorage.getItem("totalHours"));
            });

            $("#clockIn").on("click", function(){
                if ($("#password").val().length > 0) {
                    if (login($("#password").val())) {
                        markTime("start");

                        $("#password").val("");
                        toggleClockIn();
                    } else {
                        triggerFormControl();
                    }
                } else {
                    triggerFormControl();
                }
            });

            $("#clockOut").on("click", function(){
                if ($("#password").val().length > 0) {
                    if (login($("#password").val())) {
                        markTime("end");

                        $("#password").val("");
                        toggleClockOut();

                        /*-- Populate Base --*/
                        populateTimeCard();
                    } else {
                        triggerFormControl();
                        console.log("[DEBUG] Triggered Form Control");
                    }
                } else {
                    triggerFormControl();
                    console.log("[DEBUG] Triggered Form Control");
                }
            });
        </script>
    </body>
</html>

timecard.js

/*
 * Time Card web application.
 * ------------------------------
 * This program is not secure as it's only JS at work.
 */
initialize();

function initialize() {
    if (localStorage.getItem("startShift") != null) {
        toggleClockIn();
    } else {
        toggleClockOut();
    }
}

function populateTimeCard() {
    var tcDatas = JSON.parse(localStorage.getItem("timeCardDatas"));
    for (var i = tcDatas.length - 1; i >= 0; i--) {
        if (i >= 2) {
            var datas = {
                "id": i,
                "title": tcDatas[i][0][2],
                "clockIn": tcDatas[i][0][0],
                "clockOut": tcDatas[i][1][0],
                "total": calculateHours(tcDatas[i][0][1] - tcDatas[i][1][1])
            };
            $("#accordion").append(tmpl("tmpl-demo", datas));
        }
    }
}

function login(password) {
    if (md5(password) == "<MD5 Hash Here>") {
        return true;
    } else {
        return false;
    }
}

function reset() {
    localStorage.removeItem("startShift");
}

function calculateHours(single) {
    var timeCardDatas = JSON.parse(localStorage.getItem("timeCardDatas")),
        totalHour = 0;

    single = (typeof single !== 'undefined') ? single : false;
    if (!single) {
        for (var i = timeCardDatas.length - 1; i >= 0; i--) {
            if (i >= 2) {
                var curHour = ((timeCardDatas[i][0][1] - timeCardDatas[i][1][1]) / 1000 / 60 / 60).toFixed(2);
                if (curHour < 0) {
                    totalHour += parseFloat((curHour * -1).toFixed(2));
                } else {
                    totalHour += parseFloat(curHour).toFixed(2);
                }
            }
        }

        localStorage.setItem("totalHours", totalHour.toFixed(2));
    } else {
        var tHour = ((parseFloat(single)) / 1000 / 60 / 60).toFixed(2);
        if (tHour < 0) {
            tHour = parseFloat((tHour * -1)).toFixed(2);
        } else {
            tHour = parseFloat(tHour).toFixed(2);
        }
        return tHour;
    }
}

function markTime(shift) {
    var curTime = new Date();
    var h = curTime.getHours();
    var m = curTime.getMinutes();
    var s = curTime.getSeconds();

    m = padZero(m);
    s = padZero(s);

    var shiftData = [militaryToStandard(h + ":" + m + ":" + s), curTime.getTime(), constructTime(true)];

    if (shift == "start") {
        localStorage.setItem("startShift", JSON.stringify(shiftData));
    } else if (shift == "end") {
        var tcDatas = [JSON.parse(localStorage.getItem("startShift")), shiftData];
        if (localStorage.getItem("timeCardDatas") == null) {
            var fillerDatas = [["FILLER", 0000000000], ["FILLER", 0000000000]];
            localStorage.setItem("timeCardDatas", JSON.stringify(fillerDatas));

            var oldItems = JSON.parse(localStorage.getItem("timeCardDatas"));

            oldItems.push(tcDatas);
            localStorage.setItem("timeCardDatas", JSON.stringify(oldItems));

            reset();
        } else {
            var oldItems = JSON.parse(localStorage.getItem("timeCardDatas"));

            oldItems.push(tcDatas);
            localStorage.setItem("timeCardDatas", JSON.stringify(oldItems));

            reset();
        }
    } else {
        console.log("[Time Card] An error has occured.");
    }
}

/*
 * Error Controls
 */

function triggerFormControl() {
    $("#password").addClass("form-control-danger");
    $("#password-group").addClass("has-danger");
}

function toggleClockIn() {
    $("#clockIn").addClass("disabled");
    $("#clockOut").removeClass("disabled");

    $('#clockIn').prop('disabled', true);
    $('#clockOut').prop('disabled', false);
}

function toggleClockOut() {
    $("#clockOut").addClass("disabled");
    $("#clockIn").removeClass("disabled");

    $('#clockIn').prop('disabled', false);
    $('#clockOut').prop('disabled', true);
}

/*
 * Utility
 */

function militaryToStandard(time) {
    time = time.split(':');

    var hours = Number(time[0]);
    var minutes = Number(time[1]);
    var seconds = Number(time[2]);

    var timeValue;

    if (hours > 0 && hours <= 12) {
      timeValue= "" + hours;
    } else if (hours > 12) {
      timeValue= "" + (hours - 12);
    } else if (hours == 0) {
      timeValue= "12";
    }

    timeValue += (minutes < 10) ? ":0" + minutes : ":" + minutes;
    timeValue += (seconds < 10) ? ":0" + seconds : ":" + seconds;
    timeValue += (hours >= 12) ? " P.M." : " A.M.";

    return timeValue;
}

function dowToWord(dow) {
    switch (dow) {
        case 0:
            return "Sunday";
            break
        case 1:
            return "Monday";
            break;
        case 2:
            return "Tuesday";
            break;
        case 3:
            return "Wednesday";
            break;
        case 4:
            return "Thursday";
            break;
        case 5:
            return "Friday";
            break;
        case 6:
            return "Saturday";
            break;
    }
}

function padZero(number) {
    if (number <= 9) {
        number = "0" + number;
    }

    return number;
}

function constructTime(withDOW) {
    var curTime = new Date();
    var construct = padZero((curTime.getMonth() + 1)) + "/" +
                    curTime.getDate() + "/" +
                    curTime.getFullYear();

    var a = (typeof withDOW !== 'undefined') ?  true : false;

    if (a) {
        construct += " " + dowToWord(curTime.getDay());
    }

    return construct;
}

JS Libraries


Libraries that I installed.


Answer(s) I'm Looking For


  • Code Improvements
  • General Tips
  • References to Guides (Naming Convention, etc.)
  • AND any other meaningful and useful stuff.


If I am missing any other information do let me know what other information you need down in the comments. I will be sure to answer than as fast as I can.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Unnecessary loop cycles

In this loop in populateTimeCard:

for (var i = tcDatas.length - 1; i >= 0; i--) {
    if (i >= 2) {
        // ...
    }
}

It would be better to change the loop condition and remove the if statement from the loop body, like this:

for (var i = tcDatas.length - 1; i >= 2; i--) {

Review the other similar loops as well.

Use boolean expressions directly

Instead of this:

if (md5(password) == "<MD5 Hash Here>") {
    return true;
} else {
    return false;
}

You can write simply:

return md5(password) == "<MD5 Hash Here>";

Bug or feature?

The two branches of this condition look a bit suspicious:

if (curHour < 0) {
    totalHour += parseFloat((curHour * -1).toFixed(2));
} else {
    totalHour += parseFloat(curHour).toFixed(2);
}

Did you misplace the parentheses in the if branch? Perhaps you meant to write this:

if (curHour < 0) {
    totalHour += parseFloat(-curHour).toFixed(2);
} else {
    totalHour += parseFloat(curHour).toFixed(2);
}

In this case I would suggest to not repeat yourself, as totalHour += parseFloat(...).toFixed(2); is the same in both branches. You could write:

totalHour += parseFloat(Math.abs(curHour)).toFixed(2);

Notice that when duplicate logic is eliminated, it's impossible to misplace parentheses. This is one of the reasons why it's good to eliminate duplications always.

Don't repeat yourself

The parseFloat(...).toFixed(2) snippet appears in many places in the code. It would be good to encapsulate this logic in a helper function and eliminate the duplications.

Multiplying by -1

Instead of x * -1 you can write simply -x.

Returning from case in a switch

When you return from a case in a switch, you don't need to add a break statement.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.