diff --git a/api/controller.js b/api/controller.js
index 949731c..5a1c3c1 100644
--- a/api/controller.js
+++ b/api/controller.js
@@ -16,8 +16,17 @@ exports.calculate = function(req, res) {
'subtract': function(a, b) { return a - b },
'multiply': function(a, b) { return a * b },
'divide': function(a, b) { return a / b },
+ 'power': function(a, b) { return Math.pow(a, b) },
+ 'sqrt': function(a) { return Math.sqrt(a) },
+ 'sin': function(a) { return Math.sin(a) },
+ 'cos': function(a) { return Math.cos(a) },
+ 'tan': function(a) { return Math.tan(a) },
+ 'log': function(a) { return Math.log10(a) },
+ 'ln': function(a) { return Math.log(a) },
};
+ var singleOperandOps = ['sqrt', 'sin', 'cos', 'tan', 'log', 'ln'];
+
if (!req.query.operation) {
throw new Error("Unspecified operation");
}
@@ -29,16 +38,24 @@ exports.calculate = function(req, res) {
}
if (!req.query.operand1 ||
- !req.query.operand1.match(/^(-)?[0-9\.]+(e(-)?[0-9]+)?$/) ||
+ !req.query.operand1.match(/^(-)?[0-9.]+(e(-)?[0-9]+)?$/) ||
req.query.operand1.replace(/[-0-9e]/g, '').length > 1) {
throw new Error("Invalid operand1: " + req.query.operand1);
}
- if (!req.query.operand2 ||
- !req.query.operand2.match(/^(-)?[0-9\.]+(e(-)?[0-9]+)?$/) ||
- req.query.operand2.replace(/[-0-9e]/g, '').length > 1) {
- throw new Error("Invalid operand2: " + req.query.operand2);
+ // Only validate operand2 for operations that require two operands
+ if (!singleOperandOps.includes(req.query.operation)) {
+ if (!req.query.operand2 ||
+ !req.query.operand2.match(/^(-)?[0-9.]+(e(-)?[0-9]+)?$/) ||
+ req.query.operand2.replace(/[-0-9e]/g, '').length > 1) {
+ throw new Error("Invalid operand2: " + req.query.operand2);
+ }
}
- res.json({ result: operation(req.query.operand1, req.query.operand2) });
+ // Call operation with appropriate number of operands
+ var result = singleOperandOps.includes(req.query.operation)
+ ? operation(req.query.operand1)
+ : operation(req.query.operand1, req.query.operand2);
+
+ res.json({ result: result });
};
diff --git a/public/client.js b/public/client.js
index 1d21fd9..a359c72 100644
--- a/public/client.js
+++ b/public/client.js
@@ -16,6 +16,9 @@ var operand1 = 0;
var operand2 = 0;
var operation = null;
+// Single operand operations that execute immediately
+var SINGLE_OPERAND_OPS = ['√', 'sin', 'cos', 'tan', 'log', 'ln'];
+
function calculate(operand1, operand2, operation) {
var uri = location.origin + "/arithmetic";
@@ -33,13 +36,38 @@ function calculate(operand1, operand2, operation) {
case '/':
uri += "?operation=divide";
break;
+ case '^':
+ uri += "?operation=power";
+ break;
+ case '√':
+ uri += "?operation=sqrt";
+ break;
+ case 'sin':
+ uri += "?operation=sin";
+ break;
+ case 'cos':
+ uri += "?operation=cos";
+ break;
+ case 'tan':
+ uri += "?operation=tan";
+ break;
+ case 'log':
+ uri += "?operation=log";
+ break;
+ case 'ln':
+ uri += "?operation=ln";
+ break;
default:
setError();
return;
}
uri += "&operand1=" + encodeURIComponent(operand1);
- uri += "&operand2=" + encodeURIComponent(operand2);
+
+ // Only add operand2 for binary operations
+ if (!SINGLE_OPERAND_OPS.includes(operation)) {
+ uri += "&operand2=" + encodeURIComponent(operand2);
+ }
setLoading(true);
@@ -116,6 +144,15 @@ function operationPressed(op) {
state = states.operator;
}
+function scientificPressed(op) {
+ // Single operand operations execute immediately
+ if (SINGLE_OPERAND_OPS.includes(op)) {
+ operand1 = getValue();
+ calculate(operand1, null, op);
+ state = states.complete;
+ }
+}
+
function equalPressed() {
if (state < states.operand2) {
state = states.complete;
diff --git a/public/default.css b/public/default.css
index bd14feb..6baf245 100644
--- a/public/default.css
+++ b/public/default.css
@@ -1,44 +1,44 @@
-/* CSS Custom Properties for Modern Theming */
+/* CSS Custom Properties for The Green Theme of the Shire */
:root {
- --primary-bg: #f0f2f5;
- --calculator-bg: #ffffff;
- --display-bg: #1e1e1e;
- --display-text: #ffffff;
- --button-bg: #f8f9fa;
- --button-hover: #e9ecef;
- --button-active: #dee2e6;
- --button-operator: #007bff;
- --button-operator-hover: #0056b3;
- --button-operator-active: #004085;
+ --primary-bg: #d4f1d4;
+ --calculator-bg: #f0fff0;
+ --display-bg: #1a4d1a;
+ --display-text: #c8ffc8;
+ --button-bg: #e8f5e8;
+ --button-hover: #c8e6c8;
+ --button-active: #a8d6a8;
+ --button-operator: #2d8659;
+ --button-operator-hover: #257047;
+ --button-operator-active: #1d5a38;
--button-equals: #28a745;
--button-equals-hover: #1e7e34;
--button-equals-active: #155724;
--button-clear: #dc3545;
--button-clear-hover: #c82333;
--button-clear-active: #bd2130;
- --text-primary: #212529;
- --text-secondary: #6c757d;
- --shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
- --shadow-md: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
- --shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
+ --text-primary: #1a4d1a;
+ --text-secondary: #4a7c4a;
+ --shadow-sm: 0 0.125rem 0.25rem rgba(0, 100, 0, 0.1);
+ --shadow-md: 0 0.5rem 1rem rgba(0, 100, 0, 0.2);
+ --shadow-lg: 0 1rem 3rem rgba(0, 100, 0, 0.25);
--border-radius: 0.5rem;
--border-radius-sm: 0.375rem;
--transition-duration: 0.15s;
--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
}
-/* Dark mode support */
+/* Dark mode support - Green theme of Fangorn Forest */
@media (prefers-color-scheme: dark) {
:root {
- --primary-bg: #121212;
- --calculator-bg: #1e1e1e;
- --display-bg: #000000;
- --display-text: #ffffff;
- --button-bg: #2d2d2d;
- --button-hover: #3d3d3d;
- --button-active: #4d4d4d;
- --text-primary: #ffffff;
- --text-secondary: #adb5bd;
+ --primary-bg: #0a2f0a;
+ --calculator-bg: #1a3d1a;
+ --display-bg: #0d1f0d;
+ --display-text: #90ee90;
+ --button-bg: #2d4d2d;
+ --button-hover: #3d6d3d;
+ --button-active: #4d8d4d;
+ --text-primary: #90ee90;
+ --text-secondary: #6b9d6b;
}
}
@@ -47,7 +47,7 @@ BODY {
width: 100vw;
padding: 0;
margin: 0;
- background: linear-gradient(135deg, var(--primary-bg) 0%, #e3e6ea 100%);
+ background: linear-gradient(135deg, var(--primary-bg) 0%, #b8e6b8 100%);
font-family: var(--font-family);
display: flex;
justify-content: center;
@@ -226,7 +226,21 @@ BODY {
height: 200px;
}
-/* Button type specific styling */
+/* Button type specific styling - Scientific buttons like the leaves of Lothlórien */
+.btn-scientific {
+ background: #4a9d5f;
+ color: white;
+ font-size: 1rem;
+}
+
+.btn-scientific:hover {
+ background: #3d8550;
+}
+
+.btn-scientific:active {
+ background: #2f6d40;
+}
+
.btn[onclick*="operationPressed"] {
background: var(--button-operator);
color: white;
diff --git a/public/index.html b/public/index.html
index eaa7da7..e552658 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,14 +1,14 @@
- Modern Calculator
+ Scientific Palantír of Rivendell
-
+
-
+
@@ -16,10 +16,20 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/arithmetic.test.js b/test/arithmetic.test.js
index deded48..961b226 100644
--- a/test/arithmetic.test.js
+++ b/test/arithmetic.test.js
@@ -205,4 +205,63 @@ describe('Arithmetic', function () {
});
});
});
+
+ describe('Scientific Operations - Power of the Istari', function () {
+ it('raises number to power', function (done) {
+ request.get('/arithmetic?operation=power&operand1=2&operand2=3')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 8 });
+ done();
+ });
+ });
+ it('calculates square root', function (done) {
+ request.get('/arithmetic?operation=sqrt&operand1=9')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 3 });
+ done();
+ });
+ });
+ it('calculates sine of zero', function (done) {
+ request.get('/arithmetic?operation=sin&operand1=0')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 0 });
+ done();
+ });
+ });
+ it('calculates cosine of zero', function (done) {
+ request.get('/arithmetic?operation=cos&operand1=0')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 1 });
+ done();
+ });
+ });
+ it('calculates tangent of zero', function (done) {
+ request.get('/arithmetic?operation=tan&operand1=0')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 0 });
+ done();
+ });
+ });
+ it('calculates log10 of 100', function (done) {
+ request.get('/arithmetic?operation=log&operand1=100')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 2 });
+ done();
+ });
+ });
+ it('calculates natural log of e', function (done) {
+ request.get('/arithmetic?operation=ln&operand1=2.718281828459045')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body.result).to.be.closeTo(1, 0.0001);
+ done();
+ });
+ });
+ });
});