<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="">
<style>
.calc-wrapper,
.calc-wrapper * {
margin: 0;
padding: 0;
}
.calc-wrapper {
width: 150px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 10px;
background: #eee;
}
.calc-display div {
font-family: Helvetica, Arial, sans-serif;
font-size: 14px;
text-align: right;
padding: 3px 5px;
margin: 5px 0 10px;
border: 1px solid #ddd;
border-radius: 8px;
background: #fff;
}
.calc-wrapper table {
width: 150px;
}
.calc-wrapper table {
width: 150px;
}
.calc-wrapper table td {
padding: 2px 1px;
}
.calc-wrapper table button {
width: 100%;
padding: 3px 0;
}
</style>
<script>
(function () {
let operatorButtons,
digitButtons,
numberIndex = 0,
expr = "", //"( 2 + 3 ) * 2",
display,
/*
* Update display
*/
updateDisplay = function (output) {
display.innerHTML = output;
},
/*
* Get priority of an operation
*/
operatorPriority = function (operator) {
switch (operator) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
}
},
/*
* Convert an expression (e.g. 9 - 3 * 2) in UPN (umgekehrt polnische Notation).
*
* @params
* - tokens - (Array) contains numbers, operators, functions in expression-order (e.g. [9, "-", 3, "*", 2])
*
* @return
* upn_output - (Array) representation of the input expression as UPN array (e.g. [9, 3, 2, "*", "-"])
*/
exprToUPN = function (tokens) {
let upn_output = [],
stack = [];
tokens.forEach(function (token, index) {
// if token is a number
if (!isNaN(token)) upn_output.push(token);
// if token is a function
else if (/sin|cos|ln/.test(token)) stack.push(token);
// if token is an operator
else if (/^\+|\-|\*|\/$/.test(token)) {
while (
stack.length > 0 &&
/^\+|\-|\*|\/$/.test(stack[stack.length - 1]) &&
operatorPriority(token) <= operatorPriority(stack[stack.length - 1])
) {
upn_output.push(stack.pop());
}
// token to stack
stack.push(token);
}
// token is opening bracket
else if (/^\($/.test(token)) stack.push(token);
// token is closing bracket
else if (/^\)$/.test(token)) {
while (!/^\($/.test(stack[stack.length - 1])) {
if (stack.length === 0) {
updateDisplay('err');
return [];
}
upn_output.push(stack.pop());
}
// remove opening bracket from stack
stack.pop();
// if token is a function
if (/sin|cos|ln/.test(stack[stack.length - 1])) upn_output.push(stack[stack.length - 1]);
}
});
while (stack.length > 0) upn_output.push(stack.pop());
console.log("exprToUPN: ", stack, upn_output);
return upn_output;
},
/*
* Calculates an expression
*/
calculate = function (leftOp, rightOp, op) {
switch (op) {
case "+":
return leftOp + rightOp;
case "-":
return leftOp - rightOp;
case "*":
return leftOp * rightOp;
case "/":
return leftOp / rightOp;
}
},
/*
* Computes the current expression and display the result
*/
compute = function () {
// extract tokens from the current expression
const tokens = expr.trim().replace(/ /g, " ").split(" ").map(function (token) {
return isNaN(token) ? token : Number(token);
});
// convert expression to upn
let upn = exprToUPN(tokens),
stack = [],
leftOp,
rightOp,
result = 0;
// if a valid upn array exists
if (upn.length) {
// iterate over upn array
upn.forEach(function (arg, index) {
// is number
if (!isNaN(arg)) {
stack.push(arg);
// is operator
} else if (/^\+|\-|\*|\/$/.test(arg)) {
rightOp = stack.pop();
leftOp = stack.pop();
stack.push(calculate(leftOp, rightOp, arg));
}
});
// result is still on top of stack
result = stack.pop();
}
return result;
},
/*
* Resets the calculator
*/
resetCalculator = function () {
expr = "";
updateDisplay("0");
},
/*
* Add a digit to the expression string
*/
addDigit = function (digit) {
expr += digit;
updateDisplay(expr);
},
/*
* Add an operator to the expression string and separates it by a space character.
*/
addOperator = function (op) {
expr += ` ${op} `;
updateDisplay(expr);
};
window.addEventListener('load', function () {
display = document.querySelector("#calc-display div");
operatorButtons = document.querySelectorAll("button[data-operator]");
digitButtons = document.querySelectorAll("button[data-digit]");
/*
* operator buttons
*/
operatorButtons.forEach(function (button) {
button.addEventListener('click', function (evt) {
addOperator(evt.target.dataset.operator);
});
});
/*
* digit buttons
*/
digitButtons.forEach(function (button) {
button.addEventListener('click', function (evt) {
addDigit(evt.target.dataset.digit);
});
});
/*
* decimal button
*/
document.querySelector("button[data-decimal]").addEventListener('click', function (evt) {
addDigit(evt.target.dataset.decimal);
});
/*
* result button
*/
document.querySelector("button[data-result]").addEventListener('click', function () {
// compute expression on display
const result = compute();
// start/init expression string with result
expr = result;
// display result
updateDisplay(result);
});
/*
* reset button
*/
document.querySelector("button[data-reset]").addEventListener('click', function () {
resetCalculator();
});
});
})();
</script>
</head>
<body>
<div class="calc-wrapper" id="calculator">
<div id="calc-display" class="calc-display">
<div>0</div>
</div>
<table id="calc-panel" class="calc-panel">
<tbody>
<tr>
<td></td>
<td><button data-operator="(">(</button></td>
<td><button data-operator=")">)</button></td>
<td><button data-reset="CE">CE</button></td>
</tr>
<tr>
<td><button data-digit="7">7</button></td>
<td><button data-digit="8">8</button></td>
<td><button data-digit="9">9</button></td>
<td><button data-operator="/">/</button></td>
</tr>
<tr>
<td><button data-digit="4">4</button></td>
<td><button data-digit="5">5</button></td>
<td><button data-digit="6">6</button></td>
<td><button data-operator="*">x</button></td>
</tr>
<tr>
<td><button data-digit="1">1</button></td>
<td><button data-digit="2">2</button></td>
<td><button data-digit="3">3</button></td>
<td><button data-operator="-">-</button></td>
</tr>
<tr>
<td><button data-digit="0">0</button></td>
<td><button data-decimal=".">.</button></td>
<td><button data-result="=">=</button></td>
<td><button data-operator="+">+</button></td>
</tr>
</tbody>
</table>
</div>
<script>
console.log(document.getElementById("calc-panel"));
</script>
</body>
</html>