<div class="calculator">
<div class="screen-wrapper">
<div id="screen">
<div id="expression"><span id="typed-cursor"/></div>
<div id="result">0</div>
</div>
</div>
<table class="keys">
<tbody>
<tr>
<td class="red key" data-func="clear">C</td>
<td class="red key" data-func="cancel_entry">CE</td>
<td class="gray key power" data-token="op_pow">x<span>y</span></td>
<td class="gray key divide" data-token="op_divide">÷</td>
</tr>
<tr>
<td class="blue key" data-token="num_7">7</td>
<td class="blue key" data-token="num_8">8</td>
<td class="blue key" data-token="num_9">9</td>
<td class="gray key" data-token="op_multiply">×</td>
</tr>
<tr>
<td class="blue key" data-token="num_4">4</td>
<td class="blue key" data-token="num_5">5</td>
<td class="blue key" data-token="num_6">6</td>
<td class="gray key substract" data-token="op_substract">–</td>
</tr>
<tr>
<td class="blue key" data-token="num_1">1</td>
<td class="blue key" data-token="num_2">2</td>
<td class="blue key" data-token="num_3">3</td>
<td class="gray key add" data-token="op_add" rowspan="2">+</td>
</tr>
<tr>
<td class="blue key" data-token="num_dot">.</td>
<td class="blue key" data-token="num_0">0</td>
<td class="blue key eval" data-func="eval">=</td>
</tr>
</tbody>
</table>
<p class="footer">Electronic Calculator</p>
</div>
html {
width:100%;
height: 100%;
padding:0;
margin:0;
display: flex;
align-items: center;
justify-content: center;
}
body {
padding:0;
margin:0;
background-image: linear-gradient( to right, rgb(189, 195, 199) 0%, rgb(236, 240, 241) 40%, rgb(236, 240, 241) 60%, rgb(189, 195, 199) 100%);
}
.calculator {
width: 250px;
background-color: rgb(44, 62, 80);
background-image: linear-gradient( to right, rgb(51, 75, 99) 0%, rgb(75, 101, 128) 40%, rgb(75, 101, 128) 60%, rgb(51, 75, 99) 100%);
border-width: 1px;
border-style: solid;
border-top-color: rgba(255, 255, 255, 0.6);
border-bottom-color: black;
border-right-color: black;
border-left-color: black;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.4), inset 0 0 3px 4px rgba(0, 0, 0, 0.5);
border-top: 4px solid rgba(255, 255, 255, 0.2);
}
/* Screen
-------------------------------*/
.screen-wrapper {
margin: 15px;
padding: 3px;
background-color: rgba(0, 0, 0, 0.3);
box-shadow: 0 2px 2px rgba(255, 255, 255, 0.5);
border-radius: 5px;
}
#screen {
height: 60px;
background-image: linear-gradient(to bottom, rgb(255, 204, 0) 0%, rgb(255, 222, 116) 100%);
border-radius: 30%/7%;
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.6);
padding: 8px 5px;
}
#expression {
font-family: 'Press Start 2P', cursive;
font-size: 12px;
height: 20px;
line-height: 20px;
overflow: hidden;
white-space: nowrap;
display: flex;
align-items: center;
}
#result {
font-family: 'Press Start 2P', cursive;
font-size: 28px;
text-align: right;
margin-top: 10px;
white-space: nowrap;
}
#typed-cursor {
border-right: 3px solid black;
height: 14px;
animation: 1s blink step-end infinite;
}
@keyframes blink {
from, to {
border-color: transparent;
}
50% {
border-color: black;
}
}
.keys {
width: 100%;
border-spacing: 12px;
}
.key {
width:25%;
cursor: pointer;
font-family: 'Open Sans', sans-serif;
color: rgb(236, 240, 241);
font-size: 18px;
text-align: center;
padding: 8px 0;
border-radius: 5px;
border-width: 3px;
border-style: solid;
border-top-color: rgba(255, 255, 255, 0.2);
border-left-color: rgba(0, 0, 0, 0.1);
border-right-color: rgba(0, 0, 0, 0.1);
border-bottom-color: rgba(0, 0, 0, 0.3);
box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.7);
transition: all 0.2s ease-out;
&:hover {
border: 3px solid rgba(226, 154, 26, 0.6);
box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.7), inset 0 0 6px rgba(0, 0, 0, 0.4);
color: rgba(226, 170, 26, 1);
text-shadow: 0 0 10px rgba(255, 218, 153, 1);
}
&.red {
background-image: linear-gradient( to bottom, rgb(150, 52, 52) 0%, rgb(190, 77, 77) 50%, rgb(150, 52, 52) 100%);
}
&.blue {
background-image: linear-gradient( to bottom, rgb(44, 62, 80) 0%, rgb(58, 80, 102) 50%, rgb(44, 62, 80) 100%);
}
&.gray {
background-image: linear-gradient( to bottom, rgb(110, 133, 151) 0%, rgb(130, 155, 175) 50%, rgb(110, 133, 151) 100%);
}
&.substract,
&.divide,
&.add,
&.eval {
font-size: 28px;
line-height: 18px;
}
&.substract {
vertical-align: top;
}
span {
font-size: 10px;
vertical-align: top;
}
}
.footer {
font-family: 'Open Sans', sans-serif;
font-size: 12px;
text-align: center;
color: rgba(0, 0, 0, 0.5);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.17);
}
View Compiled
// This is an old project of mine, resurrected from the dead :)
// I made this back in 2014, as I was learning about postfix
// notation at the university.
// This calculator uses postfix notation (AKA Reverse Polish
// Notation) to compute the result of the given mathematical
// expression in the correct order of operations.
//
// I kept the code as it is, so I apologize if it looks a bit outdated... :)
// To read more about postfix notation, see https://en.wikipedia.org/wiki/Reverse_Polish_notation
window.onload = function() {
new Calculator();
};
/**
* Calculator class
*/
function Calculator() {
this.op_add = '+';
this.op_substract = '-';
this.op_multiply = '*';
this.op_divide = '/';
this.op_pow = '^';
this.num_1 = 1;
this.num_2 = 2;
this.num_3 = 3;
this.num_4 = 4;
this.num_5 = 5;
this.num_6 = 6;
this.num_7 = 7;
this.num_8 = 8;
this.num_9 = 9;
this.num_0 = 0;
this.num_dot = '.';
this.screen = new Screen(this);
this.keys = new Keyboard(this);
}
Calculator.prototype.infixToPostfix = function( infix ) {
var length = infix.length;
var stack = new Array(); // Holds operators
var postfix = ""; // The result expression
var chars = infix.split('');
// Each character
for ( var i = 0; i < length; i++ ) {
var curChar = chars[i],
nextChar = '';
// Get the next char (unless it's the last char)
if(i < length-1) {
nextChar = chars[i+1];
}
// Operators
if(!this.isInt(chars[i]) && chars[i] !== ".") {
while ( stack.length > 0 ) {
if (this.comparePrecedence(stack.peek(), curChar)) {
postfix += stack.pop() + " ";
}
else {
break;
}
}
stack.push(curChar);
}
// Operands
else {
if(this.isInt(nextChar) || nextChar === '.')
postfix += curChar;
else postfix += curChar + " ";
}
}
// Pop the remaining operators
while (stack.length > 0) {
postfix += stack.pop() + " ";
}
return postfix.trim();
};
Calculator.prototype.postfixEval = function( postfix ) {
var resultStack = new Array();
var postfix = postfix.split(" ");
var length = postfix.length;
var decimal;
var curElement;
// Each element
for ( var i = 0; i < length; i++ ) {
curElement = postfix[i];
// Operand
if( this.isFloat(curElement) ) {
var decimal = parseFloat(curElement);
resultStack.push(decimal);
}
// Operator
else {
var result = this.applyOperator(resultStack.pop(), resultStack.pop(), curElement);
resultStack.push(result);
}
}
return resultStack.pop();
};
Calculator.prototype.comparePrecedence = function( op_a, op_b ) {
var precedence = ['+','-','*','/','^','(',')'];
return precedence.indexOf(op_a) > precedence.indexOf(op_b);
};
Calculator.prototype.applyOperator = function(b, a, operator) {
switch( operator ) {
case "+":
return a + b;
case "-":
return a - b;
case "*":
return a * b;
case "/":
return a / b;
case "^":
return Math.pow(a, b);
}
}
Calculator.prototype.isInt = function( num ) {
return !isNaN(parseInt(num));
};
Calculator.prototype.isFloat = function( num ) {
return !isNaN(parseFloat(num));
};
/**
* Screen class
*/
function Screen( calculator ) {
this.calculator = calculator;
this.expressionHolder = document.getElementById('expression');
this.expression = '';
this.result = document.getElementById('result');
this.cursor = new Cursor(this);
}
Screen.prototype.appendToExpression = function( token ) {
var value = this.calculator[token];
this.expressionHolder.innerHTML += value;
this.expressionHolder.scrollLeft = this.expressionHolder.scrollWidth;
this.expression += value;
this.cursor.moveToEnd();
};
Screen.prototype.setResult = function( number ) {
this.result.innerHTML = number.toString().slice(0,11);
this.expression = number.toString();
this.expressionHolder.innerHTML = number;
this.cursor.moveToEnd();
};
Screen.prototype.clear = function() {
this.result.innerHTML = 0;
this.expressionHolder.innerHTML = '';
this.expression = '';
this.cursor.moveToStart();
};
Screen.prototype.cancelEntry = function() {
this.expressionHolder.innerHTML = '';
this.expression = '';
this.cursor.moveToStart();
};
/**
* Cursor class
*/
function Cursor( screen ) {
this.screen = screen;
this.pos = 0;
}
Cursor.prototype.moveTo = function( pos ) {
if( pos >= 0 && pos <= this.screen.expression.length) {
// Remove current cursor
try {
document.getElementById('expression').removeChild(document.getElementById('typed-cursor'));
}
catch(e) {
}
// Recreate it
var cursor = '<span id="typed-cursor"></span>';
var expression = this.screen.expression;
expression = expression.substring(0,pos) + cursor + expression.substring(pos, expression.length);
this.pos = pos;
document.getElementById('expression').innerHTML = expression;
}
};
Cursor.prototype.moveToEnd = function() {
this.moveTo(this.screen.expression.length);
};
Cursor.prototype.moveToStart = function() {
this.moveTo(0);
};
/**
* Keyboard class
*/
function Keyboard( calculator ) {
this.calculator = calculator;
this.keys = document.querySelectorAll('.key');
for( var i = 0; i < this.keys.length; i++ ) {
var self = this;
this.keys[i].onclick = function() {
self.keyPress(this);
};
}
}
Keyboard.prototype.keyPress = function( key ) {
if( key.getAttribute("data-func") === null) {
this.calculator.screen.appendToExpression( key.getAttribute("data-token") );
}
else {
this['key_' + key.getAttribute("data-func")]();
}
};
// Calculator key actions
Keyboard.prototype.key_eval = function() {
var postfix = this.calculator.infixToPostfix(this.calculator.screen.expression);
this.calculator.screen.setResult(this.calculator.postfixEval(postfix));
};
Keyboard.prototype.key_clear = function() {
this.calculator.screen.clear();
};
Keyboard.prototype.key_cancel_entry = function() {
this.calculator.screen.cancelEntry();
};
Array.prototype.peek = function() {
return this[this.length-1];
};
This Pen doesn't use any external JavaScript resources.