calc
by sperske
HTML
<div class="calc">
<div>
<button class="clear">C</button>
<input name="display" readonly="readonly" placeholder="enter some numbers" />
<button class="input" data-value="7">7</button>
<button class="input" data-value="8">8</button>
<button class="input" data-value="9">9</button>
<button class="operation" data-value="/">/</button>
<button class="input" data-value="4">4</button>
<button class="input" data-value="5">5</button>
<button class="input" data-value="6">6</button>
<button class="operation" data-value="*">×</button>
<button class="input" data-value="1">1</button>
<button class="input" data-value="2">2</button>
<button class="input" data-value="3">3</button>
<button class="operation" data-value="+">+</button>
<button class="input" data-value="0">0</button>
<button class="input" data-value=".">.</button>
<button class="equals">=</button>
<button class="operation" data-value="-">-</button>
</div>
</div>
CSS
body {
padding: 0;
margin: 0;
}
.calc {
height: 100%;
min-width: 350px;
background-color: #8dcfed;
padding: 10px;
}
.calc>div {
display: grid;
row-gap: 10px;
column-gap: 5px;
grid-template-rows: repeat(4, 1fr);
grid-template-columns: repeat(4, 1fr);
}
.calc input,
.calc button {
font-family: Helvetica, "Helvetica Neue";
font-size: 28px;
font-weight: bold;
}
.calc input {
grid-column: span 3;
padding-right: 15px;
text-align: right;
background-color: #78a7bb;
border: none;
box-shadow: inset 0 8px #618698;
color: #fff;
border-radius: 8px;
margin-bottom: -8px;
}
.calc button {
color: #81898e;
background-color: #fff;
width: 100%;
height: 68px;
border-radius: 8px;
border: none;
box-shadow: 0 8px #78a7bb;
}
.calc button:active {
transform: translateY(4px);
box-shadow: 0 4px #78a7bb;
}
.calc button.clear:active {
box-shadow: 0 4px #ff7b83;
}
.calc button.operation:active {
box-shadow: 0 4px #78a7bb;
}
.calc button.equals:active {
box-shadow: 0 4px #9cab44;
}
.calc button.clear {
background-color: #ff9fa7;
box-shadow: 0 8px #ff7b83;
color: #fff;
}
.calc button.equals {
background-color: #f1ff8b;
box-shadow: 0 8px #9cab44;
color: #81898e;
}
.calc button.operation {
background-color: #fff0f5;
box-shadow: 0 8px #78a7bb;
color: #81898e;
}
JavaScript
const re = /([+-/*//])?(\d*\.*(\d*))*/g;
const display = $("input[name=display]");
let allow_op = true;
let allow_decimal = false;
const ops = {
"+"(a, b) {
return a + b;
},
"-"(a, b) {
return a - b;
},
"*"(a, b) {
return a * b;
},
"/"(a, b) {
return a / b;
}
};
function calc(s) {
let sum = 0;
let m;
while ((m = re.exec(s)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
const op = m[1] || "+";
const value = parseFloat(m[2]);
if (!isNaN(value) && ops[op]) {
sum = ops[op](sum, value);
}
}
return sum;
}
/***
* Takes an input and tests if it should
* be allowed based on what has been typed before.
* TODO(sperske): currently allows too many decimals
* @param{string} input to parse
* @returns boolean if allowed
*/
function allowInput(input) {
let allow = true;
switch (input) {
case ".":
allow = allow_decimal;
allow_decimal = false;
allow_op = false;
break;
case "0":
allow_decimal = true;
allow_op = true;
break;
case "-":
case "+":
allow = allow_op;
allow_decimal = false;
allow_op = false;
break;
default:
allow_decimal = true;
allow_op = true;
}
return allow;
}
$("button.clear").on("click", (el) => {
display.val("");
allow_op = true;
allow_decimal = false;
});
$("button.input, button.operation").on("click", (el) => {
const button = $(el.target);
const value = button.data("value");
if (allowInput(value)) {
display.val(display.val() + button.data("value"));
}
});
$("button.equals").on("click", (el) => {
display.val(calc(display.val()));
allow_op = true;
allow_decimal = false;
});