To an extent, this chapter is a bit of a catchall, holding some things that I want to incorporate on the page that would not in and of themselves occupy a full chapter. While these items may not be tied together from a functional standpoint, they are related in that they involve using javascript to access form elements for some purpose. For example, I am going to enhance the function executed when one of the submit buttons is clicked to make it capable of verifying that the code in the relevant hidden field conforms to the manner in which the code values are constructed, much as a web page might check to make sure that an entered telephone number is appropriately formatted. Following that I will discuss setting at bat result codes in those relatively unusual circumstances in which an at bat event establishes sufficient information to allow the definition of the result code.
Before I get into that, however, I want to address a measure that should prevent a user from inadvertently following a sequence that could lead to the construction of a malformed code. In the versions of dosel() and dosel_onbase() that have been used to this point, a user could follow a selection path leading to assignment of part or all of a code, but then change selections in select boxes prior to the point they had reached in the sequence. This could result in a string that represented the initial part of one code being concantenated onto the intial part of another code value, already stored in the hidden field. To see this in action, go to the final working example in the second chapter previous to this one, in which I changed the hidden fields to text widgets to allow the codes to be displayed as they are constructed. Select "batted foul" in the at bat events select mechanism, then "in play", and "caught" or "error". Select a position, take a look at the abe_code value, then select a different position. The second position will have been concantenated onto the end of the value previously stored there.
Obviously, that is not something that I want to have happening. The new version of the validentry() function I will be introducing shortly will keep a value like that from being submitted, but by its nature validentry() will return a relatively generic message upon failure, because it could be triggered by the attempted submission of any code value that is not in the appropriate form, whether it be blank, partial, or malformed. As this would likely puzzle a naive user, I decided to trap the circumstance that leads to its creation in the first place.
The context that leads to the creation of this problem lies in the switch cases that append chunks of text to what is stored in the hidden field. Therefore, the best way to trap the problem event is to make sure that the value already stored in the hidden field is not longer than it should be, whenever I enter one of the cases in which text is appended. To that end, I looked at the values that would be stored in the hidden fields and realized that the following values represent the maximum lengths for the string in the hidden field as selections from the specified select box are processed.
| Hidden Field | Select Box | Maximum Length |
| abe_code | abe4 | 10 |
| abr_code | abr3 | 10 |
| abr_code | abr4 | 13 |
| obe_code | obe3 | 10 |
| obe_code | obe4 | 13 |
var stored_abe=new String(document.abe.abe_code.value); var stored_abr=new String(document.abe.abr_code.value);I can now use the length property of these objects to determine to pop an alert window and break execution of the function if the length of that value exceeds the value specified in the table above. The following, for example, is the snippet I have included in the case that processes the values associated with selections made in abr4.
if (stored_abe.length > 10) {
alert('Full Code already stored. Return to first select box to select another code');
return;
}
As the values returned from the select boxes on all three of the on base forms are handled by dosel_onbase() and the hidden fields in those forms all have the same names, I create the analogous string object early in the function.var stored=new String(form.obe_code.value);and use the length of that string object to determine whether to pop the alert window, as in the following snippet that is now included in the case that processes values associated with selections made in the select boxes that have the index value of 3 ... in other words, selections made in the last select box in the on base event sequences.
if (stored.length > 13) {
alert('Full Code already stored. Return to first select box to select another code');
return;
}
The following working example will pop the alert window if the selections that are made create a bad code value. The fields that would be hidden are displayed below the selection mechanisms to provide a visual sense of what is going on here.| At Bat Event | At Bat Result |
target.length=0; document.abe.abe3.length=0; document.abe.abe4.length=0;and
target.length=0; document.abe.abr3.length=0; document.abe.abr4.length=0;to the abe1 and abr1 cases, respectively, accomplished what I desired. In dosel_onbase(), however, I got just a little fancier, using a while() loop to iteratively zero out the length of the select boxes by accessing them through their position in the elements array.
var ctr=1;
while (ctr < 4) {
form.elements[ctr].length=0;
ctr++;
}
Note that I start with the index value 1, which represents the second select on the form, because I of course do not want to overwrite the initial values in the first select box. Once a selection is made in the first select box in a sequence, the later boxes in that sequence will now be blank. (I am not going to drop in a working example just yet ... have a bit of patience.)The only problem with the sequence at this point is that selecting the same option previously made in the first select will not blank out later selects. That is because I am associating dosel() and dosel_onbase() with the onchange() event. As selection of the same item does not change anything, the functions are not executed. By changing the triggering event to onclick(), which invokes associated events whenever a user clicks on a selection, that was resolved.
Now I will include a working example.
| At Bat Event | At Bat Result |
With that out of the way, I can move on to the construction of the new version of validentry(). This one is a little tricky beyond the application level task of appropriately constructing the function, largely because the mod_perl-enabled apache server is generating the page through the perl interpreter. First, however, I will discuss the construction of an appropriate pattern against which to match the values of the hidden fields before submission.
The regular expression I constructed a few chapters ago when I lauched my first foray into javascript was, of course, a very simple one that simply checked to make sure that there was a numeric character somewhere in the string against which the regular expression was tested. The regular expression that I will construct here will verify that the constructed codes fully conform to the structure of the codes contained in the database, thus preventing the inadvertent submittal of a partially-constructed code. This regular expression will be not unlike those often used on web pages to verify what is entered for a social security or telephone number, but as I am constructing the relevant value I have the freedom to require that the submitted value strictly conforms to the specified pattern. Similar constructs that verify user input should be more flexible to account for the variety of manners in which a user might enter a valid string.
Here, however, I am primarily concerned with verifying that a complete code has been constructed before I send the submission to the server.Therefore I will construct a template to which the value must correspond before submission is allowed. As javascript's regular expressions are modelled on perl's, this almost by definition means that there are a number of ways to go about constructing the regular expression. Recall that the first segment of the code is upper case alphabetic, but that after that segments could be either alphabetic or numeric. The regular expression could therefore be expressed as
[A-Z]{3}:[A-Z,0-9]{2}:[A-Z,0-9]{2}:[A-Z,0-9]{2}:[A-Z,0-9]{2}
As may be apparent, the number in curly braces represents the number of occurences of the specified characters. The expression in the first part of the regular expression thus matches any string of three occurences of upper-case aphabetic characters, and in the second part any string comprised of two of the characters in the set of upper-case aphabetic characters and integers. A colon (":") must of course be between the segments so specified. Note that this pattern would have some failings should it be applied to typed user input. For example, the first part of the expression would accept any three capital letters, whether they made any sense in the context of the code or not. In such a circumstance that part of the pattern could be expressed as "[ABE,ABR,OBE]", but a better approach would involve the use of a select box to define that part of the entry. This pattern would also accept mixed upper-case alphabetic and integer values for the segments after the first, which does not perfectly correspond to the form of the code, which would involve either upper-case alphabetic or integer strings. The part of the pattern that corresponds to those segments would therefore be more appropriately specified as "[A-Z]{2} || [0-9]{2}". All of that, however, is only tangentially pertinent to this situation, because I know how the code is being constructed. I just want to make sure that all of it is there before I allow it to be submitted. This frees me from the concerns addressed in the last paragraph, and allows me to use a more simple formulation than that above. The template I use is based on the "\w" special character, which will match any alphanumeric character. In concert with proper placement of the colons, I can therefore determine whether I have an appropriately constructed code. The template I use is therefore based on the following expression.
\w{3}:\w{2}:\w{2}:\w{2}:\w{2}
Now this is where things get tricky. First, there are two ways to instantiate a regular expression in javascript. The first is to instantiate a new RegExp() object as I did in the early example, and the second is to instantiate the regular expression object with the expression enclosed within a pair of forward slashes ("/"), which denote a regular expression. Thus
var regexp =new RegExp("[0-9]");
andvar regexp=/[0-9]/;are equivalent. Using the second form, the expression I created above would therefore be defined as
var regexp = /^\w{3}:\w{2}:\w{2}:\w{2}:\w{2}\$/;
(The caret ("^") and dollar sign ("$") at the beginning and end of the expression anchor the match to the beginning and end of the submitted string. While this should not be necessary under normal system operation, it is not beyond the realm of possibility that hardware problems on the client could result in a value that would pass an unanchored match but was not in a form appropriate for storage in the database.)When the regular expression object is created by invoking the RegExp() constructore, however, the backslash ("\") in the expression that is used to flag special characters has to be escaped with a second backslash to tell the javascript parser not to treat that as a special character for its own purposes. Therefore, declaring this regular expression object by using the RegExp() constructor looks like this:
var regexp = new RegExp("^\\w{3}:\\w{2}:\\w{2}:\\w{2}:\\w{2}\$");
From the perspective of legibility alone, many therefore recommend instantiating the object by including the expression within the regular expression delimiters.The previous paragraph, however, pertains to javascript alone. Once mod_perl is thrown into the mix, each backslash must be escaped to tell the perl interpreter to pass it along. The instantiation of the regular expression object using delimiters now becomes
var regexp = /^\\w{3}:\\w{2}:\\w{2}:\\w{2}:\\w{2}\$/;
I have dealt with similar issues elsewhere in this document, so this should seem at least vaguely familiar. It is, however, important to recognize the difference between this expression and the one that immediately preceeded it. To restate, although the expression looks the same, in the first case the backslashes are escaped for the javascript environment while in that just above the first backslash is there for perl, to tell it to treat the second backslash as a character, ignoring the special functionality perl associates with its use. In this kind of environment, with mod_perl being used to return the page, if a mistake is made in the number of escapes included in a given string leads perl to attempt to interpret a special character inappropriately, an error similar to the following may be displayed when the server is started.Unrecognized escape \w passed through at /home/www/bb_lib/BB_APP_INTERFACE.pm line 1714.A quick and dirty way to spot a segment of a string that is triggering the error above is to look for an odd number of backslashes, because that means that at that point perl will reach what it will attempt to interpret for its own use. Recognize that this does not mean that the specific string will be escaped appropriately for the environment to which it is being passed, it just means that perl will not choke on it.
So how does one instantiate a javascript regular expression using the new() constructor when the string has to be passed through mod_perl? As javascript needs two backslashes, one for the special character and one to escape that special character for its own parser, each of those backslashes must have a backslash to tell perl to pass it along. Therefore, the statement in the here document that includes the javascript code would look like this:
var regexp = new RegExp("^\\\\w{3}:\\\\w{2}:\\\\w{2}:\\\\w{2}:\\\\w{2}\$");
Most would agree that there are a lot of backslashes there. Clearly, the version of the statement that uses the regular expression delimiters represents code that is more legible and easier to maintain.As a side note, a viable strategy to employ in such circumstances is to develop a working version of the regular expression in a static html document, much as I have done in the working examples in the past few chapters. Once the javascript works, the task has been limited to making sure that the module code that generates the pertinent page is appropriately escaped. (I do not, however, recommend trying to put three different working examples of the same script on the same page. I can say from personal experience that keeping everything straight in such a circumstance is a real pain in the ... neck. <grin>).
Finally, I want to set the at bat result code in those circumstances in which it can be determined by the selections made in the at bat events sequence. The simplest context to which this applies is the at bat event "hit by pitch", which is, of course, also the result of the at bat. The other set of contexts in which an at bat result can be directly recorded as the result of at bat events is that of caught foul balls. In these circumstances, the selection sequence for the at bat event has already established sufficient information to allow the at bat result code to be assigned, and it makes absolutely no sense to require the user to go through another set of selections to define something that is already known. (The discerning reader, which means all of you out there, might well ask what the difference is between foul balls and balls batted in fair territory. Why don't I do the same type of thing for fair balls? The answer to that has to do with the nature of the event. A foul ball, even one in play, will not inherently result in resolution of the at bat. As long as the batter is not bunting, foul ball after foul ball can be hit, and the at bat can theoretically go on forever. A fair ball, however, is inherently going to result in a resolution of the at bat, be it a hit, an out, reaching base as a result of an error, or whatever. That is the reason why foul balls stay within the at bat event sequence, and fair balls do not.) While there are other at bat event selections that will ultimately be used to trigger automatic assignment of the at bat result code, such as recording a strikeout on strike three or a walk on ball four, the significance of the at bat event to the at bat result will be established by the state of the at bat; information that will not be available until I implement a state management strategy in an upcoming chapter.
At inception, this is a very simple adaptation. I simply added the following lines to the case pertinent to "hit by pitch".
//since hit by pitch also establishes at bat result, store that value in abr_code document.abe.abr_code.value='ABR:03:02:NA:01';The analogous snippet for a caught foul ball is only slightly more complex. As the select sequence that involves caught foul balls establishes that the foul ball was caught in the step before the final selection determines the position that made the catch, which is required to assign the full code. Therefore, storage of the abr_code is determined by the presence of the appropriate value in the substring of the stored value that was established in the prior select.
//a caught foul ball also establishes the result of the at bat, so that value can be assigned here
if (stored_abe.substr(7,2)=='06') {
document.abe.abr_code.value="ABR:01:04:"+code.substr(1,2)+":NA";
That might sound complicated, but it is not. All that is required is that I remove everything from the at bat result selects and display a descriptive option in the first select in that sequence that will not lead to unanticipated results. This is accomplished by simply adding the folowing snippet immediately below the lines that store the abr_code.
document.abe.abr1.length=0;
document.abe.abr2.length=0;
document.abe.abr3.length=0;
document.abe.abr4.length=0;
document.abe.abr1.options[0]=new Option("No Entry Needed","");
The entire if() statement that stores the code for caught foul balls now looks like this:
//a caught foul ball also establishes the result of the at bat, so that value can be assigned here
if (stored_abe.substr(7,2)=='06') {
document.abe.abr_code.value="ABR:01:04:"+code.substr(1,2)+":NA";
document.abe.abr1.length=0;
document.abe.abr2.length=0;
document.abe.abr3.length=0;
document.abe.abr4.length=0;
document.abe.abr1.options[0]=new Option("No Entry Needed","");
}

As one might expect, a little thought will reveal that there are wrinkles to this as well. What happens if a user clicks on one of the triggering at bat events as a mistake? Once I have made the changes above, they would be in effect locked out of entering anything in the at bat results sequence. While many readers might find this a bit of a surprise, it is a fact that users do make mistakes.<ahem> Therefore, I want to have a way to rewrite the options and values in the at bat result sequence to their initial state.
It seemed to me that the best way to catch changes from one of the triggering at bat events was to look for a different selection in either of the two selects that establish that the abr_code can be set, and when the abr1 select already displays the text "No Entry Needed". To a certain extent, this is arbitrary. All I really need to do is make sure that whatever selection is triggering the execution of the is not from one of the at bat result selects, because that would result in the sequence being continuously rewritten to their defaults. Minimizing the set of events that will trigger this reset will, however, save a few execution cycles in all but those contexts to which it most directly pertains. I therefore based that reset on the following three conditions: the selection must have been made in either abe1 or abe3, the value of the selected option must be other than those associated with hit by pitch and caught foul ball, and the text for the option in abr1 must be "No Entry Needed". This translates into the following lengthy if() statement:
if (((box.name == "abe3" && code != "ABFR02") || (box.name=='abe1' && code != "05")) && document.abe.abr1.options[0].text == "No Entry Needed")(Remember that I have previously defined the variable code as the value of the selected option in the current select box.) That reads in a fairly straightforward manner, as long as one recognizes the form of the javascript and ("&&") and or ("||") logical conjunctions. The code that resets to the default values is also straightforward:
document.abe.abr1.options[0] = new Option("Out","01");
document.abe.abr1.options[1] = new Option("Hit","02");
document.abe.abr1.options[2] = new Option("Other","03");
document.abe.abr2.options[0] = new Option("Subcategory","");
document.abe.abr3.options[0] = new Option("Subcategory","");
document.abe.abr4.options[0] = new Option("Subcategory","");
document.abe.abr_code.value="";
It is possible that the form of this expression will get fancier with time, but this will suffice for now. It is also likely that I will change the form of the if() statement once the availability of state information allows me to add additional triggers to set the at bat result code. Here is the working example of the pertinent selection sequences with these changes made.| At Bat Event | At Bat Result |
Now that stuff is worked out, at least to an extent, one item remains before I can start messing around with what I am going to do at the server. That is, inasmuch as the selection sequences for both at bat events and at bat results are both independent and inter-related, and both are part of the same html form, some thought must be given to an appropriate interface for form submission. Ultimately, I decided to include three different submit buttons, one for submitting at bat event information only, one for submitting at bat result information only, and one for submitting both. This is at one level an artifice, because all of the information within the defined form will be submitted when any of the three buttons are pressed. In practice, however, the buttons will provide the user with control over just what is evaluated from the form, because I will use the value associated with the button (the text displayed on the button) to determine what is executed on the server.
The inclusion of a button for submitting both the at bat event and at bat result codes does, however, add a little bit more challenge to the process of verifying that the codes to be submitted are of the correct form, for to this point I have simply passed the value of the pertinent hidden field to the validentry() function as an argument, and when I submit both codes I must have a way to check both of them. The resolution of this one is easier than it might appear. Javascript passes arguments to a function in an array named, logically enough, arguments. As a result, with some modification to validentry(), I can pass one or more strings to the function and have the function iteratively perform the regular expression check on each of the strings in the arguments array. The new version of the validentry() function is now
function validentry() {
for (var i=0; i<arguments.length; i++) {
if (! is_code(arguments[i])){
alert('At least part of the submission is not in the appropriate form.\\n
Please begin again and complete the selection sequence.');
return false;}
}
}
The call to validentry() is, of course, associated with the onclick() event for the submit buttons, so these lines are now at the end of the abe html form.
$$r->print("<center><input type='submit' name='submit' value='Submit_At_Bat_Event'
onclick='return validentry(abe_code.value)'>");
$$r->print("<input type='submit' name='submit' value='Submit_At_Bat_Result'
onclick='return validentry(abr_code.value)'><br>");
$$r->print("<input type='submit' name='submit' value='Submit_Event_and_Result'
onclick='return validentry(abe_code.value,abr_code.value)'></center>");
Note that the only structural change in validentry() is the wrapping of the if() statement that calls the is_code() function within a for() loop that executes while the value of a counter variable is less than the length of the arguments array. The i counter is initialized to the value 0 so it can be used to reference the elements of the array, and as the value returned by the length attribute will be a positive integer value representing the number of elements in that array the index value of the last element in the array will be one less than the number of elements in the array. For example, the sole element of an array of length 1 would be referenced as arguments[0], which is why the upper bound on the execution of the loop is specified as "i < arguments.length". I know that I have been over this kind of thing before, but as I have seen javascript tutorials in which examples of similar loops made the mistake of initializing i to 1 I thought it appropriate to be explicit about the proper formulation here. The element occupying the position referenced by the i counter is then passed to the is_code() function for testing against the specified regular expression. In this version I have also included a more descriptive error message in the alert() box that will be displayed should the regular expression test fail. It is important to recognize, however, that no indication will be given as to which code value precipitated the error. The submission will simply be terminated with the first string that does not match the regular expression. While that is currently only of concern when both code values are submitted, it does strike me as desireable to return some indication of the specific code that failed the test. I will add that in a later chapter.As a side note, I put the line break character in the string sent to the alert box in validentry() primarily to allow the message to be fully displayed on this page without stretching the page to the right while still maintaining valid code. As it is, the alert box message will be split over two lines. Note that here as well the "\n" line break special character must be escaped or perl will simply insert a line break into the generated page. Should the line be broken in the code without the line break special character, whether intentionally or by forgetting to escape the special character, a javascript "unterminated string" error will be generated. In Internet Explorer that error will be displayed when the page that contains the javascript code is displayed. In Firefox, however, the page will load but the script will be disabled; the javascript console under the tools menu must be open when the page is loaded to discover the source of the problem.
It is important to recognize that despite what has been done in this chapter, a user can still select the wrong event. Input errors are something that refinements to the user interface can never completely eliminate, but as I begin to track the state of the at bat it should be possible to both introduce further automatic storage of results and to trap inconsistent selections. Therefore, in the next chapter I will start building the server functions that will process submissions from these forms and maintain state information.