Calculator App Help - Factorial and Sqrt implementation

I’d like to create this as my first “app”: a calculator. I’m sure 99% of devs started this as their first JS project.

Here’s the codebase:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DynamoDevX.net</title>
    <link rel="icon" href="favicon.png">
    <link rel="stylesheet" href="../../main.css">
    <style>
        @font-face {
            font-family: 'BlenderFont';
            src: url('../../bfont.woff') format('woff');
        }
        input {
            border: 5px solid rgb(255, 0, 85);
            border-radius: 0px;
            background-color: rgb(0, 247, 255);
            font: 'BlenderFont';
            size: 200px;
        }
        #calc-container {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 5px;
            max-width: 500px;
            margin: 0 auto;
        }

        #calc-container button {
            width: 100%;
            height: 80px;
            font-size: 20px;
            margin: 0;
        }

        #calc{
          margin-bottom: 10px;
        }
        #calc #display{
          width: 100%;
          margin-bottom: 10px;
          font-size: 24px;
          padding: 10px;
          text-align: center;
        }
        #Calc_buttons{
            width: fit-content;
        }

        p {
            font-size: 15px;
            color: rgb(255, 0, 85);
            text-align: left;
        }
        h6 {
            font-size: 10px;
            text-align: left;
            color:rgba(255, 0, 85, 0.096)
        }
        #quad_form:hover {
            background-color: rgb(0, 0, 0);
            color: rgb(255, 255, 255);
        }
    </style>
