├── App.js ├── README.md ├── app.json ├── assets ├── icon.png └── splash.png ├── components ├── CalcButton.js ├── CalcDisplay.js └── index.js ├── lib ├── swisscalc.calc.calculator.js ├── swisscalc.calc.loanCalculator.js ├── swisscalc.calc.tipCalculator.js ├── swisscalc.display.fixedPointDisplay.js ├── swisscalc.display.memoryDisplay.js ├── swisscalc.display.numericDisplay.js ├── swisscalc.lib.format.js ├── swisscalc.lib.operator.js ├── swisscalc.lib.operatorCache.js └── swisscalc.lib.shuntingYard.js ├── package.json └── screens └── CalculatorScreen.js /App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, Text, View } from 'react-native'; 3 | import CalculatorScreen from './screens/CalculatorScreen'; 4 | 5 | export default class App extends React.Component { 6 | render() { 7 | return 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReactCalculator 2 | Calculator built using React Native 3 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "react-calc-01", 4 | "description": "This project is really great.", 5 | "slug": "react-calc-01", 6 | "privacy": "public", 7 | "sdkVersion": "26.0.0", 8 | "platforms": ["ios", "android"], 9 | "version": "1.0.0", 10 | "icon": "./assets/icon.png", 11 | "splash": { 12 | "image": "./assets/splash.png", 13 | "resizeMode": "contain", 14 | "backgroundColor": "#ffffff" 15 | }, 16 | "updates": { 17 | "fallbackToCacheTimeout": 0 18 | }, 19 | "assetBundlePatterns": [ 20 | "**/*" 21 | ], 22 | "ios": { 23 | "supportsTablet": true 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProProgramming101/ReactCalculator/26bab9b44b10783d50934aa6752c0bf2885d4d6f/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProProgramming101/ReactCalculator/26bab9b44b10783d50934aa6752c0bf2885d4d6f/assets/splash.png -------------------------------------------------------------------------------- /components/CalcButton.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { StyleSheet, View, Text, TouchableOpacity } from 'react-native'; 4 | 5 | export default class CalcButton extends React.Component { 6 | 7 | static defaultProps = { 8 | onPress: function() { }, 9 | title: "", 10 | color: "white", 11 | backgroundColor: "black", 12 | radius: 40, 13 | 14 | style: { }, 15 | } 16 | 17 | 18 | render() { 19 | var r = this.props.radius; 20 | var w = this.props.radius * 2; 21 | var h = this.props.radius * 2; 22 | var bc = this.props.backgroundColor; 23 | 24 | return ( 25 | 27 | {this.props.title} 28 | 29 | ); 30 | } 31 | } 32 | 33 | const styles = StyleSheet.create({ 34 | container: { alignItems: "center", justifyContent: "center", margin: 5, }, 35 | text: { fontSize: 30, fontWeight: "bold", }, 36 | }); -------------------------------------------------------------------------------- /components/CalcDisplay.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { StyleSheet, View, Text }from 'react-native'; 4 | 5 | export default class CalcDisplay extends React.Component { 6 | 7 | static defaultProps = { 8 | display: "", 9 | } 10 | 11 | render() { 12 | return ( 13 | 14 | {this.props.display} 15 | 16 | ) 17 | } 18 | } 19 | 20 | const styles = StyleSheet.create({ 21 | container: { padding: 20, }, 22 | display: { fontSize: 70, color: "white", textAlign: "right", }, 23 | }) -------------------------------------------------------------------------------- /components/index.js: -------------------------------------------------------------------------------- 1 | 2 | export { default as CalcButton } from './CalcButton'; 3 | export { default as CalcDisplay } from './CalcDisplay'; -------------------------------------------------------------------------------- /lib/swisscalc.calc.calculator.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Class for implementing a basic/scientific calculator 7 | 8 | /* 9 | Usage: 10 | 11 | var oc = global.swisscalc.lib.operatorCache; 12 | var calc = new global.swisscalc.calc.calculator(); 13 | 14 | // Calculate: 12 + 45 = 15 | calc.addDigit("1"); 16 | calc.addDigit("2"); 17 | calc.addBinaryOperator(oc.AdditionOperator); 18 | calc.addDigit("4"); 19 | calc.addDigit("5"); 20 | calc.equalsPressed(); 21 | alert(calc.getMainDisplay()); // 57 22 | calc.clear(); 23 | 24 | */ 25 | 26 | global.swisscalc = global.swisscalc || {}; 27 | global.swisscalc.calc = global.swisscalc.calc || {}; 28 | global.swisscalc.calc.calculator = function() { 29 | this._state = 0; // STATE_AWAITING_OPERATOR 30 | this._evaluator = new global.swisscalc.lib.shuntingYard(); 31 | this._mainDisplay = new global.swisscalc.display.numericDisplay(true, 7); 32 | this._memoryDisplay = new global.swisscalc.display.memoryDisplay(); 33 | }; 34 | 35 | // Constants... 36 | global.swisscalc.calc.calculator.STATE_AWAITING_OPERAND = 0; // Don't use. Use STATE_AWAITING_OPERATOR instead 37 | global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR = 0; 38 | global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND = 1; 39 | global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR = 2; 40 | 41 | // Sets the current state of the calculator. 42 | global.swisscalc.calc.calculator.prototype.setState = function(state) { 43 | this._state = state; 44 | }; 45 | 46 | // Pushes the value of _mainDisplay onto the operand stack. 47 | global.swisscalc.calc.calculator.prototype.pushDisplay = function() { 48 | var val = this._mainDisplay.getDisplayValue(); 49 | this._evaluator.addOperand(val); 50 | }; 51 | 52 | // Adds the given digit, or starts the display over if applicable. 53 | // Only send 0...9 or . (decimal). Must be a string. State dependent. 54 | global.swisscalc.calc.calculator.prototype.addDigit = function(digit) { 55 | if (this._state == global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR) 56 | { 57 | this._mainDisplay.clear(); 58 | this._mainDisplay.addDigit(digit); 59 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND); 60 | } 61 | else if (this._state == global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND) 62 | { 63 | this._mainDisplay.addDigit(digit); 64 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND); 65 | } 66 | else if (this._state == global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR) 67 | { 68 | this._mainDisplay.clear(); 69 | this._mainDisplay.addDigit(digit); 70 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND); 71 | } 72 | }; 73 | 74 | // Removes the last character if applicable. State dependent. 75 | global.swisscalc.calc.calculator.prototype.backspace = function() { 76 | if (this._state == global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR) 77 | { 78 | this.setState(global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR); 79 | } 80 | else if (this._state == global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND) 81 | { 82 | this._mainDisplay.backspace(); 83 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND); 84 | } 85 | else if (this._state == global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR) 86 | { 87 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR); 88 | } 89 | }; 90 | 91 | // Clears everything and returns to initial state 92 | global.swisscalc.calc.calculator.prototype.clear = function() { 93 | this._mainDisplay.clear(); 94 | this._evaluator.clear(); 95 | this.setState(global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR); 96 | }; 97 | 98 | // Clears the display. Does not change state. (Like pressing CE on a calculator) 99 | global.swisscalc.calc.calculator.prototype.clearEntry = function() { 100 | this._mainDisplay.clear(); 101 | }; 102 | 103 | // Pushes display, evaluates, and updates display. 104 | global.swisscalc.calc.calculator.prototype.equalsPressed = function() { 105 | this.pushDisplay(); 106 | var result = this._evaluator.evaluate(); 107 | this._mainDisplay.setDisplayValue(result); 108 | this.setState(global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR); 109 | }; 110 | 111 | // Adds parenthesis and clears display. 112 | global.swisscalc.calc.calculator.prototype.openParen = function() { 113 | this._evaluator.addOpenParen(global.swisscalc.lib.operatorCache.OpenParenOperator); 114 | this._mainDisplay.clear(); 115 | this.setState(global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR); 116 | }; 117 | 118 | // If in a sub-expression, pushes display, applies parenthesis, and updates display. 119 | global.swisscalc.calc.calculator.prototype.closeParen = function() { 120 | // Ignore if not in sub-expression... 121 | if (!this._evaluator.inSubExpression()) 122 | return; 123 | 124 | this.pushDisplay(); 125 | this._evaluator.addCloseParen(global.swisscalc.lib.operatorCache.CloseParenOperator); 126 | this._mainDisplay.setDisplayValue(this._evaluator.popOperand()); 127 | this.setState(global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR); 128 | }; 129 | 130 | // Just displays the constant on the screen. 131 | global.swisscalc.calc.calculator.prototype.addNullaryOperator = function(nullaryOperator) { 132 | var val = nullaryOperator.evaluate(); 133 | this._mainDisplay.setDisplayValue(val); 134 | this.setState(global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR); 135 | }; 136 | 137 | // Negation is a special type of unary operator because the user must be allowed to continue typing the number. 138 | global.swisscalc.calc.calculator.prototype.negate = function() { 139 | if (this._state == global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR) 140 | { 141 | this.addUnaryOperator(global.swisscalc.lib.operatorCache.NegateOperator); 142 | } 143 | else if (this._state == global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND) 144 | { 145 | this._mainDisplay.negate(); 146 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND); 147 | } 148 | else if (this._state == global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR) 149 | { 150 | this.addUnaryOperator(global.swisscalc.lib.operatorCache.NegateOperator); 151 | } 152 | }; 153 | 154 | // Adds the given unary operator. Do NOT send this function a NegateOperator; use negate() instead. 155 | global.swisscalc.calc.calculator.prototype.addUnaryOperator = function(unaryOperator) { 156 | this.pushDisplay(); 157 | this._evaluator.addUnaryOperator(unaryOperator); 158 | this._mainDisplay.setDisplayValue(this._evaluator.popOperand()); 159 | this.setState(global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR); 160 | }; 161 | 162 | // Adds the given binary operator. 163 | global.swisscalc.calc.calculator.prototype.addBinaryOperator = function(binaryOperator) { 164 | if (this._state == global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR) 165 | { 166 | this.pushDisplay(); 167 | this._evaluator.addBinaryOperator(binaryOperator); 168 | this._mainDisplay.setDisplayValue(this._evaluator.peekOperand()); 169 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR); 170 | } 171 | else if (this._state == global.swisscalc.calc.calculator.STATE_ENTERING_OPERAND) 172 | { 173 | this.pushDisplay(); 174 | this._evaluator.addBinaryOperator(binaryOperator); 175 | this._mainDisplay.setDisplayValue(this._evaluator.peekOperand()); 176 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR); 177 | } 178 | else if (this._state == global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR) 179 | { 180 | // If entering an operator, we must have already had one, so we can pop.. 181 | this._evaluator.popOperator(); 182 | this._evaluator.addBinaryOperator(binaryOperator); 183 | this._mainDisplay.setDisplayValue(this._evaluator.peekOperand()); 184 | this.setState(global.swisscalc.calc.calculator.STATE_ENTERING_OPERATOR); 185 | } 186 | }; 187 | 188 | // Returns the current display on the _mainDisplay. 189 | global.swisscalc.calc.calculator.prototype.getMainDisplay = function() { 190 | return this._mainDisplay.getCurrentDisplay(); 191 | }; 192 | 193 | // 194 | // *** MEMORY FUNCTIONS *** 195 | // 196 | 197 | // Clears the memory. 198 | global.swisscalc.calc.calculator.prototype.memoryClear = function() { 199 | this._memoryDisplay.memoryClear(); 200 | }; 201 | 202 | // Adds current display to memory. 203 | global.swisscalc.calc.calculator.prototype.memoryPlus = function() { 204 | var val = this._mainDisplay.getDisplayValue(); 205 | this._memoryDisplay.memoryPlus(val); 206 | }; 207 | 208 | // Subtracts current display from memory. 209 | global.swisscalc.calc.calculator.prototype.memoryMinus = function() { 210 | var val = this._mainDisplay.getDisplayValue(); 211 | this._memoryDisplay.memoryMinus(val); 212 | }; 213 | 214 | // Sets memory to the display. 215 | global.swisscalc.calc.calculator.prototype.memorySet = function() { 216 | var val = this._mainDisplay.getDisplayValue(); 217 | this._memoryDisplay.memorySet(val); 218 | }; 219 | 220 | // Displays memory on the display and waits for operator. 221 | global.swisscalc.calc.calculator.prototype.memoryRecall = function() { 222 | // Ignore if memory not set... 223 | if (!this._memoryDisplay.hasMemory()) 224 | return; 225 | 226 | var val = this._memoryDisplay.memoryRecall(); 227 | this._mainDisplay.setDisplayValue(val); 228 | this.setState(global.swisscalc.calc.calculator.STATE_AWAITING_OPERATOR); 229 | }; -------------------------------------------------------------------------------- /lib/swisscalc.calc.loanCalculator.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Class for implementing a loan calculator 7 | 8 | var swisscalc = swisscalc || {}; 9 | swisscalc.calc = swisscalc.calc || {}; 10 | swisscalc.calc.loanCalculator = function(termUnit) { 11 | this._termUnit = (typeof termUnit === "undefined") ? 1 : termUnit; // Default: TERM_UNIT_MONTHS 12 | this._loanAmount = new swisscalc.display.numericDisplay(); // Unit: Dollars 13 | this._loanTerm = new swisscalc.display.fixedPointDisplay(0); // Unit: Months 14 | this._downPayment = new swisscalc.display.numericDisplay(); // Unit: Dollars 15 | this._interestRate = new swisscalc.display.numericDisplay(); // Unit: Decimal (I.e. 0.01 == 1%) 16 | }; 17 | 18 | // Constants... 19 | swisscalc.calc.loanCalculator.TERM_UNIT_MONTHS = 1; 20 | swisscalc.calc.loanCalculator.TERM_UNIT_YEARS = 2; 21 | 22 | // Getters... 23 | swisscalc.calc.loanCalculator.prototype.getTermMonths = function() { var v = this._loanTerm.getDisplayValue(); return (this._termUnit == swisscalc.calc.loanCalculator.TERM_UNIT_MONTHS) ? v : v * 12; }; 24 | swisscalc.calc.loanCalculator.prototype.getTermYears = function() { var v = this._loanTerm.getDisplayValue(); return (this._termUnit == swisscalc.calc.loanCalculator.TERM_UNIT_YEARS) ? v : v / 12; }; 25 | swisscalc.calc.loanCalculator.prototype.getLoanAmount = function() { return this._loanAmount.getDisplayValue(); }; 26 | swisscalc.calc.loanCalculator.prototype.getDownPayment = function() { return this._downPayment.getDisplayValue(); }; 27 | swisscalc.calc.loanCalculator.prototype.getDownPaymentPercent = function() { return (this.getDownPayment() / this.getLoanAmount()) * 100.0; }; 28 | swisscalc.calc.loanCalculator.prototype.getInterestRateDecimal = function() { return this._interestRate.getDisplayValue() / 100.0; }; 29 | swisscalc.calc.loanCalculator.prototype.getInterestRatePercentage = function() { return this._interestRate.getDisplayValue(); }; 30 | 31 | // Setters... 32 | swisscalc.calc.loanCalculator.prototype.setTermMonths = function(term) { this._loanTerm.setDisplayValue(term); this._termUnit = swisscalc.calc.loanCalculator.TERM_UNIT_MONTHS; }; // Set term in months 33 | swisscalc.calc.loanCalculator.prototype.setTermYears = function(term) { this._loanTerm.setDisplayValue(term); this._termUnit = swisscalc.calc.loanCalculator.TERM_UNIT_YEARS; }; // Set term in years 34 | swisscalc.calc.loanCalculator.prototype.setLoanAmount = function(lAmount) { this._loanAmount.setDisplayValue(lAmount); }; // Set the loan amount 35 | swisscalc.calc.loanCalculator.prototype.setDownPayment = function(dPayment) { this._downPayment.setDisplayValue(dPayment); }; // Set the down payment 36 | swisscalc.calc.loanCalculator.prototype.setDownPaymentPercent = function(perc) { this._downPayment.setDisplayValue((perc/100.0) * this.getLoanAmount()); }; // Set down payment as percentage of loan 37 | swisscalc.calc.loanCalculator.prototype.setInterestRateDecimal = function(decimal) { this._interestRate.setDisplayValue(decimal * 100); }; // Set interest rate as a decimal 38 | swisscalc.calc.loanCalculator.prototype.setInterestRatePercentage = function(perc) { this._interestRate.setDisplayValue(perc); }; // Set interest rate by percentage (e.g. 12 == 12%) 39 | 40 | // Display functions... 41 | swisscalc.calc.loanCalculator.prototype.getTermDisplay = function() { return this._loanTerm.getCurrentDisplay(); }; 42 | swisscalc.calc.loanCalculator.prototype.getLoanAmountDisplay = function() { return swisscalc.lib.format.asUSCurrency(this.getLoanAmount()); }; 43 | swisscalc.calc.loanCalculator.prototype.getDownPaymentDisplay = function() { return swisscalc.lib.format.asUSCurrency(this.getDownPayment()); }; 44 | swisscalc.calc.loanCalculator.prototype.getInterestRateDisplay = function() { return this._interestRate.getCurrentDisplay() + "%"; }; 45 | 46 | // Returns the total monthly payment to be paid on this loan 47 | swisscalc.calc.loanCalculator.prototype.getMonthlyPayment = function() { 48 | var n = this.getTermMonths(); 49 | var i = this.getInterestRateDecimal() / 12.0; 50 | var A = this.getLoanAmount() - this.getDownPayment(); 51 | 52 | // If no interest and no term, return full amount... 53 | if (i === 0 && n === 0) 54 | return A; 55 | 56 | // If no term, just return full amount... 57 | if (n === 0) 58 | return A; 59 | 60 | // If no interest, just divide by terms... 61 | if (i === 0) 62 | return A / n; 63 | 64 | // Standard formula... 65 | return (i * A) / (1 - Math.pow(1 + i, -1 * n)); 66 | }; 67 | 68 | // Returns the total payment to be paid on this loan 69 | swisscalc.calc.loanCalculator.prototype.getTotalPayment = function(monthlyPayment) { 70 | var n = this.getTermMonths(); 71 | return monthlyPayment * n; 72 | }; 73 | 74 | // Returns the total interest to be paid on this loan 75 | swisscalc.calc.loanCalculator.prototype.getTotalInterest = function(monthlyPayment) { 76 | var n = this.getTermMonths(); 77 | var A = this.getLoanAmount() - this.getDownPayment(); 78 | 79 | // If there was no term, just return 0 interest 80 | if (n === 0) return 0; 81 | 82 | return monthlyPayment * n - A; 83 | }; 84 | 85 | // Returns a div containing formatted output. 86 | swisscalc.calc.loanCalculator.prototype.getDisplay = function() { 87 | var div = document.createElement("div"); 88 | this.addDisplay(div); 89 | return div; 90 | }; 91 | 92 | // Adds the formatted output to the given div. 93 | swisscalc.calc.loanCalculator.prototype.addDisplay = function(div) { 94 | // Perform calculations... 95 | var monthlyPayment = this.getMonthlyPayment(); 96 | var totalPayment = this.getTotalPayment(monthlyPayment); 97 | var totalInterest = this.getTotalInterest(monthlyPayment); 98 | 99 | // Set formatting... 100 | monthlyPayment = swisscalc.lib.format.asUSCurrency(monthlyPayment); 101 | totalPayment = swisscalc.lib.format.asUSCurrency(totalPayment); 102 | totalInterest = swisscalc.lib.format.asUSCurrency(totalInterest); 103 | 104 | // Build output div... 105 | div.style.fontFamily = "\"Lucida Console\",Monaco,monospace"; 106 | div.style.whiteSpace = "pre"; 107 | div.innerHTML = ""; 108 | div.innerHTML += " Monthly: " + monthlyPayment + "
"; 109 | div.innerHTML += " Interest: " + totalInterest + "
"; 110 | div.innerHTML += " Total: " + totalPayment; 111 | }; -------------------------------------------------------------------------------- /lib/swisscalc.calc.tipCalculator.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Class for implementing a tip calculator 7 | 8 | var swisscalc = swisscalc || {}; 9 | swisscalc.calc = swisscalc.calc || {}; 10 | swisscalc.calc.tipCalculator = function() { 11 | this._subtotal = new swisscalc.display.fixedPointDisplay(2); // Use fixed point display to handle keypad inputs 12 | this._tipPercent = 0.15; // Store the actual value 13 | }; 14 | 15 | // Getters... 16 | swisscalc.calc.tipCalculator.prototype.getSubtotalValue = function() { return this._subtotal.getDisplayValue(); }; 17 | swisscalc.calc.tipCalculator.prototype.getTipValueDecimal = function() { return this._tipPercent; }; 18 | swisscalc.calc.tipCalculator.prototype.getTipValuePercentage = function() { return this._tipPercent * 100.0; }; 19 | 20 | // Setters... 21 | swisscalc.calc.tipCalculator.prototype.setSubtotalValue = function(value) { this._subtotal.setDisplayValue(value); }; 22 | swisscalc.calc.tipCalculator.prototype.setTipValueDecimal = function(decimal) { this._tipPercent = decimal; }; 23 | swisscalc.calc.tipCalculator.prototype.setTipValuePercentage = function(perc) { this._tipPercent = perc / 100.0; }; 24 | 25 | // Display functions... 26 | swisscalc.calc.tipCalculator.prototype.getSubtotalDisplay = function() { return swisscalc.lib.format.asUSCurrency(this._subtotal.getDisplayValue()); }; 27 | swisscalc.calc.tipCalculator.prototype.getTipPercentDisplay = function() { return (this._tipPercent * 100.0).toFixed(1) + "%"; }; 28 | swisscalc.calc.tipCalculator.prototype.getTipAmountDisplay = function() { return swisscalc.lib.format.asUSCurrency(this.getTipAmount()); }; 29 | swisscalc.calc.tipCalculator.prototype.getTipCombinedDisplay = function() { return this.getTipPercentDisplay() + " " + this.getTipAmountDisplay(); }; 30 | swisscalc.calc.tipCalculator.prototype.getTotalDisplay = function() { return swisscalc.lib.format.asUSCurrency(this.getTotal()); }; 31 | 32 | // Returns the tip amount (in dollars) 33 | swisscalc.calc.tipCalculator.prototype.getTipAmount = function() { 34 | var subtotal = this.getSubtotalValue(); 35 | return subtotal * this._tipPercent; 36 | }; 37 | 38 | // Returns the bill total including tip (in dollars) 39 | swisscalc.calc.tipCalculator.prototype.getTotal = function() { 40 | var tipAmount = this.getTipAmount(); 41 | return this.getSubtotalValue() + tipAmount; 42 | }; -------------------------------------------------------------------------------- /lib/swisscalc.display.fixedPointDisplay.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Class for displaying a fixed-point number. 7 | 8 | var swisscalc = swisscalc || {}; 9 | swisscalc.display = swisscalc.display || {}; 10 | 11 | // Constructor. 12 | // numDecimalPlaces: Number of characters to show past decimal (default: 2) 13 | // maxLength: Maximum number of characters to display (default: 20) 14 | swisscalc.display.fixedPointDisplay = function(numDecimalPlaces, maxLength) { 15 | this._display = ""; 16 | this._isNegative = false; 17 | this._numDecimalPlaces = (typeof numDecimalPlaces === "undefined") ? 2 : numDecimalPlaces; 18 | this._maxLength = (typeof maxLength === "undefined") ? 20 : maxLength; 19 | }; 20 | 21 | // Returns current display 22 | swisscalc.display.fixedPointDisplay.prototype.getCurrentDisplay = function() { 23 | var str = ""; 24 | var len = this._display.length; // Number of characters 25 | var num = this._numDecimalPlaces; // Number of decimal places 26 | 27 | // If no decimal places, handle separately... 28 | if (num === 0) { 29 | if (len === 0) return "0"; // If no characters, return 0 30 | if (this._isNegative) return "-" + this._display; // If negative, add a "-" 31 | return this._display; // Otherwise, display as-is 32 | } 33 | 34 | if (len > num) { 35 | var p1 = this._display.substring(0, len - num); 36 | var p2 = this._display.substring(len - num, len); 37 | str = p1 + "." + p2; 38 | } else if (len == num) { 39 | str = "0." + this._display; 40 | } else if (len < num) { 41 | str = "0."; 42 | for (var i = 0; i < num - len; i++) str += "0"; 43 | str += this._display; 44 | } 45 | 46 | if (this._isNegative) str = "-" + str; 47 | 48 | return str; 49 | }; 50 | 51 | // Adds the given character to the display, if appropriate. 52 | // The only valid digits are: 0...9, . (decimal). Must be a string. 53 | swisscalc.display.fixedPointDisplay.prototype.addDigit = function(digit) { 54 | // Don't go past maximum length... 55 | if (this._display.length >= this._maxLength) 56 | return; 57 | 58 | // Don't add decimals even though it says you can... 59 | if (digit == ".") 60 | return; 61 | 62 | // If display is empty, don't add any 0's... 63 | if (this._display.length === 0 && digit == "0") 64 | return; 65 | 66 | // Add the digit to the end (note: '.' will result in '0.')... 67 | this._display += digit; 68 | }; 69 | 70 | // Adds or removes the negative sign 71 | swisscalc.display.fixedPointDisplay.prototype.negate = function() { 72 | this._isNegative = !this._isNegative; 73 | }; 74 | 75 | // Removes the last character if possible 76 | swisscalc.display.fixedPointDisplay.prototype.backspace = function() { 77 | var len = this._display.length; 78 | if (len == 1) 79 | this._display = ""; 80 | else if (len == 2 && this._display.charAt(0) == "-") 81 | this._display = ""; 82 | else 83 | this._display = this._display.substring(0, len - 1); 84 | }; 85 | 86 | // Clears the display 87 | swisscalc.display.fixedPointDisplay.prototype.clear = function() { 88 | this._display = ""; 89 | }; 90 | 91 | // Returns _display as a numeric value 92 | swisscalc.display.fixedPointDisplay.prototype.getDisplayValue = function() { 93 | var sDisplay = this.getCurrentDisplay(); 94 | return parseFloat(sDisplay); 95 | }; -------------------------------------------------------------------------------- /lib/swisscalc.display.memoryDisplay.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Class for displaying/storing the memory on a calculator. 7 | 8 | global.swisscalc = global.swisscalc || {}; 9 | global.swisscalc.display = global.swisscalc.display || {}; 10 | global.swisscalc.display.memoryDisplay = function() { 11 | this._display = ""; 12 | this._memValue = 0; 13 | this._hasMemory = false; 14 | }; 15 | 16 | // Returns true if memory is set. 17 | global.swisscalc.display.memoryDisplay.prototype.hasMemory = function() { 18 | return this._hasMemory; 19 | }; 20 | 21 | // Returns current display 22 | global.swisscalc.display.memoryDisplay.prototype.getCurrentDisplay = function() { 23 | return this._display; 24 | }; 25 | 26 | // Returns memory value. 27 | global.swisscalc.display.memoryDisplay.prototype.memoryRecall = function() { 28 | return this._memValue; 29 | }; 30 | 31 | // Sets the memory to the given value. 32 | global.swisscalc.display.memoryDisplay.prototype.memorySet = function(val) { 33 | this._hasMemory = true; 34 | this._memValue = val; 35 | this._display = "M"; 36 | }; 37 | 38 | // Adds given number to the memory. 39 | global.swisscalc.display.memoryDisplay.prototype.memoryPlus = function(val) { 40 | this._hasMemory = true; 41 | this._memValue += val; 42 | this._display = "M"; 43 | }; 44 | 45 | // Subtracts the given value from memory. 46 | global.swisscalc.display.memoryDisplay.prototype.memoryMinus = function(val) { 47 | this._hasMemory = true; 48 | this._memValue -= val; 49 | this._display = "M"; 50 | }; 51 | 52 | // Clears the memory. 53 | global.swisscalc.display.memoryDisplay.prototype.memoryClear = function() { 54 | this._hasMemory = false; 55 | this._memValue = 0; 56 | this._display = ""; 57 | }; 58 | -------------------------------------------------------------------------------- /lib/swisscalc.display.numericDisplay.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Class for displaying a floating-point number. 7 | 8 | global.swisscalc = global.swisscalc || {}; 9 | global.swisscalc.display = global.swisscalc.display || {}; 10 | 11 | // Constructor. 12 | // groupDigits: Should digits be grouped by a comma (true/false) (default: true) 13 | // maxLength: Maximum number of characters to display (default: 20) 14 | global.swisscalc.display.numericDisplay = function(groupDigits, maxLength) { 15 | this._display = "0"; 16 | this._groupDigits = (typeof groupDigits === "undefined") ? true : groupDigits; 17 | this._maxLength = (typeof maxLength === "undefined") ? 20 : maxLength; 18 | }; 19 | 20 | // Returns current display 21 | global.swisscalc.display.numericDisplay.prototype.getCurrentDisplay = function() { 22 | return (this._groupDigits) 23 | ? global.swisscalc.lib.format.groupDigits(this._display) 24 | : this._display; 25 | }; 26 | 27 | // Adds the given character to the display, if appropriate. 28 | // The only valid digits are: 0...9, . (decimal). Must be a string. 29 | global.swisscalc.display.numericDisplay.prototype.addDigit = function(digit) { 30 | // Don't go past maximum length... 31 | if (this._display.length >= this._maxLength) 32 | return; 33 | 34 | // Don't add multiple decimals... 35 | if (digit == "." && this._display.indexOf(".") >= 0) 36 | return; 37 | 38 | // If not a decimal and display is empty, remove 0... 39 | if (digit != "." && this._display == "0") 40 | this._display = ""; 41 | 42 | // Add the digit to the end (note: '.' will result in '0.')... 43 | this._display += digit; 44 | }; 45 | 46 | // Adds or removes the negative sign 47 | global.swisscalc.display.numericDisplay.prototype.negate = function() { 48 | var fChar = this._display.charAt(0); 49 | this._display = (fChar == "-") ? this._display.substring(1) : "-" + this._display; 50 | }; 51 | 52 | // Removes the last character if possible 53 | global.swisscalc.display.numericDisplay.prototype.backspace = function() { 54 | var len = this._display.length; 55 | if (len == 1) 56 | this._display = "0"; 57 | else if (len == 2 && this._display.charAt(0) == "-") 58 | this._display = "0"; 59 | else 60 | this._display = this._display.substring(0, len - 1); 61 | }; 62 | 63 | // Clears the display 64 | global.swisscalc.display.numericDisplay.prototype.clear = function() { 65 | this._display = "0"; 66 | }; 67 | 68 | // Returns _display as a numeric value 69 | global.swisscalc.display.numericDisplay.prototype.getDisplayValue = function() { 70 | return parseFloat(this._display); 71 | }; 72 | 73 | // Formats the value and sets the display. "val" should be a number. 74 | global.swisscalc.display.numericDisplay.prototype.setDisplayValue = function(val) { 75 | // TODO: May need to do some formatting/rounding... 76 | this._display = val.toString(); 77 | }; 78 | -------------------------------------------------------------------------------- /lib/swisscalc.lib.format.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | global.swisscalc = global.swisscalc || {}; 7 | global.swisscalc.lib = global.swisscalc.lib || {}; 8 | global.swisscalc.lib.format = function() {}; 9 | 10 | // Adds commas to the given numeric-string to group digits. 11 | global.swisscalc.lib.format.groupDigits = function(number) { 12 | // http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript/2901298#2901298 13 | var parts = number.toString().split("."); 14 | parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); 15 | return parts.join("."); 16 | }; 17 | 18 | // Formats the given number as a US currency string 19 | global.swisscalc.lib.format.asUSCurrency = function(number) { 20 | var s = number.toFixed(2); 21 | s = global.swisscalc.lib.format.groupDigits(s); 22 | return (s.charAt(0) == "-") ? "-$" + s.substring(1) : "$" + s; 23 | }; -------------------------------------------------------------------------------- /lib/swisscalc.lib.operator.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Generic operator class. Implementation of individual operators will be defined in the OperatorCache. 7 | // "evaluate" should be a function that takes a global.swisscalc.lib.shuntingYard instance as a parameter and returns the result. 8 | global.swisscalc = global.swisscalc || {}; 9 | global.swisscalc.lib = global.swisscalc.lib || {}; 10 | global.swisscalc.lib.operator = function(arity, associativity, precedence, numOperands, isOpenParen, isCloseParen, evaluate) { 11 | this.Arity = arity; 12 | this.Associativity = associativity; 13 | this.Precedence = precedence; 14 | this.NumOperands = numOperands; 15 | this.IsOpenParen = isOpenParen; 16 | this.IsCloseParen = isCloseParen; 17 | this.evaluate = evaluate; 18 | }; 19 | 20 | // Constants 21 | global.swisscalc.lib.operator.ARITY_NULLARY = 0; 22 | global.swisscalc.lib.operator.ARITY_UNARY = 1; 23 | global.swisscalc.lib.operator.ARITY_BINARY = 2; 24 | global.swisscalc.lib.operator.ASSOCIATIVITY_NONE = 0; 25 | global.swisscalc.lib.operator.ASSOCIATIVITY_RIGHT = 1; 26 | global.swisscalc.lib.operator.ASSOCIATIVITY_LEFT = 2; 27 | 28 | // Static functions 29 | global.swisscalc.lib.operator.degreesToRadians = function(degrees) { return degrees * (Math.PI / 180.0); }; 30 | global.swisscalc.lib.operator.radiansToDegrees = function(radians) { return radians * (180.0 / Math.PI); }; 31 | 32 | // Returns true if precedence is higher than given operator 33 | global.swisscalc.lib.operator.prototype.isHigherPrecedence = function(operator) { 34 | if (this.Precedence == operator.Precedence) 35 | return (this.Associativity == global.swisscalc.lib.operator.ASSOCIATIVITY_LEFT); 36 | return (this.Precedence > operator.Precedence); 37 | }; -------------------------------------------------------------------------------- /lib/swisscalc.lib.operatorCache.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Cache for storing operators (rather than instantiating new ones). 7 | // To create a new operator, add it to the cache as a new global.swisscalc.lib.operator, filling in the properties and evaluate() function. 8 | global.swisscalc = global.swisscalc || {}; 9 | global.swisscalc.lib = global.swisscalc.lib || {}; 10 | global.swisscalc.lib.operatorCache = function() { }; 11 | 12 | global.swisscalc.lib.operatorCache.AdditionOperator = new global.swisscalc.lib.operator( 13 | global.swisscalc.lib.operator.ARITY_BINARY, global.swisscalc.lib.operator.ASSOCIATIVITY_LEFT, 2, 2, false, false, function(sy) { 14 | var op1 = sy.popOperand(); 15 | var op2 = sy.popOperand(); 16 | return op1 + op2; 17 | }); 18 | 19 | global.swisscalc.lib.operatorCache.SubtractionOperator = new global.swisscalc.lib.operator( 20 | global.swisscalc.lib.operator.ARITY_BINARY, global.swisscalc.lib.operator.ASSOCIATIVITY_LEFT, 2, 2, false, false, function(sy) { 21 | var op1 = sy.popOperand(); 22 | var op2 = sy.popOperand(); 23 | return op2 - op1; 24 | }); 25 | 26 | global.swisscalc.lib.operatorCache.MultiplicationOperator = new global.swisscalc.lib.operator( 27 | global.swisscalc.lib.operator.ARITY_BINARY, global.swisscalc.lib.operator.ASSOCIATIVITY_LEFT, 3, 2, false, false, function(sy) { 28 | var op1 = sy.popOperand(); 29 | var op2 = sy.popOperand(); 30 | return op1 * op2; 31 | }); 32 | 33 | global.swisscalc.lib.operatorCache.DivisionOperator = new global.swisscalc.lib.operator( 34 | global.swisscalc.lib.operator.ARITY_BINARY, global.swisscalc.lib.operator.ASSOCIATIVITY_LEFT, 3, 2, false, false, function(sy) { 35 | var op1 = sy.popOperand(); 36 | var op2 = sy.popOperand(); 37 | return op2 / op1; 38 | }); 39 | 40 | global.swisscalc.lib.operatorCache.ModulusOperator = new global.swisscalc.lib.operator( 41 | global.swisscalc.lib.operator.ARITY_BINARY, global.swisscalc.lib.operator.ASSOCIATIVITY_LEFT, 3, 2, false, false, function(sy) { 42 | var op1 = sy.popOperand(); 43 | var op2 = sy.popOperand(); 44 | return op2 % op1; 45 | }); 46 | 47 | global.swisscalc.lib.operatorCache.ExponentialOperator = new global.swisscalc.lib.operator( 48 | global.swisscalc.lib.operator.ARITY_BINARY, global.swisscalc.lib.operator.ASSOCIATIVITY_RIGHT, 4, 2, false, false, function(sy) { 49 | var op1 = sy.popOperand(); 50 | var op2 = sy.popOperand(); 51 | return Math.pow(op2, op1); 52 | }); 53 | 54 | global.swisscalc.lib.operatorCache.RootOperator = new global.swisscalc.lib.operator( 55 | global.swisscalc.lib.operator.ARITY_BINARY, global.swisscalc.lib.operator.ASSOCIATIVITY_RIGHT, 4, 2, false, false, function(sy) { 56 | var op1 = sy.popOperand(); 57 | var op2 = sy.popOperand(); 58 | return Math.pow(op2, 1.0 / op1); 59 | }); 60 | 61 | global.swisscalc.lib.operatorCache.EEOperator = new global.swisscalc.lib.operator( 62 | global.swisscalc.lib.operator.ARITY_BINARY, global.swisscalc.lib.operator.ASSOCIATIVITY_RIGHT, 10, 2, false, false, function(sy) { 63 | var op1 = sy.popOperand(); 64 | var op2 = sy.popOperand(); 65 | return op2 * Math.pow(10.0, op1); 66 | }); 67 | 68 | global.swisscalc.lib.operatorCache.PiOperator = new global.swisscalc.lib.operator( 69 | global.swisscalc.lib.operator.ARITY_NULLARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 0, false, false, function(sy) { 70 | return Math.PI; 71 | }); 72 | 73 | global.swisscalc.lib.operatorCache.EOperator = new global.swisscalc.lib.operator( 74 | global.swisscalc.lib.operator.ARITY_NULLARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 0, false, false, function(sy) { 75 | return Math.E; 76 | }); 77 | 78 | global.swisscalc.lib.operatorCache.RandomOperator = new global.swisscalc.lib.operator( 79 | global.swisscalc.lib.operator.ARITY_NULLARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 0, false, false, function(sy) { 80 | return Math.random(); 81 | }); 82 | 83 | global.swisscalc.lib.operatorCache.NegateOperator = new global.swisscalc.lib.operator( 84 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 85 | var op = sy.popOperand(); 86 | return -1.0 * op; 87 | }); 88 | 89 | global.swisscalc.lib.operatorCache.InverseOperator = new global.swisscalc.lib.operator( 90 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 91 | var op = sy.popOperand(); 92 | return 1.0 / op; 93 | }); 94 | 95 | global.swisscalc.lib.operatorCache.EExponentialOperator = new global.swisscalc.lib.operator( 96 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 97 | var op = sy.popOperand(); 98 | return Math.pow(Math.E, op); 99 | }); 100 | 101 | global.swisscalc.lib.operatorCache.TenExponentialOperator = new global.swisscalc.lib.operator( 102 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 103 | var op = sy.popOperand(); 104 | return Math.pow(10.0, op); 105 | }); 106 | 107 | global.swisscalc.lib.operatorCache.SquareRootOperator = new global.swisscalc.lib.operator( 108 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 109 | var op = sy.popOperand(); 110 | return Math.sqrt(op); 111 | }); 112 | 113 | global.swisscalc.lib.operatorCache.CubeRootOperator = new global.swisscalc.lib.operator( 114 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 115 | var op = sy.popOperand(); 116 | return Math.cbrt(op); 117 | }); 118 | 119 | global.swisscalc.lib.operatorCache.XSquaredOperator = new global.swisscalc.lib.operator( 120 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 121 | var op = sy.popOperand(); 122 | return op * op; 123 | }); 124 | 125 | global.swisscalc.lib.operatorCache.XCubedOperator = new global.swisscalc.lib.operator( 126 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 127 | var op = sy.popOperand(); 128 | return Math.pow(op, 3); 129 | }); 130 | 131 | global.swisscalc.lib.operatorCache.PercentOperator = new global.swisscalc.lib.operator( 132 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 133 | var op = sy.popOperand(); 134 | return op / 100.0; 135 | }); 136 | 137 | global.swisscalc.lib.operatorCache.LogBase10Operator = new global.swisscalc.lib.operator( 138 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 139 | var op = sy.popOperand(); 140 | return Math.log(op) / Math.LN10; 141 | }); 142 | 143 | global.swisscalc.lib.operatorCache.NaturalLogOperator = new global.swisscalc.lib.operator( 144 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 145 | var op = sy.popOperand(); 146 | return Math.log(op); 147 | }); 148 | 149 | global.swisscalc.lib.operatorCache.SineOperator = new global.swisscalc.lib.operator( 150 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 151 | var op = sy.popOperand(); 152 | return Math.sin(op); 153 | }); 154 | 155 | global.swisscalc.lib.operatorCache.CosineOperator = new global.swisscalc.lib.operator( 156 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 157 | var op = sy.popOperand(); 158 | return Math.cos(op); 159 | }); 160 | 161 | global.swisscalc.lib.operatorCache.TangentOperator = new global.swisscalc.lib.operator( 162 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 163 | var op = sy.popOperand(); 164 | return Math.tan(op); 165 | }); 166 | 167 | global.swisscalc.lib.operatorCache.ArcSineOperator = new global.swisscalc.lib.operator( 168 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 169 | var op = sy.popOperand(); 170 | return Math.asin(op); 171 | }); 172 | 173 | global.swisscalc.lib.operatorCache.ArcCosineOperator = new global.swisscalc.lib.operator( 174 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 175 | var op = sy.popOperand(); 176 | return Math.acos(op); 177 | }); 178 | 179 | global.swisscalc.lib.operatorCache.ArcTangentOperator = new global.swisscalc.lib.operator( 180 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 181 | var op = sy.popOperand(); 182 | return Math.atan(op); 183 | }); 184 | 185 | global.swisscalc.lib.operatorCache.SineDegreesOperator = new global.swisscalc.lib.operator( 186 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 187 | var op = sy.popOperand(); 188 | return Math.sin(global.swisscalc.lib.operator.degreesToRadians(op)); 189 | }); 190 | 191 | global.swisscalc.lib.operatorCache.CosineDegreesOperator = new global.swisscalc.lib.operator( 192 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 193 | var op = sy.popOperand(); 194 | return Math.cos(global.swisscalc.lib.operator.degreesToRadians(op)); 195 | }); 196 | 197 | global.swisscalc.lib.operatorCache.TangentDegreesOperator = new global.swisscalc.lib.operator( 198 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 199 | var op = sy.popOperand(); 200 | return Math.tan(global.swisscalc.lib.operator.degreesToRadians(op)); 201 | }); 202 | 203 | global.swisscalc.lib.operatorCache.ArcSineDegreesOperator = new global.swisscalc.lib.operator( 204 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 205 | var op = sy.popOperand(); 206 | return global.swisscalc.lib.operator.radiansToDegrees(Math.asin(op)); 207 | }); 208 | 209 | global.swisscalc.lib.operatorCache.ArcCosineDegreesOperator = new global.swisscalc.lib.operator( 210 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 211 | var op = sy.popOperand(); 212 | return global.swisscalc.lib.operator.radiansToDegrees(Math.acos(op)); 213 | }); 214 | 215 | global.swisscalc.lib.operatorCache.ArcTangentDegreesOperator = new global.swisscalc.lib.operator( 216 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 217 | var op = sy.popOperand(); 218 | return global.swisscalc.lib.operator.radiansToDegrees(Math.atan(op)); 219 | }); 220 | 221 | global.swisscalc.lib.operatorCache.HyperbolicSineOperator = new global.swisscalc.lib.operator( 222 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 223 | var op = sy.popOperand(); 224 | return 0.5 * (Math.pow(Math.E, op) - Math.pow(Math.E, -1.0 * op)); 225 | }); 226 | 227 | global.swisscalc.lib.operatorCache.HyperbolicCosineOperator = new global.swisscalc.lib.operator( 228 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 229 | var op = sy.popOperand(); 230 | return 0.5 * (Math.pow(Math.E, op) + Math.pow(Math.E, -1.0 * op)); 231 | }); 232 | 233 | global.swisscalc.lib.operatorCache.HyperbolicTangentOperator = new global.swisscalc.lib.operator( 234 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 235 | var op = sy.popOperand(); 236 | return (1 - Math.pow(Math.E, -2.0 * op)) / (1 + Math.pow(Math.E, -2.0 * op)); 237 | }); 238 | 239 | global.swisscalc.lib.operatorCache.InverseHyperbolicSineOperator = new global.swisscalc.lib.operator( 240 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 241 | var op = sy.popOperand(); 242 | return Math.log(op + Math.sqrt((op * op) + 1)); 243 | }); 244 | 245 | global.swisscalc.lib.operatorCache.InverseHyperbolicCosineOperator = new global.swisscalc.lib.operator( 246 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 247 | var op = sy.popOperand(); 248 | return Math.log(op + Math.sqrt((op * op) - 1)); 249 | }); 250 | 251 | global.swisscalc.lib.operatorCache.InverseHyperbolicTangentOperator = new global.swisscalc.lib.operator( 252 | global.swisscalc.lib.operator.ARITY_UNARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 1, false, false, function(sy) { 253 | var op = sy.popOperand(); 254 | return 0.5 * Math.log((1 + op) / (1 - op)); 255 | }); 256 | 257 | global.swisscalc.lib.operatorCache.OpenParenOperator = new global.swisscalc.lib.operator( 258 | global.swisscalc.lib.operator.ARITY_NULLARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 0, true, false, function(sy) { 259 | console.error("Cannot evaluate open parenthesis."); 260 | }); 261 | 262 | global.swisscalc.lib.operatorCache.CloseParenOperator = new global.swisscalc.lib.operator( 263 | global.swisscalc.lib.operator.ARITY_NULLARY, global.swisscalc.lib.operator.ASSOCIATIVITY_NONE, 0, 0, false, true, function(sy) { 264 | console.error("Cannot evaluate close parenthesis."); 265 | }); -------------------------------------------------------------------------------- /lib/swisscalc.lib.shuntingYard.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eric Morgan 3 | // Copyright (c) 2014. 4 | // 5 | 6 | // Implementation of the Shunting Yard algorithm. 7 | global.swisscalc = global.swisscalc || {}; 8 | global.swisscalc.lib = global.swisscalc.lib || {}; 9 | global.swisscalc.lib.shuntingYard = function() { 10 | this._numOpenParen = 0; 11 | this._operands = []; 12 | this._operators = []; 13 | this._actionBuffer = []; 14 | }; 15 | 16 | // Peeks highest value on stack. Returns 0 if empty. 17 | global.swisscalc.lib.shuntingYard.prototype.peekOperand = function() { 18 | var len = this._operands.length; 19 | return (len !== 0) ? this._operands[len-1] : 0.0; 20 | }; 21 | 22 | // Pops highest value on stack. Returns 0 if empty. 23 | global.swisscalc.lib.shuntingYard.prototype.popOperand = function() { 24 | var len = this._operands.length; 25 | return (len !== 0) ? this._operands.pop() : 0.0; 26 | }; 27 | 28 | // Returns number of operands. 29 | global.swisscalc.lib.shuntingYard.prototype.numOperands = function() { 30 | return this._operands.length; 31 | }; 32 | 33 | // Pops highest operator on stack. 34 | global.swisscalc.lib.shuntingYard.prototype.popOperator = function() { 35 | return this._actionBuffer.pop(); 36 | }; 37 | 38 | // Returns number of operators. 39 | global.swisscalc.lib.shuntingYard.prototype.numOperators = function() { 40 | return this._actionBuffer.length; 41 | }; 42 | 43 | // Returns true if currently evaluating sub-expression. 44 | global.swisscalc.lib.shuntingYard.prototype.inSubExpression = function() { 45 | return this._numOpenParen > 0; 46 | }; 47 | 48 | // Clears all stacks. 49 | global.swisscalc.lib.shuntingYard.prototype.clear = function() { 50 | this._operands.length = 0; 51 | this._operators.length = 0; 52 | this._actionBuffer.length = 0; 53 | }; 54 | 55 | // Empties the stack and returns the final evaluation. 56 | global.swisscalc.lib.shuntingYard.prototype.evaluate = function() { 57 | // Push all _actionBuffer to _operators... 58 | for (var i = 0; i < this._actionBuffer.length; i++) 59 | this._operators.push(this._actionBuffer[i]); 60 | this._actionBuffer.length = 0; 61 | 62 | // Evaluate all _operators... 63 | while (this._operators.length > 0) { 64 | var operator = this._operators.pop(); 65 | this.applyOperator(operator); 66 | } 67 | 68 | // Check for errors and return result... 69 | if (this._operands.length != 1) 70 | console.error("Invalid operand length (" + this._operands.length + ")"); 71 | 72 | return this._operands.pop(); 73 | }; 74 | 75 | // Evaluates the given operator and adds result to _operands. 76 | global.swisscalc.lib.shuntingYard.prototype.applyOperator = function(operator) { 77 | var val = operator.evaluate(this); 78 | this.addOperand(val); 79 | }; 80 | 81 | // Adds an operand to the stack. 82 | global.swisscalc.lib.shuntingYard.prototype.addOperand = function(operand) { 83 | this._operands.push(operand); 84 | }; 85 | 86 | // Adds the given operator. 87 | global.swisscalc.lib.shuntingYard.prototype.addOperator = function(operator) { 88 | if (operator.IsOpenParen) { 89 | this.addOpenParen(operator); 90 | } else if (operator.IsCloseParen) { 91 | this.addCloseParen(operator); 92 | } else if (operator.Arity == global.swisscalc.lib.operator.ARITY_NULLARY) { 93 | this.addNullaryOperator(operator); 94 | } else if (operator.Arity == global.swisscalc.lib.operator.ARITY_UNARY) { 95 | this.addUnaryOperator(operator); 96 | } else if (operator.Arity == global.swisscalc.lib.operator.ARITY_BINARY) { 97 | this.addBinaryOperator(operator); 98 | } 99 | }; 100 | 101 | // Evaluates the NullaryOperator and pushes result to stack. 102 | global.swisscalc.lib.shuntingYard.prototype.addNullaryOperator = function(operator) { 103 | this.applyOperator(operator); 104 | }; 105 | 106 | // Evaluates the NullaryOperator and pushes result to stack. 107 | global.swisscalc.lib.shuntingYard.prototype.addUnaryOperator = function(operator) { 108 | this.applyOperator(operator); 109 | }; 110 | 111 | // First adds operator to _actionBuffer before committing to anything. 112 | global.swisscalc.lib.shuntingYard.prototype.addBinaryOperator = function(operator) { 113 | // If not parenthesis, perform precedence checks as usual... 114 | while (this._actionBuffer.length > 0) 115 | { 116 | // If previous is not higher, exit... 117 | var abLen = this._actionBuffer.length; 118 | if (!this._actionBuffer[abLen-1].isHigherPrecedence(operator)) 119 | break; 120 | 121 | var prevOperator = this._actionBuffer.pop(); 122 | this.applyOperator(prevOperator); 123 | } 124 | 125 | this._actionBuffer.push(operator); 126 | }; 127 | 128 | // Adds the open parenthesis operator. Just adds to _actionBuffer. 129 | global.swisscalc.lib.shuntingYard.prototype.addOpenParen = function(operator) { 130 | this._actionBuffer.push(operator); 131 | this._numOpenParen++; 132 | }; 133 | 134 | // Adds the close parenthesis operator. Pops operators until open is reached. 135 | global.swisscalc.lib.shuntingYard.prototype.addCloseParen = function(operator) { 136 | // Ignore if no open parentheses... 137 | if (this._numOpenParen === 0) 138 | return; 139 | 140 | this._numOpenParen--; 141 | while (this._actionBuffer.length > 0) 142 | { 143 | // If encountered an open paren, return... 144 | var nextOperator = this._actionBuffer.pop(); 145 | if (nextOperator.IsOpenParen) 146 | return; 147 | 148 | // Evaluate the operator and then push it as an operand... 149 | this.applyOperator(nextOperator); 150 | } 151 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "private": true, 4 | "dependencies": { 5 | "expo": "^26.0.0", 6 | "react": "16.3.0-alpha.1", 7 | "react-native": "https://github.com/expo/react-native/archive/sdk-26.0.0.tar.gz" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /screens/CalculatorScreen.js: -------------------------------------------------------------------------------- 1 | 2 | require("../lib/swisscalc.lib.format.js"); 3 | require("../lib/swisscalc.lib.operator.js"); 4 | require("../lib/swisscalc.lib.operatorCache.js"); 5 | require("../lib/swisscalc.lib.shuntingYard.js"); 6 | require("../lib/swisscalc.display.numericDisplay.js"); 7 | require("../lib/swisscalc.display.memoryDisplay.js"); 8 | require("../lib/swisscalc.calc.calculator.js"); 9 | 10 | import React from 'react'; 11 | import { StyleSheet, Dimensions, PanResponder, View, Text } from 'react-native'; 12 | import { CalcDisplay, CalcButton } from './../components'; 13 | 14 | export default class CalculatorScreen extends React.Component { 15 | 16 | constructor(props) { 17 | super(props); 18 | this.state = { 19 | display: "0", 20 | orientation: "portrait", // "portrait" or "landscape" 21 | } 22 | 23 | // Initialize calculator... 24 | this.oc = global.swisscalc.lib.operatorCache; 25 | this.calc = new global.swisscalc.calc.calculator(); 26 | 27 | // Listen for orientation changes... 28 | Dimensions.addEventListener('change', () => { 29 | const { width, height } = Dimensions.get("window"); 30 | var orientation = (width > height) ? "landscape" : "portrait"; 31 | this.setState({ orientation: orientation }); 32 | }); 33 | 34 | // Setup gestures... 35 | this.panResponder = PanResponder.create({ 36 | onStartShouldSetPanResponder: (evt, gestureState) => true, 37 | onStartShouldSetPanResponderCapture: (evt, gestureState) => true, 38 | onMoveShouldSetPanResponder: (evt, gestureState) => true, 39 | onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, 40 | onPanResponderMove: (evt, gestureState) => { }, 41 | onPanResponderRelease: (evt, gestureState) => { 42 | if (Math.abs(gestureState.dx) >= 50) { 43 | this.onBackspacePress(); 44 | } 45 | }, 46 | }) 47 | } 48 | 49 | onDigitPress = (digit) => { 50 | this.calc.addDigit(digit); 51 | this.setState({ display: this.calc.getMainDisplay() }); 52 | } 53 | 54 | onUnaryOperatorPress = (operator) => { 55 | this.calc.addUnaryOperator(operator); 56 | this.setState({ display: this.calc.getMainDisplay() }); 57 | } 58 | 59 | onBinaryOperatorPress = (operator) => { 60 | this.calc.addBinaryOperator(operator); 61 | this.setState({ display: this.calc.getMainDisplay() }); 62 | } 63 | 64 | onEqualsPress = () => { 65 | this.calc.equalsPressed(); 66 | this.setState({ display: this.calc.getMainDisplay() }); 67 | } 68 | 69 | onClearPress = () => { 70 | this.calc.clear(); 71 | this.setState({ display: this.calc.getMainDisplay() }); 72 | } 73 | 74 | onPlusMinusPress = () => { 75 | this.calc.negate(); 76 | this.setState({ display: this.calc.getMainDisplay() }); 77 | } 78 | 79 | onBackspacePress = () => { 80 | this.calc.backspace(); 81 | this.setState({ display: this.calc.getMainDisplay() }); 82 | } 83 | 84 | renderPortrait() { 85 | return ( 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | { this.onUnaryOperatorPress(this.oc.PercentOperator) }} title="%" color="white" backgroundColor="#DCC894" /> 96 | { this.onBinaryOperatorPress(this.oc.DivisionOperator) }} title="/" color="white" backgroundColor="#DCA394" /> 97 | 98 | 99 | 100 | { this.onDigitPress("7") }} title="7" color="white" backgroundColor="#607D8B" /> 101 | { this.onDigitPress("8") }} title="8" color="white" backgroundColor="#607D8B" /> 102 | { this.onDigitPress("9") }} title="9" color="white" backgroundColor="#607D8B" /> 103 | { this.onBinaryOperatorPress(this.oc.MultiplicationOperator) }} title="x" color="white" backgroundColor="#DCA394" /> 104 | 105 | 106 | 107 | { this.onDigitPress("4") }} title="4" color="white" backgroundColor="#607D8B" /> 108 | { this.onDigitPress("5") }} title="5" color="white" backgroundColor="#607D8B" /> 109 | { this.onDigitPress("6") }} title="6" color="white" backgroundColor="#607D8B" /> 110 | { this.onBinaryOperatorPress(this.oc.SubtractionOperator) }} title="-" color="white" backgroundColor="#DCA394" /> 111 | 112 | 113 | 114 | { this.onDigitPress("1") }} title="1" color="white" backgroundColor="#607D8B" /> 115 | { this.onDigitPress("2") }} title="2" color="white" backgroundColor="#607D8B" /> 116 | { this.onDigitPress("3") }} title="3" color="white" backgroundColor="#607D8B" /> 117 | { this.onBinaryOperatorPress(this.oc.AdditionOperator) }} title="+" color="white" backgroundColor="#DCA394" /> 118 | 119 | 120 | 121 | { this.onDigitPress("0") }} title="0" color="white" backgroundColor="#607D8B" style={{flex:2}} /> 122 | { this.onDigitPress(".") }} title="." color="white" backgroundColor="#607D8B" /> 123 | 124 | 125 | 126 | 127 | 128 | ); 129 | } 130 | 131 | renderLandscape() { 132 | return ( 133 | 134 | Landscape 135 | 136 | ) 137 | } 138 | 139 | render() { 140 | var view = (this.state.orientation == "portrait") 141 | ? this.renderPortrait() 142 | : this.renderLandscape(); 143 | 144 | return ( 145 | 146 | {view} 147 | 148 | ) 149 | } 150 | 151 | } 152 | 153 | const styles = StyleSheet.create({ 154 | container: { flex: 1, paddingVertical: 50, backgroundColor: "black" }, 155 | }) --------------------------------------------------------------------------------