Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions api/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ 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) },
};

if (!req.query.operation) {
Expand All @@ -29,13 +31,13 @@ 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.match(/^(-)?[0-9.]+(e(-)?[0-9]+)?$/) ||
req.query.operand2.replace(/[-0-9e]/g, '').length > 1) {
throw new Error("Invalid operand2: " + req.query.operand2);
}
Expand Down
33 changes: 32 additions & 1 deletion public/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ function calculate(operand1, operand2, operation) {
case '/':
uri += "?operation=divide";
break;
case '^':
uri += "?operation=power";
break;
case '√':
uri += "?operation=sqrt";
break;
default:
setError();
return;
Expand Down Expand Up @@ -132,6 +138,31 @@ function equalPressed() {
calculate(operand1, operand2, operation);
}

// Theme toggle functionality
function initThemeToggle() {
// Check for saved theme preference or default to system preference
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
document.body.setAttribute('data-theme', savedTheme);
}
}

function toggleTheme() {
const currentTheme = document.body.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
document.body.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
}

// Initialize theme on load
if (typeof document !== 'undefined') {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initThemeToggle);
} else {
initThemeToggle();
}
}

// Enhanced keyboard support
document.addEventListener('keydown', (event) => {
// Handle keydown for special keys
Expand All @@ -154,7 +185,7 @@ document.addEventListener('keypress', (event) => {
} else if (event.key == '.') {
decimalPressed();
event.preventDefault();
} else if (event.key.match(/^[-*+/]$/)) {
} else if (event.key.match(/^[-*+/^]$/)) {
operationPressed(event.key);
event.preventDefault();
} else if (event.key == '=' || event.key == 'Enter') {
Expand Down
77 changes: 77 additions & 0 deletions public/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,40 @@
}
}

/* Manual theme override */
body[data-theme="dark"] {
--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[data-theme="light"] {
--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;
--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;
}

BODY {
height: 100vh;
width: 100vw;
Expand Down Expand Up @@ -344,3 +378,46 @@ BODY {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}

/* Theme toggle button styling */
.theme-toggle-container {
display: flex;
justify-content: flex-end;
margin-bottom: 1rem;
width: 100%;
}

.theme-toggle-btn {
background: var(--button-bg);
border: none;
border-radius: 50%;
width: 44px;
height: 44px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--transition-duration) ease;
box-shadow: var(--shadow-sm);
font-size: 1.5rem;
padding: 0;
}

.theme-toggle-btn:hover {
background: var(--button-hover);
transform: scale(1.1);
box-shadow: var(--shadow-md);
}

.theme-toggle-btn:active {
transform: scale(0.95);
}

.theme-toggle-btn:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.4);
}

.theme-icon {
user-select: none;
}
8 changes: 7 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@
<body onLoad="setValue(0)">
<div class="container">
<main class="calculator" role="application" aria-label="Calculator">
<div class="theme-toggle-container">
<button class="theme-toggle-btn" onClick="toggleTheme()" aria-label="Toggle theme">
<span class="theme-icon">🌓</span>
</button>
</div>
<div id="results">
<div id="result" role="textbox" aria-label="Calculator display" aria-live="polite" aria-readonly="true" tabindex="0"><span class="digit0">0</span></div>
</div>

<div id="loading" style="visibility:hidden" aria-hidden="true"></div>

<div class="calculator-buttons" role="grid" aria-label="Calculator buttons">
<button class="btn span-2" aria-label="Empty button" disabled></button>
<button class="btn" onClick="operationPressed('√')" aria-label="Square root">√</button>
<button class="btn" onClick="operationPressed('^')" aria-label="Power">^</button>
<button class="btn" onClick="clearPressed()" aria-label="Clear all">C</button>
<button class="btn" onClick="clearPressed()" aria-label="Clear entry">CE</button>

Expand Down
62 changes: 62 additions & 0 deletions test/arithmetic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,66 @@ describe('Arithmetic', function () {
});
});
});

describe('Power', function () {
it('raises a positive integer to a positive 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('raises a positive integer to the power of 0', function (done) {
request.get('/arithmetic?operation=power&operand1=42&operand2=0')
.expect(200)
.end(function (err, res) {
expect(res.body).to.eql({ result: 1 });
done();
});
});
it('raises a number to a negative power', function (done) {
request.get('/arithmetic?operation=power&operand1=2&operand2=-2')
.expect(200)
.end(function (err, res) {
expect(res.body).to.eql({ result: 0.25 });
done();
});
});
it('raises a negative number to a 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();
});
});
});

describe('Square Root', function () {
it('calculates square root of a positive integer', function (done) {
request.get('/arithmetic?operation=sqrt&operand1=9&operand2=0')
.expect(200)
.end(function (err, res) {
expect(res.body).to.eql({ result: 3 });
done();
});
});
it('calculates square root of a decimal', function (done) {
request.get('/arithmetic?operation=sqrt&operand1=2.25&operand2=0')
.expect(200)
.end(function (err, res) {
expect(res.body).to.eql({ result: 1.5 });
done();
});
});
it('calculates square root of zero', function (done) {
request.get('/arithmetic?operation=sqrt&operand1=0&operand2=0')
.expect(200)
.end(function (err, res) {
expect(res.body).to.eql({ result: 0 });
done();
});
});
});
});