CISC103, Fall 2006
lab10
(final lab assignment for the semester!)


Introduction

What's new this week

Part 1 of this lab walks you through "five steps" for a solved problem.

In Part 1, I'll walk you through a solved problem for defining a JavaScript function computeSalesTax() that could be used on a shopping web site.

There is nothing you have to turn in from Part 1. However, Part 1 forms the basis of everything you do in the remainder of this lab, so you need to work through it carefully.

Part 2 of this lab asks you to do step 3 yourself

In Part 2 , I'll walk you through a second solved problem—namely, computing the percentage margin of victory between two candidates—but this time, step 3, the function body, is left for you to figure out yourself.

More on the five steps

The five steps consist of the four step "design recipe" we talked about previously in class—and described in detail on the Wiki Page CISC103_Design_Recipes—plus one extra step—using your function in one or more useful working web pages:

Here's some more detail about what we'll do:

Step by Step Instructions

Part 0: Getting Ready

To get ready for this lab, you need to copy some files into a new directory under your web page http://copland.udel.edu/~youruserid/cisc103/lab10

There are two possible ways to do this.

Method 1: The easy way—three simple Unix commands
  1. The first command creates your new directory:
  2. The second command copies the files
  3. The third command makes everything readable on the web

Here's all three commands the way you would type them in. Note that copland.udel.edu% is just the Unix prompt—you don't type that part in.

