I mostly need feedback on the client-side JS.
Calculator.html is mostly a big button array made in Bootstrap.
!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"></script>
<script src="Evaluator.js"></script>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
</head>
<body style="padding-left:10px;">
<div class="container">
<div class="row">
<div class="card card-info">
<div class="card-header"><label>CTI110 Final Project</label></div>
<div class="card-block"><input id="Expression" type="text" placeholder="2+2=4" class="form-control" style="text-align: right;"></div>
<div role="toolbar" class="btn-toolbar">
<div role="group" class="btn-group-lg"><button id="Backspace" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">←</button></div>
<div role="group" class="btn-group-lg"><button id="Clearentry" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">CE</button></div>
<div role="group" class="btn-group-lg"><button id="Clearall" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">C</button></div>
<div role="group" class="btn-group-lg"><button id="Plusminus" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">±</button></div>
<div role="group" class="btn-group-lg"><button id="Max" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">⋀</button></div>
</div>
<div role="toolbar" class="btn-toolbar">
<div role="group" class="btn-group-lg"><button id="Seven" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">7</button></div>
<div role="group" class="btn-group-lg"><button id="Eight" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">8</button></div>
<div role="group" class="btn-group-lg"><button id="Nine" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">9</button></div>
<div role="group" class="btn-group-lg"><button id="Divide" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">/</button></div>
<div role="group" class="btn-group-lg"><button id="Min" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">∨</button></div>
</div>
<div role="toolbar" class="btn-toolbar">
<div role="group" class="btn-group-lg"><button id="Four" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">4</button></div>
<div role="group" class="btn-group-lg"><button id="Five" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">5</button></div>
<div role="group" class="btn-group-lg"><button id="Six" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">6</button></div>
<div role="group" class="btn-group-lg"><button id="Multiply" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">*</button></div>
<div role="group" class="btn-group-lg"><button id="Modulus" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">%</button></div>
</div>
<div role="toolbar" class="btn-toolbar">
<div role="group" class="btn-group-lg"><button id="One" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">1</button></div>
<div role="group" class="btn-group-lg"><button id="Two" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">2</button></div>
<div role="group" class="btn-group-lg"><button id="Three" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">3</button></div>
<div role="group" class="btn-group-lg"><button id="Subtract" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">-</button></div>
<div role="group" class="btn-group-lg"><button id="^" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">x<sup>x</sup></button></div>
</div>
<div role="toolbar" class="btn-toolbar">
<div role="group" class="btn-group-lg"><button id="Zero" type="button" style="width: 118px; height: 59px" class="btn btn-secondary rounded-0">0</button></div>
<div role="group" class="btn-group-lg"><button id="Decimal" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">.</button></div>
<div role="group" class="btn-group-lg"><button id="Add" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">+</button></div>
<div role="group" class="btn-group-lg"><button id="Equals" type="button" style="width: 59px; height: 59px" class="btn btn-secondary rounded-0">=</button></div>
</div>
</div>
</div>
</div>
</body>
</html>
Evaluator.js
Basically this is just responsible for the building of the expression, doing some basic checks, and sending it to the server. This is the part that is pretty sloppy and I think there is a better way of doing it.
function Expression() {
this.expression = "";
this.OperatorCheck = false;
}
Expression.prototype.ToString = function() {
return this.expression;
};
Expression.prototype.Clear = function() {
this.expression = "";
this.OperatorCheck = false;
$("#Expression").empty();
};
Expression.prototype.InsertNumber = function(Expression) {
this.expression += Expression;
};
Expression.prototype.InsertOperator = function(Operator) {
if (!this.OperatorCheck) {
this.expression += " " + Operator.toString() + " ";
this.OperatorCheck = true;
} else {
alert("Only one operator allowed!");
}
};
Expression.prototype.Backspace = function() {
this.expression = this.expression.slice(0,-1);
if (this.expression.length === 0) {
this.Clear();
}
};
Expression.prototype.Submit = function() {
let Data = {
"IntegerA": this.expression.substr(0,this.expression.indexOf(' ')),
"Operation": this.expression.substr(this.expression.indexOf(' ')+ 1,1),
"IntegerB": this.expression.substr(this.expression.indexOf(' ')+ 3)
};
$.ajax({
url:"Math.php", dataType: "json", data: Data, method: "POST",
success: $.proxy(function(response) {
$("#Expression").val(response['Result']);
this.Clear();
}).bind(this)
});
};
$(document).ready(function() {
let exp = new Expression();
$(document).click(function(e) {
let buttonText = $(e).attr('target').innerHTML;
if (!(buttonText.match(/[^.\d]/)))
exp.InsertNumber(buttonText);
else if (buttonText === "=")
exp.Submit();
else if (buttonText === "CE" || buttonText === "C")
exp.Clear();
else if (buttonText === "←")
exp.Backspace();
else if (buttonText === "±")
alert("Not currently supported");
else if (buttonText === "x<sup>x</sup>")
exp.InsertOperator("^");
else
exp.InsertOperator(buttonText);
$("#Expression").val(exp.ToString());
});
});
Evaluator.php is just a bunch of static anonymous functions.
<?php
$Add = static function($IntA,$IntB) {
return $IntA + $IntB;
};
$Sub = static function($IntA,$IntB) {
return $IntA - $IntB;
};
$Div = static function($IntA,$IntB) {
return $IntA / $IntB;
};
$Mul = static function($IntA,$IntB) {
return $IntA * $IntB;
};
$Pow = static function($IntA,$IntB) {
return pow($IntA,$IntB);
};
$Mod = static function($IntA,$IntB) {
return $IntA % $IntB;
};
$Min = static function($IntA,$IntB) {
return min($IntA,$IntB);
};
$Max = static function($IntA,$IntB) {
return max($IntA,$IntB);
};
Math.php just makes a map of the functions to the operations. I thought it was clever and I hate long if
chains.
<?php
include 'Evaluator.php';
if (empty($_POST)) {
$_POST = json_decode(file_get_contents("php://input"), true) ? : [];
}
function json_response($message = null, $code = 200) {
header_remove();
http_response_code($code);
header("Cache-Control: no-transform,public,max-age=300,s-maxage=900");
header('Content-Type: application/json');
$status = array(200 => '200 OK', 400 => '400 Bad Request', 422 => 'Unprocessable Entity', 500 => '500 Internal Server Error');
header('Status: '.$code);
return json_encode(array('status' => $code < 300,'Result' => $message));
}
$IntegerA = isset($_POST["IntegerA"]) ? $_POST["IntegerA"] : 0;
$IntegerB = isset($_POST["IntegerB"]) ? $_POST["IntegerB"] : 0;
$Operation = isset($_POST["Operation"]) ? $_POST["Operation"] : "";
$OperationMap = Array();
$OperationMap['+'] = $Add;$OperationMap['-'] = $Sub;$OperationMap['/'] = $Div;$OperationMap['*'] = $Mul;
$OperationMap['^'] = $Pow;$OperationMap['%'] = $Mod;$OperationMap['⋀'] = $Max;$OperationMap['∨'] = $Min;
foreach($OperationMap as $key => $value) {
if ($key == $Operation) {
echo json_response($value($IntegerA,$IntegerB),200);
}
}
eval
just because of some stigma that it's unsafe.. there's a way to safely use eval and doing so will hugely reduce your code base and maek your calculator faster.. not a popular opinion but it's true. \$\endgroup\$