Skip to content

Instantly share code, notes, and snippets.

Created March 8, 2016 15:31
Show Gist options
  • Save anonymous/047fd9ae1fe6c281feeb to your computer and use it in GitHub Desktop.
Save anonymous/047fd9ae1fe6c281feeb to your computer and use it in GitHub Desktop.
Calculator
<div id="container">
<main>
<div id="display">123456789012345</div>
<div id="keypad">
<div class="key-row">
<div id="key-1">1</div>
<div id="key-2">2</div>
<div id="key-3">3</div>
<div id="key-div">/</div>
</div>
<div class="key-row">
<div id="key-4">4</div>
<div id="key-5">5</div>
<div id="key-6">6</div>
<div id="key-mul">*</div>
</div>
<div class="key-row">
<div id="key-7">7</div>
<div id="key-8">8</div>
<div id="key-9">9</div>
<div id="key-sub">-</div>
</div>
<div class="key-row">
<div id="key-0">0</div>
<div id="key-dec">.</div>
<div id="key-neg">&#x00b1;</div>
<div id="key-add">+</div>
</div>
<div class="key-row">
<div id="key-clr" class="span-2">clear</div>
<div id="key-bksp">&#x232b;</div>
<div id="key-eq">=</div>
</div>
</div>
</main>
</div>
class BinOp {
constructor(op, prec, fn) {
this.op = op;
this.precedence = prec;
this.fn = fn;
}
show() {
return `[op: ${this.op}]`;
}
eval(stack) {
let right = stack.pop();
let left = stack.pop();
try {
stack.push(this.fn(left, right));
} catch (e) {
console.log(e);
stack.push(+"nope");
}
}
}
class Num {
constructor(n) {
this.n = n;
}
show() {
return this.n;
}
eval(stack) {
stack.push(this.n);
}
}
const OPS = {
[Symbol.for('+')]: new BinOp('+', 1, (a, b) => a + b),
[Symbol.for('-')]: new BinOp('-', 1, (a, b) => a - b),
[Symbol.for('*')]: new BinOp('*', 2, (a, b) => a * b),
[Symbol.for('/')]: new BinOp('/', 2, (a, b) => a / b)
}
class Calculator {
constructor() {
this.buffer = [];
this.ops = [];
this.machine = [];
}
get digits() {
return this.buffer.join('');
}
flush() {
if (this.pending) {
this.pushOperand();
this.pushOperator(this.pending);
this.pending = null;
}
}
pushOperand() {
if (this.buffer.length) {
this.machine.push(new Num(+this.digits));
this.buffer = [];
}
}
pushOperator(operator) {
let op = OPS[Symbol.for(operator)];
let n = 0, i = this.ops.length - 1;
for (; i >= 0; i--) {
let prev = this.ops[i];
if (op.precedence <= prev.precedence) {
this.machine.push(prev);
n++;
} else {
break;
}
}
this.ops.splice(i + 1, n);
this.ops.push(op);
}
calculate() {
for (let o of this.ops.reverse()) {
this.machine.push(o);
}
let output = [];
for (let e of this.machine) {
console.log(e.show());
e.eval(output);
}
let answer = output.pop();
this.machine = [];
this.ops = [];
this.buffer = ('' + answer).split('');
this.evaluated = true;
return answer;
}
}
jQuery(function () {
const $ = jQuery;
let model = new Calculator;
const view = {
$: $('#display'),
show(n) {
this.$.text(n);
},
clear() {
this.show('0');
this.deselect();
},
update() {
this.show(model.digits || '0');
},
select(node) {
this.deselect();
this.$highlighted = $(node).addClass('active');
},
deselect() {
if (this.$highlighted) {
this.$highlighted.removeClass('active');
this.$highlighted = null;
}
}
}
const digit = n => _ => {
if (n === 0 && !model.buffer.length) {
return;
}
if (model.evaluated) {
model = new Calculator;
} else {
model.flush();
}
model.buffer.push(n);
view.update();
}
const operator = op => e => {
view.select(e.target);
model.pending = op;
model.evaluated = null;
}
const controller = {
'key-0': digit(0),
'key-1': digit(1),
'key-2': digit(2),
'key-3': digit(3),
'key-4': digit(4),
'key-5': digit(5),
'key-6': digit(6),
'key-7': digit(7),
'key-8': digit(8),
'key-9': digit(9),
'key-add': operator('+'),
'key-sub': operator('-'),
'key-mul': operator('*'),
'key-div': operator('/'),
['key-clr']() {
model = new Calculator;
view.clear();
},
['key-bksp']() {
if (model.buffer.length) {
model.buffer.pop();
model.evaluated = null;
view.update();
}
},
['key-dec']() {
if (model.buffer.indexOf('.') === -1) {
model.flush();
model.buffer.push('.');
model.evaluated = null;
view.update();
}
},
['key-neg']() {
if (!model.buffer.length || model.buffer[0] !== '-') {
model.buffer.unshift('-');
} else {
model.buffer.shift();
}
model.evaluated = null;
view.update();
},
['key-eq']() {
model.pushOperand();
model.calculate();
view.deselect();
view.update();
}
}
$('#container').on('click', '.key-row > div', e => {
let handler = controller[e.target.id];
handler && handler(e);
});
view.clear();
});
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
body {
padding: 2em;
}
#container {
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-content: center;
align-items: center;
}
main {
border: 1px solid #333;
flex: 0 1 auto;
background: #dedede;
padding: 0.5em;
}
#keypad {
}
#display {
font-size: 2em;
line-height: 1;
background: #fff;
border: 1px solid #a0a0a0;
margin: 0.25em;
padding: 0.5em;
text-align: right;
width: 12em;
font-family: monospace;
overflow-wrap: word-wrap;
word-break: break-all;
}
.key-row {
display: flex;
flex-flow: row no-wrap;
}
.key-row > div {
border: 1px solid #a0a0a0;
background: #fff;
padding: 1em;
margin: 0.5em;
flex: 1 1 auto;
text-align: center;
cursor: pointer;
font-family: monospace;
}
.key-row > div:hover {
background: #c0c0c0;
}
.key-row > div:active {
background: #808080;
}
.key-row > div.active {
background: #80e080;
}
.key-row .span-2 {
flex-grow: 3;
flex-shrink: 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment