\n"); } else { header("Content-type: text/html; charset=utf-8"); } ?> CS-081 Assignment 6

Introduction

This assignment is designed to give you experience working with Tables, Forms, and JavaScript. The idea is to mimic a data entry web page that allows a user to enter people’s names and email addresses into a table. As each set of information is entered, Javascript code validates that the name fields are filled in, and that the email address (1) is filled in, (2) is a valid email address, and (3) does not duplicate an email address that is already in the data table.

The version of this assignment that I demonstrated in class has a number of additional features that are not required for this assignment, although you can receive extra credit for any of those features that you do incorporate into your project.

To accommdate people who want to do extra-credit work for this assignment, it will be graded on a 10-point scale instead of just "not ok" or "ok." Full credit is a 10, but if you do extra-credit work, you can get more than full credit for the assignment.

This assignment will count 5% of your course grade; the other assignments will count 1% each.

It is still all right to work with someone else on this assignment, just like all the others in this course. If you work together with someone else, leave it one person’s account on maple and send me an email telling the the account and the names of both people who are submitting the assignment.

The assignment will be graded on the basis of how well the XHTML, CSS, and JavaScript code are structured, how good the page looks, and how well the page functions — all in approximately equal proportions.

Procedure

Important Note!

It is very important that you complete the steps listed below in the order given and that you test your program carefully as you complete each step. You will receive much more credit for a project that has just a few steps working correctly than for a project with lots of code and features, but which does not work correctly. To be sure you don’t lose the good parts of the assignment while working on a later step, create a subdirectory of your web site named Backups and copy the xhtml, js, and css files for the assignment into that directory each time you get a step working correctly. To be really careful, you could make a separate subdirectory in Backups for each step you complete, but that is not required.

  1. Create a new web page and link it to your site’s navigation list.

    Create a web page named Assignment_06.xhtml using your Dreamweaver template. Change the name of the file in the Javascript link in the head from template.js to assignment_06.js. Add a link to the new page to the list of navigation links in index.xhtml. Use a descriptive name for the text of the link, such as “Dynamic Table Entry.” Use this same name for the contents of the title and h1 elements of your new page.

    Test your work and save a copy of your code files in your Backups directory before proceeding to the next step.

  2. Set up a form and a JavaScript onload handler.

    Create a form in your web page, and put a a table inside it. The table is to have two rows: the top row is to have headings that tell what each column is for, and the second row is to contain five input elements: three text boxes and a submit button. The fifth column of the second row will be for dynamically generated error messages. The fourth and fifth columns of the heading row should be joined together using a colspan attribute. Use CSS in your existing screen.css file to format the table nicely, with the contents of the error message cell formatted in a way that it stands out. Put some text in the error message cell to make sure it looks the way you want, test it, and then add CSS to your stylesheet to make that cell’s display attribute have the value hidden. Now, when you view the page that cell should simply not appear.

    This form will never actually be submitted; all processing of the information the user enters will be handled completely by JavaScript. However, you will need to provide an action attribute with an arbitrary value in order for your page’s XHTML to validate. The conventional value to use in this situation is "#". In class I mentioned using an onsubmit attribute to block the page from being submitted, but this is not necessary. But you should give this form an id so that you can get a reference to it easily from your JavaScript code.

    Edit your script file for the assignment so that it has an event handler for the window.onload event. You may use either a named or anonymous function for this event handler. In it, get a reference to the form, and set up an event handler for the form’s onsubmit event. The event handler is to be a function named validate(), and for now, that function is simply to display an alert box that says “validate” (to verify that the function got called) and then executes a return false; statement. Returning a value of false is what prevents the browser from actually sending the form data to the URL specified in the form’s action attribute.

    Test your work and save a copy of your code files in your Backups directory before proceeding to the next step.

  3. Verify that the three text fields are not empty when the form is submitted.

    In your validate() function, check that the values of all three input elements are non-empty strings. If any of them is empty, replace the nodeValue attribute of the error message cell’s first child (a text node) with an appropriate error message and set the cell’s display attribute’s value from "hidden" to "visible". Whether any field is blank or not, validate() is to continue to return a value of false so the form does not get submitted.

    To make the page work properly, you will need to hide the error message when you start to validate the form.

    Test your work and save a copy of your code files in your Backups directory before proceeding to the next step.

  4. Use a regular expression to validate the email address.

    You can google for JavaScript regular expressions for validating email addresses, but I suggest you try writing your own. Here is the basic setup:

    var validEmail = /.*/; var isValid = validEmail.test("anything");

    This zippy piece of code will set the variable isValid to true. Here’s how it does it: the characters between the slashes are the regular expression, which consists of any character (indicated by the period) repeated zero or more times (represented by the asterisk following the period); the second statement executes the regular expression’s test() function, which returns true or false depending on whether the string passed to it matches the regular expression or not. In this case, any string you pass will cause test() to return true, even an empty string. Technically, you don’t need a variable to hold the regular expression:

    var isValid = /.*/.test("some string");

    But there is some overhead in setting up regular expressions, so if you are going to use one multiple times, it’s slightly more efficient to set it up once in a variable than to use the second form.

    So now the issues are (1) what is a valid email address, and (2) how can you use a regular expression to test for one? A simple-minded rule for a valid email address would be “two non-empty strings separated by an @ character.” The regular expression to test this rule would be /.+@.+/. Just as an asterisk says “zero or more repetitions of the previous character,” a plus sign says “one or more repetitions.” To get started, I suggest you try using this regular expression and make sure your code will discriminate between valid and invalid email strings based on it, using this code as a starting point:

    if (validEmail.test(emailInput.value)) { alert("email is valid"); }

    I think the following information will be enough for you to construct an elaborate-enough regular expression for validating email addresses: (1) If you want to test for a period, “escape” it by putting a backslash in front of it. (2) If you want to test for a limited set of characters rather than just “any character” as represented by a period, put the characters you are interested in inside square brackets. (3) You can use special codes to represent common groups of characters. Notably, \w matches any digit or any upper case or lower case letter. (4) Instead of using + or * to specify how many times the previous item must be repeated, you can put minimum and maximum numbers inside curly braces. (5) Finally, you can force the regular expression to match the beginning and ending of a string by using the ^ and $ characters respectively. Without this feature, the regular expression would treat a string with invalid information (such as spaces) in it as valid if there is a valid substring in the middle somewhere.

    Putting everything in the previous paragraph together, look at these regular expressions and see if you can decode them:

    /^$/ (1) /^[ab]{3,5}$/ (2) /^[\w\.]+-[\w\-]{3,5}/ (3)

    Answers:

    (1) An empty string. (2) A string between 3 and 5 characters long consisting of any mix of the lower case letters 'a' and 'b'. (3) A string that starts with a mix of one or more letters, digits, or periods, followed by a dash, followed by any mix of 3 to 5 dashes, letters, or digits. Note that the plus sign has nothing to do with the minus sign that follows it: the plus tells how many times the characters in brackets must appear, but the minus is just a character (hyphen, dash, minus, or whatever you want to call it) that must be present in the string. Also, note that the backslash in front of the second dash in the last example is necessary, and is there to let you know that the five rules given above do not cover everything there is to know about regular expressions — not by a long shot!

    Test your work and save a copy of your code files in your Backups directory before proceeding to the next step.

  5. Add a second table and add a row to it each time the user enters a valid set of data in the first table.

    The functions you will need to use are document.createElement(), document.createTextNode(), and element.appendChild() as covered in class. (For appendChild() you need to specify which element the new element is to be appended to.)

    Test your work and save a copy of your code files in your Backups directory before proceeding to the next step.

  6. Add a second form to the web page, and append hidden inputs to it for the values in the second table when this form is submitted.

    It doesn’t matter whether the data table is inside or outside the second form, but the form must have a submit button inside it, and the submit button should appear below the table on the page. Set up a function that will be invoked when the user clicks the submit button in the second form. You could use the onsubmit attribute of the form tag, but it would be better to set this up in the windows.onload event handler to avoid mixing behavior with content markup in your web page. The action attribute for the form should specify the URL, http://babbage.cs.qc.edu/courses/cs081/Form_Script.php. First, just set up the submit event handler function so that it just returns true. The script should run on babbage, yeilding a web page that says there were no parameters received.

    To get the data in the table sent to the script on babbage, you will need to add input elements of type "hidden" to the form before letting it be submitted, which you will do inside your submit event handler. A hidden input is an input control that isn’t displayed on the screen, but its name and value are included in the data that gets submitted with the form. The data for all the controls in a form get passed to the action URL when the form is submitted, but the lower form, as designed, has no input controls, just the submit button. Putting the table itself inside the form doesn’t do any good because it is only input tags inside a form that produce name/value pairs to send to the action URL. So in this step, you will append hidden input controls with values equal to the data in each of the cells in the data table. The code to set up a hidden input control looks like this:

    var newInput = document.createElement('input'); newInput.name = 'a_unique_name'; newInput.value = 'a_value'; newInput.type = 'hidden'; dataValuesForm.appendChild(newInput);

    Since you will not know how many rows there are in the data values table ahead of time, you have no choice but to put code such as the above inside a loop that gets executed once for each row in the data table:

    var rows = dataValuesTable.getElementsByTagName('tr'); for (var row = 1; row < rows.length; row++) { ... }

    You could nest another loop inside that one to go through all the td elements in each row, or you could just write your code to go through the three data cells in each row separately. In either event, the problem you need to solve is how to generate a unique name for each cell. The easiest solution is probably just to make up a string that consists of the data in the cell plus the row number, such as "lastName_" + row. Remember, when you use + between a string and a number, JavaScript converts the number into a string and concatinates the two strings together.

    The 'a_value' string has to be the nodeValue of the first child of the td you are processing. So if you had an array of all the td cells in a row, you could use an expression like, cols[col].firstChild.nodeValue as the expression to assign to newInput.value.

    After adding the hidden inputs, submit the form and verify that all the data values from the second table are passed correctly to the server-side script, each with a different name. You may use either the GET or the POST method when submitting the form; you should experiment with both to make sure you understand the difference between the two.

    Download and run Echo_HTTP_Requests.jar and study the request messages that the browser sends to the server-side script for both the GET and POST methods, and for various characters in the data strings being submitted. Note: You need to have administrator privileges to run this Java program by default. You can run it by double-clicking on it. If it starts up but exits a fraction of a second later, the problem is undoubtedly lack of administrator privileges. (If it doesn’t start at all, the problem is probably that you do not have the Java runtime installed on your system.) The solution to the administrator problem is to run the program from a command line (terminal) window. Navigate to the directory where the Jar file is, and give this command:

    java -jar Echo_HTTP_Requests.jar 1024

    This will make the program listen for web requests on port number 1024 instead of 81. Your system should not require administrative privileges for this.

    Use Echo_HTTP_Requests by directing your browser to http://localhost:81 or http://localhost:1024 depending on how you started it.

    Test your work and save a copy of your code files in your Backups directory before proceeding to any of the next steps. You may do the optional steps in any order.

  7. Extra Credit: Make sure no duplicate email addresses get entered into your data table.

    To implement this feature, you will have to perform a third test on email addresses (in addition to checking for blanks and to checking for proper form using a regular expression). Before actually adding a row to the data table, compare each email address already there to the one the user has just supplied in the entry form, and reject the line and display an appropriate message in the error message cell.

  8. Extra Credit: Insert the data into the data table in alphabetical order.

    This task involves using the insertBefore(), which has this form:

    parent.insertBefore(newChild, existingChild);

    Here, parent is the node that you are going to insert the new node into, newChild is the new node to insert, and existingChild must be one of the child nodes of parent.

    To insert a row in the right place, you have to compare the values in the new row with the values in each of the existing row. Start with the first data row, and compare the new data with the data in that row. If the existing data value is less than the new value, go to the next row, but if not insert the new row before the existing row. Don’t forget to append the row at the end if it is greater than all the existing rows.

  9. Extra Credit: Add controls that let the user change between GET and POST submission methods.

    Add two radio buttons to the data form, and whenever one is checked, set the form’s method to GET, and when the other one is checked change it to POST.

  10. Extra Credit: Add a control that lets the user change the port number in the URL the form gets submitted to.

    This feature is to make it convenient to test the page against the Echo_HTTP_Requests program described above.

  11. Extra Credit: Add labels for the controls added in the previous two steps.

    See page 82 of the text to see how to use label tags.

  12. Extra Credit: Format your pages in interesting ways.

    Use CSS to lay your page out so the two tables are next to each other, for example. Make the list of links on your home page into a horizontal navigation bar. Both of these topics are covered in the text.

Submitting the Assignment

Before submitting the assignment, check the following:

  1. There must be no tab characters in any of the files in your site, and all lines must be no wider than 80 characters.
  2. The links to the XHTML and CSS validators at the bottom of your assignment web page must work, and must generate no errors or warnings when validated. Be careful, because it is easy to miss the warnings generated by the CSS validator — be sure the message is “No error or warning found”, not just “Congratulations!”.
  3. In Dreamweaver, check your Site’s set up to be sure you have “Use case-sensitive link checking” checked in the Local Info panel of the Site editor for your site. Then right-click on the site in the Files tab of the Fiels panel on the right side of Dreamweaver, and check links for the entire local site. Any errors will show up in the Results panel at the bottom of the editing window; be sure to fix them!