</head>
<body>
    <header>
        <i><h1>DinamoDevX.</h1></i>
        <a href="../../main.html"><button>Main</button></a> <a href="../../apps.html"><button>Recent tab</button></a>
    </header>
  
    <div id="Calc_buttons">
        <div id="calc">
            <input type="text" id="display" disabled>
        </div>
        <div id="calc-container">
            <button onclick="DELETE_THAAAT()">C</button>
            <button onclick="calculate()">=</button>
            <button onclick="quadratic_formula_jumpscare()" style="color: rgba(255, 127, 80, 0.05); background-color: rgba(255, 127, 80, 0.01); border:rgba(255, 127, 80, 0.01)" id="quad_form" >Scare me!</button>
            <button onclick="delete_last_char()"> &larr; </button>
            
            <button onclick="appendOper()">sqrt</button>
            <button onclick="appendOper()">^</button>
            <button onclick="appendOper()">(</button>
            <button onclick="appendOper()">)</button>

            <button onclick="appendNum()">7</button>
            <button onclick="appendNum()">8</button>
            <button onclick="appendNum()">9</button>
            <button onclick="appendOper()">+</button>

            <button onclick="appendNum()">6</button>
            <button onclick="appendNum()">5</button>
            <button onclick="appendNum()">4</button>
            <button onclick="appendOper()">-</button>

            <button onclick="appendNum()">3</button>
            <button onclick="appendNum()">2</button>
            <button onclick="appendNum()">1</button>
            <button onclick="appendOper()">*</button>

            <button onclick="appendNum()">0</button>
            <button onclick="divideBy100()">%</button>
            <button onclick="appendNum()">.</button>
            <button onclick="appendOper()">/</button>

            <button onclick="appendConst()">e</button>
            <button onclick="appendConst()">pi</button>
            <button onclick="appendConst()">g (Earth)</button>
            <button onclick="appendConst()">c</button>

            <button onclick="doSomethingSpecial(event)">Round</button>
            <button onclick="doSomethingSpecial(event)">!</button>
            <button onclick="doSomethingSpecial(event)">i</button>
            <button onclick="doSomethingSpecial(event)">say hi</button>

            <button onclick="toggleFreewriting()">Freewrite</button>
        </div>
    </div>

    <script>
        let display = document.getElementById('display');
        let expression = '';
        let freewritingEnabled = false;

        function getLastOperand(expr) {
            let i = expr.length - 1;
            let operand = '';
            let parenCount = 0;
            while (i >= 0) {
                const char = expr[i];
                if (char == ')') {
                    parenCount++;
                } else if (char == '(') {
                    parenCount--;
                }
                if (parenCount < 0) {
                    return { operand: null, startIndex: -1, endIndex: -1 };
                }
                if (parenCount == 0 && (isNumeric(char) || char == ')')) {
                    let endIndex = i;
                    while (i >= 0) {
                        const c = expr[i];
                        if (isNumeric(c) || c == '(') {
                            i--;
                        } else {
                            break;
                        }
                    }
                    return { operand: expr.substring(i + 1, endIndex + 1), startIndex: i + 1, endIndex };
                } else {
                    i--;
                }
            }
            return { operand: null, startIndex: -1, endIndex: -1 };
        }

        function toggleFreewriting() {
            freewritingEnabled = !freewritingEnabled;
            display.disabled = !freewritingEnabled;
            if (freewritingEnabled) {
                display.focus();
                display.addEventListener('input', function () {
                    expression = display.value;
                });
            } else {
                display.value = expression;
            }
        }

        function appendNum() {
            expression += event.target.innerText;
            display.value = expression;
        }

        function appendConst() {
            if (event.target.innerText == 'e') {
                expression += '2.718281';
            } else if (event.target.innerText == 'pi') {
                expression += '3.141592';
            } else if (event.target.innerText == 'g (Earth)') {
                expression += '9.80665';
            } else if (event.target.innerText == 'c') {
                expression += '299792458';
            }
            display.value = expression;
        }

        function appendOper() {
            const targetText = event.target.innerText;
            if (targetText === 'Math.sqrt' || targetText === 'factorial') {
                expression += targetText + '(';
            } else {
                expression += targetText;
            }
            display.value = expression;
        }

        function safeEval(expr) {
            if (/^[0-9+\-*/().\s^!sqrtfactorial]+$/.test(expr)) {
                let modifiedExpr = expr.replace(/factorial$([^)]*)$/g, 'factorial($1)')
                    .replace(/sqrt$([^)]*)$/g, 'Math.sqrt($1)');
                return eval(modifiedExpr);
            } else {
                throw new Error('Invalid characters in expression');
            }
        }

        function calculate() {
            try {
                let result = safeEval(expression);
                let finalResult = result.toString();
                const factorialMatches = expression.match(/factorial$([^)]+)$/g);
                if (factorialMatches) {
                    factorialMatches.forEach(match => {
                        const num = match.match(/$([^)]+)$/)[1];
                        const factResult = factorial(safeEval(num));
                        finalResult = finalResult.replace(match, factResult);
                    });
                }
                const sqrtMatches = expression.match(/sqrt$([^)]+)$/g);
                if (sqrtMatches) {
                    sqrtMatches.forEach(match => {
                        const num = match.match(/$([^)]+)$/)[1];
                        const sqrtResult = Math.sqrt(safeEval(num));
                        finalResult = finalResult.replace(match, sqrtResult);
                    });
                }

                display.value = finalResult;
                expression = finalResult;
            } catch (error) {
                display.value = "Error: " + error.message;
                expression = '';
            }
        }
        function DELETE_THAAAT() {
            display.value = '';
            expression = '';
        }

        function divideBy100() {
            display.value = eval(expression) / 100;
            expression = display.value;
        }

        function delete_last_char() {
            display.value = display.value.slice(0, -1);
            expression = display.value;
        }

        function quadratic_formula_jumpscare() {
            display.value = '(-b+sqrt(b**2-4ac))/2a';
            expression = '';
        }
        function factorial(n) {
            if (n === 0 || n === 1) return 1;
            return n * factorial(n - 1);
        }
        function doSomethingSpecial(event) {
            const targetText = event.target.innerText;
            const display = document.getElementById('display');
            let currentExpression = display.value;

            if (targetText === 'factorial') {
                const last = getLastOperand(currentExpression);
                if (last.operand) {
                    expression = expression.slice(0, last.startIndex) + `factorial(${last.operand})!` + expression.slice(last.endIndex + 1);
                    display.value = expression;
                } else {
                    display.value = "Error: Invalid input for factorial.";
                }
            } else if (targetText === 'sqrt') {
                const last = getLastOperand(currentExpression);
                if (last.operand) {
                    expression = expression.slice(0, last.startIndex) + `sqrt(${last.operand})` + expression.slice(last.endIndex + 1);
                    display.value = expression;
                } else {
                    display.value = "Error: Invalid input for square root.";
                }
            } else if (targetText === 'Round') {
                const num = Number(currentExpression);
                if (!isNaN(num)) {
                    const decimalPlaces = prompt("Enter the number of decimal places to round to:");
                    if (decimalPlaces !== null && !isNaN(decimalPlaces)) {
                        const roundedNum = num.toFixed(parseInt(decimalPlaces));
                        display.value = roundedNum;
                    }
                } else {
                    display.value = "Error: Please enter a valid number.";
                }
            } else if (targetText === 'i') {
                display.value = "the amount of bitches you have";
            } else if (targetText === 'say hi') {
                display.value = 'hai!1!!11!!1!!1!!1';
            }
        }
    </script>
    <footer>
        <p>Website created by DinamoDevX. CC0 Licensed. Feel free to use and modify as you wish.</p>
        <p> All assets sourced from freedom-focused licenses (MIT, Creative Commons, Apache, etc.). &copy; 2025</p>
        <h6> please js devs fork and fix this shitty javascript i cant take this anymore</h6>
    </footer>
</body>
</html>

So, my question is: How do I implement the Square Root and Factorial features? I want so (like in any calc) they affect only the rightmost number/sqrt(expression in parentheses) and not the whole input field. I think I can use slicing and stuff, that’s simple for 1 digited numbers, but what about numbers with multiple digits and whole expressions?
I want the sqrt and factorials to be displayed in the input tag, but the safeEval() will not just mindlessly evaluate them, (that’ll give an error as “sqrt” and “factorial” are strings), it’ll recognise them as instructions to call Math.sqrt() and factorial() functions or something. Have no idea how to do that though.

Huge thanks and respect in advance.

Hey, I do not see it in your code but will require another function with Math.sqrt(): BTW I googled if this was a good first app to start JS and it was, also a TO-DO-LIST using react seems to be a good one. I think a calculator doesn’t always use strings very well try parseInt or double.parseInt to turn them into values. Also if the error is telling you to call these methods then maybe work on that. Good luck

1 Like