EBTCalc is a Reverse Polish Notation (RPN) calculator that can have custom buttons. Custom buttons are programmed in JavaScript with a convenient editor.
Figure 1: EBTCalc Main Screen
Expression | How to Evaluate |
17 * (3 + 4) | 17 Enter 3 Enter 4 + * |
3.14 * 123.4^2 | 3.14 Enter 123.4 x^2 * |
(3^2 * (7^2 - 8)) / 5 | 3 x^2 7 x^2 8 - * 5 / |
Figure 2: Value Entry Area, Stack, Category Selector, Built-in Buttons, Custom Buttons
As values are entered they are displayed in the Value Entry Area. Press the BS (Back Space) button to correct errors in the value you're entering. Press the Clear Ent (Clear Entry) button to clear the entire value.
Every time you press the Enter key, the value you entered is placed at the bottom of a list, called the stack.
EBTCalc includes the usual buttons that you'd expect to find on a calculator. These buttons operate on values stored in the stack.
Buttons are arranged in categories. To display the buttons belong to a particular category, click the Category Selector (Figure 2).
Custom buttons are added by programming them in JavaScript. To add a new button, click Edit. Buttons are organized into categories that correspond to JavaScript classes or constructors. For example, to add a new custom button to compute Fibonacci numbers, click Edit.
Add the following code, without the line numbers:
1: // category custom "My Custom Buttons" 2: function custom() {} 3: 4: // button custom.Fibonacci "Fibonacci Sequence" 5: custom.Fibonacci = (x) => { 6: Log._addLine(`custom.Fibonacci: x = ${x}`); 7: 8: if (x === 1 || x === 2) { 9: return 1; 10: } else { 11: return custom.Fibonacci(x - 1) + custom.Fibonacci(x - 2); 12: } 13: } 14: 15: custom.sum = (a, b) => { return a + b; } 16: 17: custom.array = (e) => { return [e, e, e]; }
Line 2 declares the custom class or constructor. By default, class names appear exactly as programmed in the Category Selector. In other words, by default the custom class would appears as "custom" in the Category Selector. However, this default can be over-ridden with the // category comment, as it is in line 1. Line 1 causes the category to display as "My Custom Buttons" in the Category Selector.
A method is declared in lines 5 to 13. By default, method names appear exactly as programmed in custom buttons. However, they can be over-ridden with a // button comment. Line 4 causes the custom button to have the "Fibonacci Sequence" text.
The argument (x) in line 5 comes from the bottom of the stack.
The logging statement on line 6 is used to write information useful for debugging in the log. More about the log later.
Remember: The "// button" comments are only necessary when the button text differs from the corresponding method name. For example, the methods in lines 15 and 17 have button texts of "sum" and "array", respectively.
After you've entered the JavaScript code, the editor should look like the illustration below. If your code matches the illustration, click OK to save your changes. If any syntax errors were discovered, you will be prompted to fix them.
To use your new custom button, click the Category Selector and you'll see the new "My Custom Buttons" category:
Figure 3: Creating a Custom Button
Select "My Custom Buttons" and the new custom button will be displayed:
Figure 4: Creating a Custom Button
Enter a value of 10 and click "Fibonacci Sequence". A result of 34 should be calculated, since 34 is the 10th number in the Fibonacci Series.
However, the custom.Fibonacci method is rather slow-running. It might time out. If it does, you'll see an error message like the following:
Figure 5: Timeout
By default, EBTCalc will automatically stop custom methods after 15 seconds. If necessary, you can increase the timeout value by selecting the Settings menu. Select Method Timeout and specify a new timeout period. You can eliminate the timeout entirely, but this is not recommended as an inadvertent infinite loop in your JavaScript code will lock up the application.
Logging statements like the one you added to the Custom.Fibonacci method, can be useful for debugging custom methods. To see your log statements, select "Log" with the Category Selector. Then long-click the stack. When you click the stack and keep holding your finger down, eventually a menu will be displayed:
Figure 6: Stack Menu
Click Display Stack Contents and you'll see the logging messages created when the Custom.Fibonacci method was run. By default, only the most recent 100 log messages are retained. This threshold can be increased by editing the JavaScript code, but increasing the number of lines will tend to slow down the app since it permanently stores the logging statements.
To long-click an item, press it and hold your finger down. Long-click the stack to display stack contents in a wider display, or copy values from the stack. Long-click a custom button to go to that button's JavaScript code in the editor. Long-click the Value Entry Area to copy or paste that value. Only numerical values can be pasted.
Long-click the stack to display the stack in a wider format, or copy/paste stack values.
Long-click the region where new values are entered to copy/paste values.
The Custom.Fibonacci method has some serious flaws that can be easily fixed:
To address the above issues, add the following argument validation code at the start of the Custom.Fibonacci method:
if (x < 1 || x > 100 || Main.FractionalPart(x) != 0) { throw "Custom Fibonacci: Invalid Argument: " + x }
Now, when invalid values of x are specified, a run time error will occur:
Figure 7: Run Time Error when Arguments are Invalid
If you do not want a given JavaScript method to cause a custom button to be created, prefix that method with the underscore character ("_"). For example:
function Utilities() {} Utilities._reverseString = function(s) { ... }
The methods for your custom buttons can store and retrieve values in an object named "Globals". These values are retained indefinitely. In other words, a value stored in the Globals object will be available after EBTCalc is closed and re-launched.
The following values are stored in the Globals object:
Named values can be stored indefinitely and retrieved. Select the Memory category with the Category Selector. To store the bottom stack value, click the String built-in button and enter a name. Then click the Store pre-programmed button in the Memory category. To retrieve this value later, Click String and enter the name. Then click the pre-programmed Retrieve button in the Memory category.
If you select "Dates" with the Category Selector and click the pre-programmed EnterDateTime button, you'll see that custom buttons can prompt the user for one or more values, with validation. For instance, the EnterDateTime button's JavaScript code prompts the user for the year, month, day, and time:
Figure 8: Prompting the User for Values
In the above illustration, the label for the time value is red because a valid value has not yet been entered. The minutes value is missing.
The JavaScript code for Dates.EnterDate is a good example of prompting the user for data:
1: Dates.EnterDateTime = function() { 2: var values = Prompt.prompt( 3: [ 4: ["Year", "v", "[0-9]{4}", ""], 5: ["Month", "s", "1|2|3|4|5|6|7|8|9|10|11|12", ""], 6: ["Day", "v", "[0-9]{1,2}", ""], 7: ["Time (hh:mm)", "v", "[0-9]{1,2}:[0-9]{1,2}", "00:00"], 8: ["am/pm", "s", "AM|PM", "1"] 9: ], "Enter Date/Time") 10: 11: if (values != null) { 12: return new Date(values[1] + "/" + values[2] + "/" + values[0] + " " + values[3] + " " + values[4]) 13: } 14: }
The Prompt.prompt method takes two arguments. The first is an array that specifies the fields for which the user will be prompted. The second is a string that will be displayed to the user. Each row of the array specifies a single value to be supplied by the user. Each row contains the following four elements:
Element Number | Description |
0 | Field Label displayed to user |
1 | "v" if this field is a free-form value. "s" if this field is a spinner (list of selections). |
2 | If this field is a free-form value, this element is a regular expression used to validate the field value. You can specify an empty string if you don't need validation. If this field is a spinner, this element specifies the spinner values, separated by "|". |
3 | If this field is a free-form value, this element specifies the default value. If this field is a spinner, this element specifies the ordinal value of the default selection. For example, to default to the first value in the list, use "1". Use "2" to default to the second item in the list, and so on. |
EBTCalc uses the Rhino JavaScript engine to run custom code. It currently uses Rhino version 1.7.15. The various JavaScript features supported by Rhino, hence EBTCalc, can be seen in the Rhino ES2015 Support Table.
The free version of EBTCalc does not allow custom buttons to be created, and does not allow the code for pre-programmed buttons to be changed. Upgrade to the paid version to add new custom buttons and modify pre-programmed buttons, and to support continued EBTCalc development!
Download EBTCalc.
Thank you for trying EBTCalc. I hope you find EBTCalc to be a useful and enjoyable tool. I welcome any and all feedback about EBTCalc. Click the About menu to send me feedback.
Eric Bergman-Terrell & Greta