CMSI 185: S/W Development Demonstration

Introduction to the Process

This page documents the basic process for developing a simple program in JavaScript, from starting with the problem statement to having a completed program.

Problem Statement

A simple substitution cypher uses the numbers 1 - 26 to represent the letters of the alphabet, A - Z. When a message is encoded, a specific value called an "offset" is added to each letter in the message, ignoring case. Values greater than 26 are "wrapped around"; for example, if the offset is 5, the letter X would be calculated by 24 + 5 = 29 - 26 = 3 and would then be replaced in the message by the letter C. Develop a function to take a string and an offset as input, which will return the encoded message string as its output. Develop a web page that has two boxes for input, one for the message and one for the offset, and a button which produces the encoded message at the bottom of the page when it is clicked.

Program Design

This program requires several steps:

  1. Get the input message value on which to operate
  2. Get the offset value used to encode the message
  3. In a loop, perform the following steps:
    • Find the numeric value of a letter in the message
    • Add the offset to the numeric value
    • Add the different letter to the output message
  4. When the loop is complete, output the encoded message

This seems pretty simple. Let's find out if it is or not.

Stepping Through the Process

Version 1

The problem statement says to develop a function which will be called by a web page. Let's do the function first, and once that works, the web page part is easy. To make it even easier, let's do the development as a program first, using the JavaScript Runner page, then turn the encoding part into a function when it's all working. That way we can "instrument" the code as we go by using temporary alert() calls to display interim values easily. Don't forget comments!!

The first thing we need to do is get some user input. We all already know how to do that, and we need to save the input to work on it later. Here's the first cut:

   // Get the user input
   var message = prompt( "Enter a message to encode: " );
   var offset  = prompt( "Enter a numeric offset: " );
   alert( "Message: " + message + "\noffset: " + offset );  // instrument the code here
      

Version 2

See how easy this is? We've already done steps one and two of our algorithm. Now we can do step three. This involves a loop to touch every character in the message. We have to find the numeric value of the letter, add the offset, and put the new letter into the output string. Wait.... WHAT output string? We'd better define one. The let's start by adding a variable declaration, and then adding the loop, which will just be an alert call as a way of instrumenting the loop to make sure we are getting the letter we think. Because the message is a string, we can use JavaScript's String.charAt and the loop index to access each character in the message in turn. Here's the code:

   // Declare variables
   var outputMsg = "";
   var letter = "";

   // Get the user input
   var message = prompt( "Enter a message to encode: " );
   var offset  = prompt( "Enter a numeric offset: " );
   alert( "Message: " + message + "\noffset: " + offset );  // instrument code here; remove later

   // Encoding loop
   for( var i = 0; i < message.length; i++ ) {
      letter = message.charAt( i );
      alert( "Character is: " + letter );
   }
      

Version 3

So now we can get user input, get the offset, and loop throught the message addressing each letter. Next we need to get the index of the letter in the alphabet.

Hmmmmmmm......

Does JavaScript have a function to do that? Characters in JavaScript are just strings that are very short (length == 1), so is there a String function that will give us the alphabet number? Just so happens that there is a function that will provide us with the Unicode value of the character at the string index. It's called "charCodeAt()" so let's try that. (In class we did a brute-force thing of defining our own array of all uppercase letters, and added another loop to find our letter in that string to get the index. This is a bit more elegant.)

   // Declare variables
   var outputMsg = "";
   var letter = 0;

   // Get the user input
   var message = prompt( "Enter a message to encode: " );
   var offset  = prompt( "Enter a numeric offset: " );
   alert( "Message: " + message + "\noffset: " + offset );  // instrument code here; remove later

   // Encoding loop
   for( var i = 0; i < message.length; i++ ) {
      letter = message.charCodeAt( i );                     // the only change!
      alert( "Character is: " + letter );
   }
      

OK, that was pretty easy. Is this really what we want, though? The problem statement said for us to ignore case and handle the letters A - Z, wrapping around if the offset ran us past 26. Using Unicode might be slick, but it may not actually fit the problem statement. For example, the code for "A" in Unicode is 65, not 1, meaning Unicode for Z is 90. So instead of wrapping at greater than 26, we have to wrap at greater than 90 and only go back to 65. This situation makes things a bit more confusing, but it's nice to have one String function call to do all that extra work for us. So let's use it!

Version 4

Now that we have our code index, we need to add the offset to it, then convert that new offset to its character and put it into the output string. BTW, the charCodeAt() function returns a type Number, not a type String, so when we alert the letter we see the number value, not the character.

So let's add the offset, and then use the matching JavaScript String function to convert back to a character representation. We'll alert it when we're done, so we can see what happened.

   // Declare variables
   var outputMsg = "";
   var letter = 0;

   // Get the user input
   var message = prompt( "Enter a message to encode: " );
   var offset  = prompt( "Enter a numeric offset: " );
   alert( "Message: " + message + "\noffset: " + offset );  // instrument code here; remove later

   // Encoding loop
   for( var i = 0; i < message.length; i++ ) {
      letter = message.charCodeAt(i);                     // convert to Unicode number value
      alert( "Character is: " + letter );
      letter += offset;                                     // add the offset and alert the new letter
      alert( "letter: " + letter + "\ncharacter: " + String.fromCharCode( letter ) );
   }
      

