diff --git a/api/controller.js b/api/controller.js
index 949731c..b43da2d 100644
--- a/api/controller.js
+++ b/api/controller.js
@@ -16,6 +16,12 @@ 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 },
+ 'sin': function(a) { return Math.sin(a) },
+ 'cos': function(a) { return Math.cos(a) },
+ 'tan': function(a) { return Math.tan(a) },
+ 'sqrt': function(a) { return Math.sqrt(a) },
+ 'power': function(a, b) { return Math.pow(a, b) },
+ 'log': function(a) { return Math.log10(a) },
};
if (!req.query.operation) {
@@ -29,16 +35,23 @@ 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);
- }
+ // Check if operation requires two operands
+ var singleOperandOps = ['sin', 'cos', 'tan', 'sqrt', 'log'];
+ var requiresTwoOperands = singleOperandOps.indexOf(req.query.operation) === -1;
- res.json({ result: operation(req.query.operand1, req.query.operand2) });
+ if (requiresTwoOperands) {
+ 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) });
+ } else {
+ res.json({ result: operation(req.query.operand1) });
+ }
};
diff --git a/public/client.js b/public/client.js
index 1d21fd9..7c301b0 100644
--- a/public/client.js
+++ b/public/client.js
@@ -33,13 +33,28 @@ function calculate(operand1, operand2, operation) {
case '/':
uri += "?operation=divide";
break;
+ case 'power':
+ uri += "?operation=power";
+ break;
+ case 'sin':
+ case 'cos':
+ case 'tan':
+ case 'sqrt':
+ case 'log':
+ uri += "?operation=" + operation;
+ break;
default:
setError();
return;
}
uri += "&operand1=" + encodeURIComponent(operand1);
- uri += "&operand2=" + encodeURIComponent(operand2);
+
+ // Only add operand2 for operations that need it
+ var singleOperandOps = ['sin', 'cos', 'tan', 'sqrt', 'log'];
+ if (singleOperandOps.indexOf(operation) === -1) {
+ uri += "&operand2=" + encodeURIComponent(operand2);
+ }
setLoading(true);
@@ -132,6 +147,44 @@ function equalPressed() {
calculate(operand1, operand2, operation);
}
+function scientificOperation(op) {
+ // Scientific operations work on the current value immediately
+ var currentValue = getValue();
+
+ // Save state for potential chaining
+ operand1 = currentValue;
+ operation = op;
+ state = states.complete;
+
+ // Calculate immediately (operand2 not needed for these operations)
+ calculate(currentValue, null, op);
+}
+
+function toggleTheme() {
+ var body = document.body;
+ var themeIcon = document.querySelector('.theme-icon');
+
+ if (body.classList.contains('dark-theme')) {
+ body.classList.remove('dark-theme');
+ body.classList.add('light-theme');
+ themeIcon.textContent = '🌙';
+ } else if (body.classList.contains('light-theme')) {
+ body.classList.remove('light-theme');
+ body.classList.add('dark-theme');
+ themeIcon.textContent = '☀️';
+ } else {
+ // First time toggle - check system preference
+ var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ if (prefersDark) {
+ body.classList.add('light-theme');
+ themeIcon.textContent = '🌙';
+ } else {
+ body.classList.add('dark-theme');
+ themeIcon.textContent = '☀️';
+ }
+ }
+}
+
// Enhanced keyboard support
document.addEventListener('keydown', (event) => {
// Handle keydown for special keys
diff --git a/public/default.css b/public/default.css
index bd14feb..d91f51e 100644
--- a/public/default.css
+++ b/public/default.css
@@ -10,6 +10,9 @@
--button-operator: #007bff;
--button-operator-hover: #0056b3;
--button-operator-active: #004085;
+ --button-scientific: #6f42c1;
+ --button-scientific-hover: #5a32a3;
+ --button-scientific-active: #4c2a8a;
--button-equals: #28a745;
--button-equals-hover: #1e7e34;
--button-equals-active: #155724;
@@ -42,6 +45,32 @@
}
}
+/* Light theme override */
+body.light-theme {
+ --primary-bg: #f0f2f5;
+ --calculator-bg: #ffffff;
+ --display-bg: #1e1e1e;
+ --display-text: #ffffff;
+ --button-bg: #f8f9fa;
+ --button-hover: #e9ecef;
+ --button-active: #dee2e6;
+ --text-primary: #212529;
+ --text-secondary: #6c757d;
+}
+
+/* Dark theme override */
+body.dark-theme {
+ --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;
+}
+
BODY {
height: 100vh;
width: 100vw;
@@ -108,6 +137,36 @@ BODY {
min-height: auto;
}
+/* Theme toggle button */
+.theme-toggle {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ background: var(--button-bg);
+ border: none;
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ font-size: 1.5rem;
+ cursor: pointer;
+ box-shadow: var(--shadow-md);
+ transition: all var(--transition-duration) ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 100;
+}
+
+.theme-toggle:hover {
+ background: var(--button-hover);
+ transform: scale(1.1);
+ box-shadow: var(--shadow-lg);
+}
+
+.theme-toggle:active {
+ transform: scale(0.95);
+}
+
#results {
margin-bottom: 1.5rem;
width: 100%;
@@ -266,6 +325,21 @@ BODY {
background: var(--button-clear-active);
}
+/* Scientific button styling */
+.btn-scientific {
+ background: var(--button-scientific);
+ color: white;
+ font-size: 1rem;
+}
+
+.btn-scientific:hover {
+ background: var(--button-scientific-hover);
+}
+
+.btn-scientific:active {
+ background: var(--button-scientific-active);
+}
+
.calculator {
background: var(--calculator-bg);
border-radius: var(--border-radius);
@@ -306,6 +380,10 @@ BODY {
background: var(--button-clear-active);
}
+.btn-scientific.pressed {
+ background: var(--button-scientific-active);
+}
+
/* Improved mobile touch targets */
@media (max-width: 767px) {
.btn {
diff --git a/public/index.html b/public/index.html
index eaa7da7..dab3ea2 100644
--- a/public/index.html
+++ b/public/index.html
@@ -8,6 +8,9 @@
+
0
@@ -16,7 +19,13 @@
-
+
+
+
+
+
+
+
diff --git a/test/arithmetic.test.js b/test/arithmetic.test.js
index deded48..1a774d3 100644
--- a/test/arithmetic.test.js
+++ b/test/arithmetic.test.js
@@ -205,4 +205,88 @@ describe('Arithmetic', function () {
});
});
});
+
+ describe('Scientific Operations', function () {
+ describe('Sine', function () {
+ it('calculates sine of 0', function (done) {
+ request.get('/arithmetic?operation=sin&operand1=0')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 0 });
+ done();
+ });
+ });
+ });
+
+ describe('Cosine', function () {
+ it('calculates cosine of 0', function (done) {
+ request.get('/arithmetic?operation=cos&operand1=0')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 1 });
+ done();
+ });
+ });
+ });
+
+ describe('Square Root', function () {
+ it('calculates square root of 9', 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 square root of 0', function (done) {
+ request.get('/arithmetic?operation=sqrt&operand1=0')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 0 });
+ done();
+ });
+ });
+ });
+
+ describe('Power', function () {
+ it('calculates 2 to the power of 3', 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 10 to the power of 2', function (done) {
+ request.get('/arithmetic?operation=power&operand1=10&operand2=2')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 100 });
+ done();
+ });
+ });
+ });
+
+ describe('Logarithm', function () {
+ it('calculates log of 10', function (done) {
+ request.get('/arithmetic?operation=log&operand1=10')
+ .expect(200)
+ .end(function (err, res) {
+ expect(res.body).to.eql({ result: 1 });
+ done();
+ });
+ });
+
+ it('calculates log 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();
+ });
+ });
+ });
+ });
});