reptile7's JavaScript blog
Sunday, January 01, 2023
 
I Do Remember, I Do Recall
Blog Entry #417

Let's get back in the saddle and lasso the next Super Calculator item on the agenda, that being the Memory section.

The SC's Memory section

Neither Jamie Beyore's Another Great Science Calculator nor Saries's Rainbow Calculator has a memory facility (actually, most JavaScript calculators don't have one), so this will be new territory of sorts for us.

I can't recall ever owning a calculator that had more than one memory field: the SC has two of them. The SC's Memory keys work with a pair of hidden controls for storing values or expressions to be remembered.

<!-- These inputs are at the very end of the mainform form. -->
<input type="hidden" name="memory1">
<input type="hidden" name="memory2">
</form>


Memory in

The and keys load the total value into the memory1 and memory2 fields, respectively, via a common mem( ) function.

function getinput(func) {
    var a = document.mainform.total;
    var b = document.mainform.memory1;
    var c = document.mainform.memory2;
    var mode = document.mainform.mode[0].checked ? 1 : 0; ...
    if (func == "mem1") { return mem(mode, a, b); }
    if (func == "mem2") { return mem(mode, a, c); } ... }

// obj is the user input field, where is the hidden field
function mem(mode, obj, where) {
    if (mode == 1) { window.alert("Pressing the memory button allows you to save the current number in the total text box for later use. You can recall the remembered number by pressing the Rcl button."); }
    where.value = obj.value; }

<td><b>Memory:</b><br>
<input type="button" value=" M1 " onclick="getinput('mem1');">
<input type="button" value=" M2 " onclick="getinput('mem2');"> ...


M1/M2 deconstruction

Clicking the push button calls the getinput( ) function
and passes to the getinput( ) function a 'mem1' string
(versus a function reference as per the SC functions we've discussed previously)
that is assigned to the func parameter of the getinput( ) function.

Next, the total, memory1, and memory2 fields (more precisely, the Text and Hidden object reflections of those fields) are variabilized as a, b, and c, respectively, and then
the mode help flag is set to 1 if we want
Help or to 0 if we prefer
No Help.

A func == "mem1" comparison subsequently triggers the mem( ) function, to which is passed mode, a, and b, which are assigned respectively to the mode, obj, and where parameters of the mem( ) function.

If mode is 1 (its initial value), then a

Pressing the memory button allows you to save the current number in the total text box for later use. You can recall the remembered number by pressing the Rcl button.

alert( ) message pops up.

Lastly, the obj/total value is copied to the where/memory1 field.

Things go in a corresponding way with the button, the differences being that
(a) a 'mem2' string is passed to the getinput( ) function,
(b) a func == "mem2" comparison triggers the mem( ) function, and
(c-d) the c Hidden object is passed to the mem( ) function and then assigned to the where mem( ) parameter.

where.value scope

A conventional calculator memory facility - check the calculator at https://www.online-calculator.com/ -
allows the user to store a number. With the SC, we can do that too of course
although we can also memorize a larger expression like 2*(3+4); as the total field is not readonly, moreover, we can even input and save something like Math.pow(2, 7).
• How long and involved can those numbers and expressions be? The total field does not have a maxlength setting, suggesting that there is no limit to the number of characters that can be loaded into the total, memory1, and memory2 fields.

More specific help

Although the and keys have the same help message, it is simple enough to differentiate the message for each key.

function mem(mode, obj, where) {
    var memID = where.name.substring(6, 7);
    if (mode) window.alert("Pressing the M" + memID + " key allows you to save the displayed value (number or expression) in a hidden memory" + memID + " field. You can redisplay the remembered value by pressing the Rcl" + memID + " key.");
    where.value = obj.value; }


The 1 or 2 differentiator at the end of the hidden input name is substring( )-extracted and then stored in a memID variable, which is subsequently deployed in a modified help message. For the key, the new message is:
Pressing the M1 key allows you to save the displayed value (number or expression) in a hidden memory1 field. You can redisplay the remembered value by pressing the Rcl1 key.
Besser, ja?

Out of storage

For their part, the Memory section's and keys

<input type="button" value=" Rcl1 " onclick="getinput('rcl1');">
<input type="button" value=" Rcl2 " onclick="getinput('rcl2');">


indirectly call on
a getinput( )-gated rcl( ) function

function getinput(func) {
    /* The a, b, c, and mode variables are defined above. */ ...
    if (func == "rcl1") { return rcl(mode, b, a); }
    if (func == "rcl2") { return rcl(mode, c, a); } ... }

// obj is the hidden field, where is the user input field
function rcl(mode, obj, where) {
    if (mode == 1) { window.alert("Rcl is a neat function that works with Memory. It recalls and gives you the number you saved in the coresponding [sic] memory."); }
    if (obj.value == "") { more("", where); }
    else { more("(" + obj.value + ")", where); } }


and the more( ) function
to respectively send the memory1 and memory2 values to the total field.

rcl( )/more( ) possibilities, plus some code-tightening

Deconstruction-wise, let's go straight to rcl( )'s post-help statements; I trust you can handle everything else up to that point. Here's the skinny on what can happen:

(i) If there's nothing in the memory1|memory2 field and the total value is 0 or the empty string, then the total value is set to the empty string, i.e., the total field is blanked.

(ii) If there's nothing in the memory1|memory2 field and the total field holds some nonzero value, then the empty string is appended to the total value, and the total value stays the same.

(iii) if there's something in the memory1|memory2 field and the total value is 0 or the empty string, then the total value is set to the memory1|memory2 value surrounded by parentheses.

(iv) if there's something in the memory1|memory2 field and the total field holds some nonzero value, then the memory1|memory2 value surrounded by parentheses is appended to the total value.

Got all that? Now, if the memory1|memory2 field is empty, then why are we sending anything to the total field via the more( ) function? Can't we just return for the (i) and (ii) cases and be done with it? Actually, yes, we can do that. But given that the rcl( ) function is itself equipped with an object reference argument for the total field, we don't need to outsource the (iii) and (iv) cases to the more( ) function either. The rcl( ) function can take care of the whole shebang all by its lonesome:

function rcl(mode, obj, where) {
    var memID = obj.name.substring(6, 7);
    if (mode) window.alert("Clicking the Rcl" + memID + " key recalls and displays the value (number or expression) that you saved via the M" + memID + " key.");
    if (obj.value == "") return;
    else if (where.value == "0" || where.value == "") where.value = obj.value;
    else where.value += "(" + obj.value + ")"; }


For the (iv) case, if you would rather just replace the total value with the memory1|memory2 value - this is what happens with the key on most calculators, in my experience - then the post-help part of the preceding function can be simplified to:

if (obj.value == "") return;
else where.value = obj.value;


No offshoring needed

At this point I should note that thirteen of the seventeen getinput( )-gated functions secondarily call on the more( ) and/or doit( ) functions to load a value into the total field (besides rcl( ), see the power( ), squareroot( ), and sine( ) functions, e.g.). The getinput( ) function passes the var a = document.mainform.total; object reference to all thirteen of these functions, and therefore their various calculation returns can be sent 'in house' to the total field if desired.

Memory addition

In the Memory subsection of the Operational overview section at the end of the first post of this series I parenthetically said that and are M+ keys. A conventional key on a standard calculator is used to add a series of numbers in a hidden field, however, and the aforedetailed mem( ) function can't do that. Suppose we want to make a standard M+ key. What changes would be necessary?

M2 → M+

We can write a separate, dedicated function for our M+ key, or not: we can continue to make use of the mem( ) function if the M+ process can be toggled on and off as needed, and a global boolean flag is a handy tool for doing just that.

In the code below,
the memplus variable is globally initialized to 0
and is toggled to 1 in the getinput( ) function right before the mem( ) call
and in the mem( ) function the switched-on memplus green-lights
the display of an M+ help message and
the addition of numberified* memory2 and total values in the memory2 field. (*The unnumberified values would be concatenated.)

function getinput(func) { ...
    if (func == "mem+") { memplus = 1; return mem(mode, a, c); } ... }

var memplus = 0;
function mem(mode, obj, where) {
    if (mode) {
        if (memplus) window.alert("Pressing the M+ key enables you to add a series of numbers in a hidden memory field. You can display the hidden sum by pressing the Rcl2 key.");
        else /* alert( ) for the remaining key, if present */ }
    if (memplus) where.value = Number(where.value) + Number(obj.value);
    else where.value = obj.value; // As per the original function }

<input type="button" value=" M+ " onclick="getinput('mem+');">


The where.value = Number(where.value) + Number(obj.value); operation takes care of normal numeric string inputs. We noted earlier that we can input larger expressions into the memory1/memory2 fields. If we want to M+ such expressions - either to themselves or to normal numbers - we have a couple of options:

(1) We can eval( ) each expression to a number before we remember it and subsequently add our M+ addends
via a where.value = Number(where.value) + eval(obj.value); operation.

(2) More generally, we can remember each expression as is and insert a + symbol between each pair of M+ addends so that when the whole thing is recalled it can be eval( )ed in one go with the key:

if (where.value == "") where.value = obj.value; // For any initial addend
else if (! isNaN(where.value) && ! isNaN(obj.value)) where.value = Number(where.value) + Number(obj.value); // If both addends are numbers
else where.value = where.value + "+" + obj.value; // If at least one addend is not a number


The former approach is good for any and all legitimate mathematical expressions but will balk at (throw an error for) an input like Hello World. The latter approach will handle any expression under the sun without batting an eye.

We're not done yet. Consider the M+ addition of 9 and 13 as carried out by the aforecited Online-Calculator.com calculator.
We press the 9 key, thereby entering a 9 into the user input field, and then the M+ key.
Pressing the 1 key for 13's tens place digit overwrites the 9 in the user input field with a 1; pressing the 3 key for 13's ones place digit appends a 3 to the 1 in the user input field.
We press the M+ key a second time. Pressing the MR key now outputs the M+ sum, 22, to the user input field.

For doing this back at the SC, clearing the 9 M+ addend without clearing the starting 1 of the 13 M+ addend requires us to tweak the more( ) function in a couple of ways:

function more(obj, where) {
    if (where.value == "" || where.value == "0" || memplus) where.value = obj;
    else where.value += obj;
    if (memplus) memplus = 0; }


When the memplus flag is on, a new total value (obj, admittedly a poor choice of variable names in this context) replaces a preceding total M+ value.
If the new value consists of more than one character AND
we're going to input those characters one by one via the SC's number/operator keys AND
we don't want each character to be replaced by the following character during the input process,
then it is necessary to turn memplus off at the end of the function once the zeroth character is in place.

Subtraction action

If your calculator has an M+ key, then it probably also has an M- key for subtracting a series of numbers from a minuend in a hidden field. Adding an M- functionality to the SC is no more difficult than changing the + operators in the above M+ code to - operators. That said, if we want the SC to have both an M+ key and an M- key in addition to the original and/or keys, then it may well be a good idea to deploy discrete M+, M-, and Min (memory input) hidden fields and functions just to keep it all straight and readable.

The eraser

Finally, clicking the Memory section's key

<input type="button" value=" MC " onclick="getinput(mc);">

calls (indirectly via the getinput( ) function, as per usual) an mc( ) function that empties the memory1 and memory2 fields by setting their values to empty strings.

function getinput(func) { ...
    if (func == mc) { return mc(mode, b, c); } ... }

function mc(mode, obj1, obj2) {
    if (mode == 1) { window.alert("Memory Clear is an easy little function that erases the values of both memorys [sic]."); }
    obj1.value = "";
    obj2.value = ""; }


I don't think we need any commentary for this one.
We're down to the last few SC keys and functions - we'll cover the Scientific Notation section in the next post.


Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)