1
\$\begingroup\$

I have re-write a new version of Javascript Simple Calculator. Any thoughts in regards to this new design and how can it be improved further? One of my thoughts are to create a Javascript object for "Calculator" and move all the relevant property into it.

"use strict";

var lastNumberString, isFirstZero, lastInputIsOperator;
// 12. lastNumberString is 12.
// 12+3. lastNumberString is 3.
// lastNumberString will be reset to empty each time operator is pressed.
// This is one reason of having lastInputIsOperator flag.

// lastNumberString is to:
// - block more than one decimal.
// - set the first zero.

// isFirstZero is flags to detect if lastNumberString has the first zero.
// true: has first zero.
// If first zero exists, and key pressed is zero or number, 
// replace (remove char and insert) will be performed.

var expStr = document.getElementById('lblExpressionString');
// Any changes in outputArray will be synced to expStr
// for display. This is the UI layer.

var result = document.getElementById('lblResult');
// Each time a equation key in pressed, result will be shown after math evaluation.
// This is the UI layer.

// For single line calculation design, this element shall be removed.
// expStr will overwrite will final result

var outputArray = [];
// Each time a key is pressed, a char will pushed into this array
// If replace operation is needed, last pushed char will be popped
// This implements stack FIFO for 0(1) performance.
// outputArray data has to be synced to expStr but hidden.
// For easier debugging, this is can displayed.

const init = () => {
    lastNumberString = '';

    outputArray = [];
    expStr.innerText = '';
    result.innerText = '0';

    lastInputIsOperator = true;
    isFirstZero = false; // False means first zero does not exist in lastNumberString yet.
}

const appendExp = s => {
    lastNumberString += s;
    outputArray.push(s);
}

const replaceLastChar = s => {
    // Does not required to update lastNumberString because 
    // subsequent checking is based on isFirstZero flag.

    // Replace only happen in two condition.
    // 0 -> 1
    // + -> -
    outputArray.pop();
    outputArray.push(s);
}

const displayExpressionString = () => {
    expStr.innerText = outputArray.join('');
}

init();

$('#ac').click(function () {
    init();
})

$('#back').click(function () {
    if (outputArray.length === 0) {
        return;
    }

    // 10 -> 1
    outputArray.pop();

    displayExpressionString();

    if (outputArray.length === 0) {
        init();
        return;
    }

    // Peek last char
    const lastChar = outputArray[outputArray.length - 1];

    // 1
    console.log(lastChar);

    // If last char after pop is a number or decimal
    // Add more condition if there are more key like %
    if (Number.isInteger(parseInt(lastChar)) || lastChar === '.')
        lastInputIsOperator = false;
    else
        lastInputIsOperator = true;
})

$('#decimal').click(function (item) {
    if (lastNumberString.includes('.')) {
        return;
    }

    if (lastNumberString === '') {
        // '' -> 0.
        appendExp('0.');
    } else {
        // 1 -> 1.
        appendExp(item.target.textContent);
    }
    displayExpressionString();

    lastInputIsOperator = false;
    isFirstZero = false;
})

$('.number').click(function (item) {
    debugger
    if (lastNumberString === '') {
        // '' -> 1
        appendExp(item.target.textContent);

        // '' -> 0
        if (item.target.textContent === '0')
            isFirstZero = true;
    } else if (isFirstZero) {
        if (item.target.textContent !== '0') {
            // 0 -> 1
            debugger
            replaceLastChar(item.target.textContent);
            isFirstZero = false;
        }
    } else {
        // 1 -> 10
        appendExp(item.target.textContent);
    }

    displayExpressionString();
    lastInputIsOperator = false;
})

$('.operator').click(function (item) {
    if (outputArray.length === 0) {
        return;
    }

    if (lastInputIsOperator) {
        // 1+ -> 1-
        replaceLastChar(item.target.textContent);
    } else {
        // 1 -> 1+
        appendExp(item.target.textContent);
    }
    lastInputIsOperator = true;
    displayExpressionString();

    // Reset to allow first zero again.
    lastNumberString = '';
    isFirstZero = false;
})

