Assignment 7: Forms and JavaScript

The purpose of this assignment is to give you practice creating forms and manipulating form data in JavaScript.

Some material that you may find useful:

The problem

You are working as a summer intern at Thom Thumb ImageWorks, a company that specializes in making thumbnails for images that its clients want to use on their web sites. Your boss, Gifford Shrinkman, recently informed of your expertise in the use of on-line forms, has asked you to build a tool for calculating GIF file sizes.

Cecilia Pendergast from the design department has produced a mock up of your page:

The idea is that clients will call up and discuss their images and proposed image thumbnails with a consultant at your company. The consultant will load your web page into their browser, plug in proposed values for the width, height, and number of colors in a GIF image, and press the button labeled Calculate to compute an estimated file size (which will show up in the area next to the label File size. The Reset button can be used to return the form to the state you see above (with all the fields empty).

After finding a file size acceptable to the client, the consultant sends the client the quote by entering the client's email address into the fields at the bottom: the username goes to the left of the @, and the mail server name goes to the right. For example, if the client were Michael Stipe at rem.com, the user name might be mstipe and the server name would be rem.com. Clicking the Send to button will cause the email estimate to be sent to the client for their records.

Here is a sample use of the form:

Step 1

The user fills out the form for a 300x350 image with 200 colors.

form example 1
Step 2

The user clicks the Calculate button and the file size (103 KB) appears in the form

form example 2
Step 3

The user fills out the client's username and server in the form, and then clicks the Send To button and the estimate is sent to the client (mstipe@rem.com in this example)

form example 3

Requirements

  1. Create a directory named hw7 in your public_html/protected directory.
  2. Create a file named assign7.html to contain your implementation of GIF file size calculator page.
  3. Your page should loosely replicate the look of Cecilia's mock up. Your page must contain a header (Thom Thumb ImageWorks GIF File Size calculator in the sample page). Your form must have the same 6 boxes and the same 3 buttons (Calculate, Reset and Send To). You may choose your own colors and fonts and details of alignment.

    Note: You can use either of the two techniques we've covered for handling alignments: tables or CSS, whichever you find easier. Just inserting non-breakable spaces, however, would represent a lower quality solution.

  4. Your page must perform the correct calculations and produce the email message, as described above. Note that the examples shown above round the file size to the nearest integer. This is done using the built-in JavaScript function Math.round(). For example, Math.round(3.14) yields the integer 3, whereas Math.round(3.99) yields the integer 4. (Also see the hints below.)

Additional Information

Computing the bit-depth of an image relies on some mathematics that you may have forgotten since high school. Let's walk through it carefully.

Counting Digits

Bit depth is always an integer, because it's the number of bits it takes to represent some value or set of values. It's the same as the number of decimal digits needed to count some collection of things.

Here's an example, using decimal so that it will be more intuitive. Suppose you want to enter 17 of your closest friends into the speed-dial of your cell phone. How many digits does the speed-dial number need to be? The answer, clearly, is 2, since a two-digit number gives you up to 100 numbers, while 1 digit isn't enough.

Suppose we wanted a programmatic, rather than an intuitive, solution. That is, we want to write some JavaScript that will compute the answer. Hmm. Not so obvious.

We know, intuitively, that it has to do with powers of 10:
digitsnumbersnumbers
110110
2102100
31031000
410410000
...
N10N10000...

Since 17 is more than 10, we know that 1 digit won't suffice, but 2 digits give up to 100 numbers, so 2 will work fine. That is, we choose 2 because:

101 < 17 ≤ 102

In general, if we have an unknown number of close friends, call it k, we are looking to find N in an expression like:

10N-1 < k ≤ 10N

How can we do that? By definition, if you take the base 10 logarithm of a number, you find the exponent you need to raise 10 to in order to get the original number:

10N = k
N = log(k)

What does this mean for our digits problem? Let's take the base 10 logarithm of each expression in our inequality:

log(101)< log(17) ≤log(102)
1 < log(17) ≤2

Or, in general:

log(10N-1)< log(k) ≤log(10N)
N-1 < log(k) ≤N

(Hang in there; we're almost done.) So, if someone gives us a k, which is the number of close friends, we can compute N, the number of digits in the speed-dial numbers as

N = log(k)

In order to figure out the bitdepth (N) for a given number of colors (k), there are now two subproblems to solve:

Math Functions, Logs and Ceilings

JavaScript supplies a lot of the standard mathematical functions, as well as some you've never heard of. These are organized by hanging them off an object called "Math." Here is a list of a JavaScript math functions. For example, you can compute the square root of a number by using Math.sqrt:

The collection of JavaScript Math functions includes a logarithm function, but it computes the natural logarithm of the parameter. The natural logs are logs base e, that magic constant that is about 2.718281828.

Fortunately, there's a way to use the natural logs to compute any log. Here's how:
expressionexplanation
10N=kOur starting point. We know k, we want N
log(10N)=log(k)take natural log of both sides
N log(10)=log(k)this is a property of logarithms
N=log(k)/log(10)divide both sides by log(10)

Using the equation above as a guide, play with the code in the following evaluation box so that, instead of computing the natural log, the code computes the log base 10 (also called the common log). You'll know you've succeeded when your code computes 2 on an input of 100, and 3 on an input of 1000 and so forth.

Note that you can use the same technique to compute the base two logarithm of a number. Modify your solution, above, to compute that. You'll know you've succeeded when your code computes 2 on an input of 4, 3 on an input of 8, 7 on an input of 128, and so forth.

You've now solved Subproblem A!

So, there's one problem remaining, namely that, except for the powers of 10, the result of taking the logarithm of a number is not an integer. And the number of digits, whether in binary or decimal or hexadecimal, is always an integer. So, intuitively, what do we do? We round up. For example, since the common log of 17 is 1.2304489, we would round up to 2 as the number of digits in our speed-dial list.

How can we round up in a programmatic way? The solution is yet another built-in Math function, this one called "Math.ceil()," short for ceiling. Try it out. The following code uses Math.random() to yield a random number between 0 and 1, and the reciprocal of that will have a wide range. (In principal, it could go to infinity, but in practice is usually pretty small.) We can use Math.ceil() to round it off to the next higher number.

You've now solved Subproblem B!

Bit Depth

We've talked about bit depth quite a lot in this course, because it's fundamental to understanding the relationship between the size of a representation and the kind of stuff you can represent. The same relationship is true whether we use binary, decimal, hexadeciaml or any number base (though not Roman numerals and other numbering systems that don't use place value), but we focus on binary because computers are binary.

The idea is essentially that if we number items using binary numbers, the number of digits in the binary number (that is, the number of bits), is closely related to how many things we can number. When dealing with GIF files, we are numbering colors, but that's irrelevant to the fundamental relationship, though it helps to be concrete in our examples.

If we want to make a picture using our 17 favorite colors, we'll need 5 bits (binary digits) to label each color. This is the same example as our speed-dial list, except in binary instead of decimal.

If we were willing to drop one of the 17 colors, we could label each color with only 4 bits, for a big savings. (5 bits down to 4 is a 20% reduction.)

Use the following form to try to figure out a JavaScript calculation that will correctly compute the bit depth for different colors. You'll know you've (probably) succeeded if you get a bit-depth of 5 for 17 colors and 4 for 16 colors, and 7 for 96 colors and 6 for 55 colors and ...

You've now figured out how to compute bit depth (y) for a given number (x) of colors!

Recommendations and Hints

We recommend that you create your implementation as described below:
  1. Start your page by creating the form in page first. Don't worry about the JavaScript at first.
  2. Implement the correct behavior of the Reset and Calculate buttons. When the Calculate button is clicked, the estimated GIF file size in kilobytes should appear in the box labeled File size.
  3. Arrange for the form to be submitted when the Send to button is clicked. Use the CGI script demonstrated in class to email the form to the specified address (you can use your own email address for debugging). Be careful: the email address is specified in two different input boxes, so you have to build the right value before the form is submitted.
  4. The two aspects of this JavaScript code: putting the GIF file size in the form and putting the destination email address in the form are very similar. How the value is arrived at is different, but what is done with the value is pretty much the same. Think about that as you're working on the problem.
  5. The Forms lecture covered how to attach event handlers to buttons. What happens if you specify an onClick attribute for the submit button? Answer: the event handler in your onClick attribute runs right after the button is clicked. If it results in a true value, the form is sent to the server; if it results in a false value, the form is not submitted.

    <input type="submit"
      onClick = "/* stuff I want to happen, written in JS */
      return true;">

    The above code will, when someone clicks on the submit button, do whatever "stuff I want to happen" and then proceed with submitting the form.