Holy crap! This isn't working at all. I entered the string "A message" and an offset of 7, and instead of seeing the letter offset of 72 (65 + 7) and the character "H", the value of letter is 657! Aha. The value entered for the offset is still being treated as a string, the way we got it back from the prompt() function. When we add it to the value of letter, one of them is a string, so JavaScript converts them both to strings and concatenates them! Not what we wanted. So, we need to convert the offset value to a number using the JavaScript function "parseInt()".

Version 5

We can convert the offset in one of two places — either when we get it from prompt(), or at the point we're going to use it. Let's do the first one (flip a coin....). Here's the code:

   // Declare variables
   var outputMsg = "";
   var letter = 0;

   // Get the user input
   var message = prompt( "Enter a message to encode: " );
   var offset  = parseInt( prompt( "Enter a numeric offset: " ) );   // the only change
   alert( "Message: " + message + "\noffset: " + offset );  // instrument code here; remove later

   // Encoding loop
   for( var i = 0; i < message.length; i++ ) {
      letter = message.charCodeAt(i);                     // convert to Unicode number value
      alert( "Character is: " + letter );
      letter += offset;                                     // add the offset and alert the new letter
      alert( "letter: " + letter + "\ncharacter: " + String.fromCharCode( letter ) );
   }
      

Version 6

But now there's another problem. (sigh) If I don't type in all upper case letters in my message, some of the values don't come out as letters. Mixed case isn't being ignored as is required by our problem statement. We can fix this by making the input all upper case letters. That's easy to do in JavaScript, of course, using the function "toUpperCase()", like so:

   // Declare variables
   var outputMsg = "";
   var letter = 0;

   // Get the user input
   var message = prompt( "Enter a message to encode: " ).toUpperCase();    // the only change
   var offset  = parseInt( prompt( "Enter a numeric offset: " ) );
   alert( "Message: " + message + "\noffset: " + offset );  // instrument code here; remove later

   // Encoding loop
   for( var i = 0; i < message.length; i++ ) {
      letter = message.charCodeAt(i);                     // convert to Unicode number value
      alert( "Character is: " + letter );
      letter += offset;                                     // add the offset and alert the new letter
      alert( "letter: " + letter + "\ncharacter: " + String.fromCharCode( letter ) );
   }
      

Version 7

Now we're getting all uppercase letters, which is good, except we are getting apostrophe letters in place of spaces, and the Z character isn't wrapping. The message "A message UVWXYZ" is encoded as "H'TLZZHNL'\]^_`a". So we're running off the end of the alphabet, since we never put in the part that wraps around. We also need to handle the spaces by leaving them alone. Finally, we need to put the encoded characters into the output string. Here's some new code to fix that stuff, and to output the result in an alert:

   // Declare variables
   var outputMsg = "";
   var letter = 0;

   // Get the user input
   var message = prompt( "Enter a message to encode: " ).toUpperCase();
   var offset  = parseInt( prompt( "Enter a numeric offset: " ) );
   alert( "Message: " + message + "\noffset: " + offset );  // instrument code here; remove later

   // Encoding loop
   for( var i = 0; i < message.length; i++ ) {
      if( message.charAt(i) == " " ) {
         outputMsg = outputMsg + " ";
         continue;
      } else {
         letter = message.charCodeAt(i);                     // convert to Unicode number value
         alert( "Character is: " + letter );
         letter += offset;
         if( letter > 90 ) {
            letter -= 26;       // wrap around to the beginning of the alphabet
         }
         alert( "letter: " + letter + "\ncharacter: " + String.fromCharCode( letter ) );
         outputMsg = outputMsg + String.fromCharCode( letter );
      }
   }

   // Output the resulting string
   alert( "outputMsg: " + outputMsg );
      

Version 8

ALL RIGHT!! Now it's working. Input the string "abc wxyz" and we get back the output "HIJ DEFG" which is correct. Now it's time to take out instrumentation and convert it to a function:

   // Declare variables
   var outputMsg = "";
   var letter = 0;

   // Get the user input
   var message = prompt( "Enter a message to encode: " ).toUpperCase();
   var offset  = parseInt( prompt( "Enter a numeric offset: " ) );

   // Encoding loop is inside the function, which takes two arguments
   function encode( msg, offs ) {
      for( var i = 0; i < msg.length; i++ ) {
         if( msg.charAt(i) == " " ) {       // handle spaces
            outputMsg = outputMsg + " ";
            continue;
         } else {
            letter = message.charCodeAt(i);     // convert to Unicode number value
            letter += offs;                     // encode by substitution
            if( letter > 90 ) {
               letter -= 26;                    // wrap to beginning of the alphabet
            }
            outputMsg = outputMsg + String.fromCharCode( letter );
         }
      }
      return outputMsg;
   }

   // Call the function and output the resulting string
   encode( message, offset );
   alert( "outputMsg: " + outputMsg );
      

Version 9

The last step is to get everything into a web page, so that it works with two input boxes and a button, and the output is displayed in an area at the bottom. This is easy to do using jsFiddle, so it is left as an exercise for you to do on your own. To finish up, then, you can either save the HTML code and the JavaScript code in two separate files as we've seen how to do already, or you can keep everything in the same file, which we've also seen how to do. Just copy the code from the jsFiddle page areas into the appropriate file or location and enjoy!

One thing that isn't tested yet is error handling. What happens if the user inputs numbers? What if she inputs special characters like the carat or dollar sign? What happens if there are Unicode code points embedded in the input? These and other questions should be addressed prior to trying to release this to the world.