-
Notifications
You must be signed in to change notification settings - Fork 56
/
calculator.js
196 lines (172 loc) · 7.04 KB
/
calculator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
* Copyright (C) 2012 Ideaviate AB
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var Calculator = function () {
// Helper variable declarations
var self = this,
decimalMark = ".",
sum = 0,
prevOperator;
// Define default values
self.display = ko.observable("0");
self.isShowingResult = ko.observable(false);
// Callback for each number button
self.number = function (item, event) {
var button = event.target.innerText || event.target.textContent;
// If a result has been shown, make sure we
// clear the display before displaying any new numbers
if (self.isShowingResult()) {
self.clearDisplay();
self.isShowingResult(false);
}
// Make sure we only add one decimal mark
if (button == decimalMark && self.display().indexOf(decimalMark) > -1)
return;
// Make sure that we remove the default 0 shown on the display
// when the user press the first number button
var newValue = (self.display() === "0" && button != decimalMark) ? button : self.display() + button;
// Update the display
self.display(newValue);
};
// Callback for each operator button
self.operator = function (item, event) {
var button = event.target.innerText || event.target.textContent;
// Only perform calculation if numbers
// has been entered since last operator button was pressed
if (!self.isShowingResult()) {
// Perform calculation
switch (prevOperator) {
case "+":
sum = sum + parseFloat(self.display(), 10);
break;
case "-":
sum = sum - parseFloat(self.display(), 10);
break;
case "x":
sum = sum * parseFloat(self.display(), 10);
break;
case "÷":
sum = sum / parseFloat(self.display(), 10);
break;
default:
sum = parseFloat(self.display(), 10);
};
}
// Avoid showing a result until you have at least
// two terms to perform calculation on
if (prevOperator)
self.display(sum);
// Make sure we don't try to calculate with the equal sign
prevOperator = (button === "=") ? null : button;
// Always set the calculator into showing result state
// after an operator button has been pressed
self.isShowingResult(true);
};
// Callback for negating a number
self.negate = function () {
// Disable the negate button when showing a result
if (self.isShowingResult() || self.display() === "0")
return;
var newValue = (self.display().substr(0, 1) === "-") ? self.display().substr(1) : "-" + self.display();
self.display(newValue);
};
// Callback for each backspace button
self.backspace = function (item, event) {
// Disable backspace if the calculator is shown a result
if (self.isShowingResult())
return;
// Remove the last character, and make the display zero when
// last character is removed
if (self.display().length > 1) {
self.display(self.display().substr(0, self.display().length - 1));
} else {
self.clearDisplay();
}
};
// Clear the entire calculator
self.clear = function () {
prevOperator = null;
self.clearDisplay();
sum = 0;
};
// Clear just the display
self.clearDisplay = function () {
self.display("0");
};
};
// Apply knockout bindings
ko.applyBindings(new Calculator());
// Enable keyboard controll
(function () {
// Key codes and their associated calculator buttons
var calculatorKeys = {
48: "0", 49: "1", 50: "2", 51: "3", 52: "4", 53: "5", 54: "6",
55: "7", 56: "8", 57: "9", 96: "0", 97: "1", 98: "2", 99: "3",
100: "4", 101: "5", 102: "6", 103: "7", 104: "8", 105: "9",
106: "x", 107: "+", 109: "-", 110: ".", 111: "÷", 8: "backspace",
13: "=", 46: "c", 67: "c"
};
// Helper function to fire an event on an element
function fireEvent(element, event) {
if (document.createEvent) {
// Dispatch for firefox + others
var evt = document.createEvent("HTMLEvents");
evt.initEvent(event, true, true);
return !element.dispatchEvent(evt);
} else {
// Dispatch for IE
var evt = document.createEventObject();
return element.fireEvent('on' + event, evt)
}
}
// Helper functions to add/remove HTML-element classes
// as IE didn't support the classList property prior to IE10
function hasClass(ele, cls) {
return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
}
function addClass(ele, cls) {
if (!hasClass(ele, cls)) ele.className += " " + cls;
}
function removeClass(ele, cls) {
if (hasClass(ele, cls)) {
var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
ele.className = ele.className.replace(reg, ' ');
}
}
// Callback for every key stroke
var keycallback = function (e) {
// Check if the key was one of our calculator keys
if (e.keyCode in calculatorKeys) {
// Get button-element associated with key
var element = document.getElementById("calculator-button-" + calculatorKeys[e.keyCode]);
// Simulate button click on keystroke
addClass(element, "active");
setTimeout(function () { removeClass(element, "active"); }, 100);
// Fire click event
fireEvent(element, "click");
}
}
// Attach a keyup-event listener on the document
if (document.addEventListener) {
document.addEventListener('keyup', keycallback, false);
} else if (document.attachEvent) {
document.attachEvent('keyup', keycallback);
}
})();