copland.udel.edu% mkdir ~/public_html/cisc103/lab10
copland.udel.edu% cp /www/htdocs/CIS/103/pconrad/06F/labs/lab10/* ~/public_html/cisc103/lab10 copland.udel.edu% chmod -R 755 ~/public_html/cisc103/lab10
copland.udel.edu%
Method 2: The hard way—copying every file one at a time, from the web

The second way is to go to the web page http://www.udel.edu/CIS/103/pconrad/06F/labs/lab10, select each file individually, do a save as on each individual file to a folder on your hard disk.

Then, use SSH Secure File transfer to:

Hopefully, this illustrates why a little knowledge of Unix commands is very handy when working with web sites!

Part 1: The five steps in developing computeSalesTax()

Step 1: Contract

In this step, we define what the function computeSalesTax consumes, and what it produces. We write the following inside the file computeSalesTax.js.

We can do this using Notepad on the PC, just as we did when creating HTML files.

// computeSalesTax.js
// P. Conrad for CISC103 sect 99, Fall 2006, lab10, 11/16/2006



// function computeSalesTax.js
//   computes sales tax
//   consumes: 
//         a dollar amount (number)
//         a sales tax rate (a number, expressed as a percentage, e.g 5.5)

//   produces:
//         the sales tax amount, rounded to the nearest penny

In this stage, we may also add the function header, and a stub for the body.

A stub is a version of the body that

For example, in this case, since the function produces a number, we can just use return 0; as the stub.

So here, is the complete contract, including the function header, and a stub for the body:

// computeSalesTax.js
// P. Conrad for CISC103 sect 99, Fall 2006, lab10, 11/16/2006

// function computeSalesTax: computes sales tax // consumes: // dollars: a dollar amount (number) // taxRate: a sales tax rate (a number, expressed as a percentage, e.g 5.5) // produces: // the sales tax amount, rounded to the nearest penny, expressed as a string function computeSalesTax(dollars,taxRate) { return 0; // stub! replace this with a correct body later }

 

Step 2: Examples

In this step, we come up with examples. For each example, we specify

For example, if the price of an item is $10, and the tax rate is 5%, the function should return 0.50

We can write this in the form of an imaginary conversation between the user and the JavaScript interpreter. We try several differ net values, to see if we get what we expect.

js> computeSalesTax(10,5)
0.50
js> computeSalesTax(10,5.5)
0.55
js> computeSalesTax(1,5.5)
0.06 js> typeof computeSalesTax(1,5.5) string js>

We then turn this into a comment that we put in our JavaScript file:

// Examples:
// js> computeSalesTax(10,5);
// 0.50
// js> computeSalesTax(10,5.5);
// 0.55
// js> computeSalesTax(1,5.5);
// 0.06
// js> typeof computeSalesTax(1,5.5)
// string
// js>

We are now ready to complete the body of the function.

Step 3 Body:

In developing the body, the question we ask ourselves is

In this case, we see that we first need to convert the percent (e.g. 5) into a decimal such as 0.05. We do this by dividing by 100. We use the word var to indicate that the new variable we are defining—namely taxRateAsDecimal—is a local variable, one known only inside the function body.

var taxRateAsDecimal = taxRate / 100; 

We then multiply the taxRate by the dollars passed in, to find the total tax:

var dollarAmount = dollars * taxRateAsDecimal;

Next, we have to find some way to round off the answer to only two digits.

We could use the toFixed() method—this is a method that can be applied to any object of type number. For example, at the JavaScript prompt, try the following. The argument to toFixed() is the number of decimal places you want in your final answer, which for dollar amounts is always 2:

js> (12).toFixed(2);
12.00
js> (12.5).toFixed(2);
12.50
js> (12.501).toFixed(2);
12.50
js> (12.509).toFixed(2);
12.51
js>

Unfortunately, there is a problem with toFixed()—it rounds inconsistently—sometimes up, and sometimes down. (This is a bug in the JavaScript interpreter):

js> (0.045).toFixed(2) 
0.04
js> (0.145).toFixed(2)
0.14
js> (0.245).toFixed(2)
0.24
js> (0.545).toFixed(2)
0.55
js> (0.845).toFixed(2)
0.84
js>

So, an alternative is to use a trick. We first round the number using a method described on this web page:
http://www.javascriptkit.com/javatutors/round.shtml

js> function roundToTwoPlaces(num) {
return Math.round(num * 100) / 100;
}
js> roundToTwoPlaces(0.545)
0.55
js> roundToTwoPlaces(0.045)
0.05
js> roundToTwoPlaces(0.145)
0.14
js> roundToTwoPlaces(0.845)
0.85
js>

Once the number is rounded to two places, we can use the toFixed(2) method to get it to show both places (instead of cutting of 3.50 to 3.5, for example). Note that additional methods—such as toFixed(2)—can be applied directly to the result of another function call (as in roundToTwoPlaces(0.502).toFixed(2)).

js> roundToTwoPlaces(0.502) 
0.5
js> roundToTwoPlaces(0.502).toFixed(2)
0.50
js>

So, the final version of this body looks like this. Note that we need an additional function to get the job done. We can list that function immediately after the definition of computeSalesTax.

Note that we have a contract and examples for that function also. If we have to write a helper function, we need to go through the same design recipe, including all the steps. To really complete the job, we'll need to test that function also when we get to step 4.

function computeSalesTax(dollars,taxRate)
{ 
   // convert from percent to decimal, e.g. from 5% to 0.05

   var taxRateAsDecimal = taxRate / 100; 

  
   // multiply to get the dollar amount

   var dollarAmount = dollars * taxRateAsDecimal;
 
   // round to two decimals, and return as a string
   return 
}


// function roundToTwoPlaces
//   rounds a number to two places
// consumes: num ( a number)
// produces: the same number rounded to two decimal places
// 
// Examples: 
//  js> roundToTwoPlaces(5.505)
//  5.51
//  js> roundToTwoPlaces(5.499)
//  5.0
//  js>
//

function roundToTwoPlaces(num) {
return Math.round(num * 100) / 100;
}

Step 4: Testing:

To test, we first use the command line JavaScript interpreter

copland.udel.edu% cd ~/public_html/cisc103/lab10
copland.udel.edu% /www/htdocs/CIS/103/pconrad/bin/js
js> 
 
The manual way of testing

This involves typing each one of your example test cases in, one at a time, and comparing the result with what you hoped to get. You first load the function into the JavaScript interpreter with the command:

js> load("computeSalesTax.js");
js> 
  

Then, you type in the examples that you want to test, one at a time. This can be somewhat tedious, but it works.

Here's how that whole session looks:

copland.udel.edu% cd ~/public_html/cisc103/lab10
copland.udel.edu% /www/htdocs/CIS/103/pconrad/bin/js
js> load("computeSalesTax.js");
js> computeSalesTax(10,5)
0.50
js> computeSalesTax(10,5.5)
0.55
js> computeSalesTax(1,5.5) 
0.06
js> typeof computeSalesTax(1,5.5)
string
js> 
   
The efficient automated way: an acceptance/regression test script

A more efficient way to test is to use an automated script with a testComputeSalesTax() function—this is a separate function that automates the testing so that we don't have to retype all the tests each time we make a change to the function. We can store this function in the same file computeSalesTax.js, along with the function definition for testComputeSalesTax().

The function looks like this:

function testComputeSalesTax()
{
 if (computeSalesTax(10,5) =="0.50")
   print("test 1: passed");
 else
   print("test 1: failed");
  
 if (computeSalesTax(10,5.5) =="0.55")
   print("test 2: passed");
 else
   print("test 2: failed");

 if (computeSalesTax(1,5.5) =="0.06")
   print("test 3: passed");
 else
   print("test 3: failed");

 if (typeof(computeSalesTax(1,5.5)) =="string")
   print("test 3: passed");
 else
   print("test 3: failed");
}

Running it looks like this:

copland.udel.edu% /www/htdocs/CIS/103/pconrad/bin/js
js> load("computeSalesTax.js");
js> testComputeSalesTax()
test 1: passed
test 2: passed
test 3: passed
test 3: passed
js> 
   

Step 5: Application:

Finally, we'll use the computeSalesTax() function to build a useful working web page, one that asks the user for the price of an item, and the sales tax rate, and compute the sales tax.

Take a look at the page computeTax.html. Look both at the page itself, and the source code.

Consider, especially, this part:

<script src="computeSalesTax.js" type="text/javascript"></script>
<script type="text/javascript">
function whatToDoWhenYouPressTheButton()
{
// get the values out of the form

var dollarAmt = parseFloat(window.document.getElementById("dollarAmtInputField").value);
var taxRateAsPercentage = parseFloat(window.document.getElementById("salesTaxInputField").value);

// compute the result
var result = computeSalesTax(dollarAmt,taxRateAsPercentage);

// stick the result back into the web page (using DHTML)
window.document.getElementById("whereTheSalesTaxGoes").innerHTML=result;
}
</script>

The first <script> element pulls in the computeSalesTax.js file that we developed in steps 1-4. The second script element defines a JavaScript function whatToDoWhenYouPressTheButton() that is used as the Event Handler for the button:

<tr>
<td colspan="2" class="centerIt">
<input type = "button" value = "Calculate" onclick = "whatToDoWhenYouPressTheButton()" />
</td>
</tr>

If you don't understand how this works, ask questions of your TA and your instructor.

We'll also build on this in lecture

 

Part 2: The five steps in developing a function for computing "margin of victory"

What do we mean by "Margin of Victory"

In this part, we'll develop a web page that computes the "percentage margin of victory" between two candidates in an election. This is useful, for example, in determining whether an automatic recount is needed, and also in distinguishing between a very close election, and one that isn't very close.

For example, suppose you win an election by 100 votes.

In the former case, your margin of victory is 100 votes out of 200 votes cast, or a margin of 50% (you got 75% of the vote, and your opponent got 25%, a difference of 50%).

In the latter case, you received just under 50.7% of the vote, and your opponent received just over 49.3% of the vote. Hence the margin of victory is only a little less than 1.4% of the vote, which might be enough to trigger an automatic recount in some states.

If you do a web search on "margin of victory" and "automatic recount", you'll find many examples of this in American electoral politics, since many states do have laws requiring an automatic recount when the margin of victory is small.

To keep our problem simple, we will only consider elections between exactly two candidates, and ignore third party or write in candidates. (In a future semester, I'll expand the problem to add third party and write in candidates, in the interest of expanding democracy and making the problem more challenging.)

In this lab, most of the work is done for you—you only have to do step 3.

In this lab, you "fill in the blanks" for step 3, "developing the body of the function. I've developed the contract, and examples, as well as a test script and an application of the function.

What to do:

  1. If you didn't already do it back at Part 0: Getting Ready, copy the files I've given you into your web directory for lab10
    See Part 0: Getting Ready for directions (i.e. ~/public_html/cisc103/lab10).
  2. Do a "View Source" on the file marginOfVictory.html
    1. Understand the purpose of the web page—which is to compute the margin of victory, given votes cast for two candidates.
    2. Note that the page doesn't work yet, because the JavaScript isn't finished yet.
  3. Then look at the file computeMarginOfVictory.js
  4. Now, log on to copland.udel.edu and bring up a Unix prompt. Change directory into your lab10 directory, and then bring up the JavaScript prompt, as shown:
    copland.udel.edu% cd ~/public_html/cisc103/lab10
    copland.udel.edu% /www/htdocs/CIS/103/pconrad/bin/js
    js>
  5. Then, transfer the file computeMarginOfVictory.js to a file on your PC, and open it with notepad or wordpad (whichever seems to work). Open it up and start editing it to add the body you think you need.
  6. When you have something you think will work, transfer it from the PC to the copland.udel.edu web server. Then try the following command to load it into the JavaScript interpreter, and try running it on an example. See if you get the result below:
    js> load("computeMarginOfVictory.js")
    js> computeMarginOfVictory(550,450); 
    10
    js> 
  7. If that works, then try running the testComputeMarginOfVictory() function as shown here:
    js> testComputeMarginOfVictory()      
    test 1: passed
    test 2: passed
    test 3: passed
    test 4: passed
    js>
  8. Finally, try your marginOfVictory.html web page (the one on your copland.udel.edu web site), and see if it computes correct results. (If you've copied the files correctly and if your computeMarginOfVictory() function passes all the tests, then it should.)

    If so, then you are just about finished—the only thing that remains is submitting on WebCT.

Final Submission

Before submitting on WebCT, be sure that your marginOfVictory.html web page works correctly.

You only need to upload your computeMarginOfVictory.js file to WebCT. Upload and submit that, and you are finished. That is the only file you need to submit.


Evaluation and Grading (50 pts total)

Due Date

Due 11:55pm Thursday November 30

Penalties for late submissions:

if submitted by 11:55pm on: days late penalty
Fri Dec 1 1 none
Sat Dec 2 2 4%
Sun Dec 3 3 8%
Mon Dec 4 4 8%
Tue Dec 5 5 16%
Wed Dec 6 6 32%
Thu Dec 7 7 no credit (zero)

 


Copyright 2006, Phillip T. Conrad, CIS Dept., University of Delaware. Permission to copy for non-commercial, non-profit, educational purposes granted, provided appropriate credit is given; all other rights reserved.