const evaluateExp = () => {
    // Result is stored in the textbox
    // The expression is stored in the textbox. Expression ie 1+2
    if (lastInputIsOperator && outputArray.length > 0) {
        outputArray.pop();
        displayExpressionString();
    } else if (outputArray.length > 0) {
        expStr.innerText = expStr.innerText.replace('x', '*').replace('÷', '/');
        result.innerText = eval(expStr.innerText);
        const tempResult = result.innerText;

        // Reset. Similar as init()
        expStr.innerText = '';
        outputArray = [];
        lastNumberString = '';

        lastInputIsOperator = false;
        isFirstZero = true;

        // Result become last number string.
        lastNumberString = tempResult;
        for (const item of tempResult)
            outputArray.push(item);
    }
}
.containerCal {
    display: grid;
    justify-content: center;
    grid-template-columns: 80px 80px 80px 80px;
    grid-template-rows: 40px 80px;
    grid-template-areas: "header header header header" "header header header header" "main main main main" "main main main main" "main main main main" "main main main main" "main main main main";
    border: 1px solid black;
    max-width: 320px;
    margin: 0 auto;
    background-color: black;
    padding: 0px;
}

.item-header1 {
    grid-area: header;
    grid-row-start: 1;
    grid-row-end: 1;
    background-color: black;
    color: silver;
    text-align: right;
    font-size: 2em;
    padding: 10px;
}

.item-header2 {
    grid-area: header;
    grid-row-start: 2;
    grid-row-end: 2;
    background-color: black;
    color: white;
    text-align: right;
    font-size: 4em;
    padding: 10px;
}

.zero {
    grid-column-start: 1;
    grid-row-start: 7;
    grid-column: span 2;
}

.equal {
    grid-column-start: 4;
    grid-row-start: 6;
    grid-row-end: span 2;
}


/* Some hover effects */

.btn:hover {
    background-color: yellow;
    cursor: pointer;
    color: black;
    /*box-shadow: 0px 4px silver;*/
}

.btn {
    font-size: 150%;
    background-color: whitesmoke;
    border: none;
    color: black;
    padding: 20px;
    text-align: center;
    margin: 1px;
    border-radius: 1%;
    font-weight: bold;
}

.orange {
    background-color: orange;
}

.btnC {
    color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="containerCal">

        <div class="item item-header1" id='lblExpressionString'></div>
        <div class="item item-header2" id='lblResult'></div>

        <button type="button" id='ac' class='btn btnC'>C</button>
        <button type="button" value="" id='back' class='btn key'><<</button>

        <button type="button" value="x" class='operator btn key'>x</button>
        <button type="button" value="÷" class='operator btn key'>÷</button>

        <button type="button" value="9" class='number btn key'>9</button>
        <button type="button" value="8" class='number btn key'>8</button>
        <button type="button" value="7" class='number btn key'>7</button>
        <button type="button" value="-" class='operator btn key'>-</button>

        <button type="button" value="6" class='number btn key'>6</button>
        <button type="button" value="5" class='number btn key'>5</button>
        <button type="button" value="4" class='number btn key'>4</button>
        <button type="button" value="+" class='operator btn key'>+</button>

        <button type="button" value="3" class='number btn key'>3</button>
        <button type="button" value="2" class='number btn key'>2</button>
        <button type="button" value="1" class='number btn key'>1</button>
        <button type="button" onClick="evaluateExp()" class="btn orange equal">=</button>

        <button type="button" value="0" class='number zero btn key'>0</button>
        <button type="button" value="." id='decimal' class='btn key'>.</button>
    </div>

The source is extracted from my github repo - https://github.com/ngaisteve1/CalculatorJS

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.