By Thom Parker July 16, 2009
That’s a very good question, and one commonly asked in various user forums. Usually this statement is followed by "I copied the code exactly," or "I changed something and then it broke." Unfortunately, this is a fact of scripting. Sometimes things just don’t work. So what do you do? When scripts break, it’s time to step back and figure out what’s wrong. In this article, we’ll discuss some of the most common problems and easy techniques for finding and fixing your script.
The very first thing to do when a script doesn’t work is to see if any errors were reported in the Console Window. The Acrobat JavaScript Console Window is the most important tool in Acrobat for JavaScript development. If the code caused an error of any kind (called an exception), it should be displayed in this window. This is the most important advice I can give you for finding out what’s wrong with your script. To see how this works, and to explore some common errors and techniques for figuring them out and fixing them, we’ll look at an example.
Example setup
The example uses some code placed in the validate script of a text field. To follow along with this example, you’ll need a PDF file with two text fields. The first text field is the input field where we’ll be entering the test code. The second text field must be named "Status." All the code presented below will be entered into the Validate Event of the first text field (Figure 1).
Figure 1 – Setup and code entry for example
The problem code
The code below has several bugs. Four of these bugs cause the script to throw an exception. They are the easy ones to find and fix. One bug causes the script to fail silently. These are the hard ones to find. The code itself is used to set the value of another form field, the "Status" field, when a value of "3" is entered into the first field. This is a basic example of how to automatically populate one field based on the input in another field.
if(event.value = 3){ var oFld = this.getField(Status); oFld.value = "Unknown"; }else{ oFld.value = "Known; }
Acrobat reports the first error as soon as the script is entered into the Validate Event (Figure 2). We don’t even need to look in the console window, although it’s also reported there. Acrobat tests the code for gross syntax errors when it’s entered, so we’ll find anything obvious very quickly. The error reported here is the most obvious and deadly kind, a syntax error.
Figure 2 – Acrobat displays syntax errors immediately on script entry
Syntax errors are probably the most common error since they are related to tiny details. JavaScript -- and all programming languages -- are extremely picky about syntax. Every comma, parenthesis, dot, quotation and bracket has to be in the right place or the JavaScript engine won’t be able to parse and run the code correctly.
Lesson number one: Programming is very detail oriented, so you need to check over your code carefully to make sure it’s all correct. For example: parentheses, brackets and quotes all come in pairs. There has to be both an opening and a closing member.
And that’s the problem with this code. The "if" code block is delimited with curly braces "{}." In the code above, there is only one closing brace -- "}" -- on line 4, which is where the error is reported. This report seems a bit confusing, after all the error is really on line 1, which is missing the opening brace. But that’s not how it works. The JavaScript engine doesn’t really know what the problem is; all it knows is that things were going along just fine until it reached the closing brace on line 4, when it realized there wasn’t a matching opening brace. So that’s where it reported the error.
Lesson number two: The reported line number for the error is not always the line the actual error is on. You’ve got to take a close look at your code to find the real culprit. In fact, in many cases Acrobat will simply report the last line of the script as the error location because it got all the way to the end before realizing some important piece of syntax is missing.
Here’s the code for the first fix. Notice the new "{" on line #2:
if(event.value = 3){ var oFld = this.getField(Status); oFld.value = "Unknown"; } else{ oFld.value = "Known; }
Uunfortunately we’re not even close to done. As soon as this new code is entered, Acrobat displays another error message (Figure 3).
Figure 3 – Errors are reported one at a time, as they are encountered
JavaScript is an interpreted language. This means that the JavaScript engine in Acrobat reads the code line by line. When it hits an error, it stops and reports the error. You have to fix that error and re-run the code to find the next error. There is no way to get a listing of all the syntax errors at once.
This error is a pretty easy one to fix. Acrobat is actually telling us the specific kind of error -- an unterminated string -- and the correct location in the code (line 8). Line 8 is reported because Acrobat didn’t know the string was unterminated until it changed lines. The error is really on line 7 where the string "Known" is being assigned to the field value. Since "Known" is a string value (a literal value), it must be surrounded by quotes. It’s the endquote that’s missing. See the fixed code below. To the casual observer, it doesn’t look any different from the previous code with the error. But we know the difference, and it’s a very important one. Without this fix the code will not run.
if(event.value = 3){ var oFld = this.getField(Status); oFld.value = "Unknown"; }else{ oFld.value = "Known"; }
Once this syntax error is fixed, Acrobat no longer complains. But the code still doesn’t work. If you’re following along by entering the code into a text field Validate Event, you’ll notice that no matter what you enter, this field the value is stuck at 3; it can’t be changed. Also, the "Status" field never changes. So what’s the first thing we should do? We need to look in the Console Window to see if an error is being reported. Sure enough, this is what’s displayed in my window (Figure 4).
Figure 4 – Exception (errors) are reported in the Console Window
What is this message trying to tell us? Working backwards from the end of the error message, we can see that the error is occurring in a "Field:Validate" event script and that the error is on line 3 of that script. The reported error is "Status is not defined." On line 3, Status is the name of the field that is being acquired with the this.getField() function. A quick look in the Acrobat JavaScript Reference tells us that the input to this function is a string. Hmmm, we do have a field named Status on the form. Ah-ha, since Status is the name of the field, it’s being used as a string literal and has to be quoted. That’s the problem. Since it’s not quoted, Acrobat thinks it is the name of a variable, and we don’t have a variable named Status. That’s why the message says it’s undefined. Alright, we’re past that one, let’s see what happens when we fix the code.
if(event.value = 3){ var oFld = this.getField("Status"); oFld.value = "Unknown"; } else { oFld.value = "Known"; }
The Status field is now acquiring a value. Yeah!! But the first field is still stuck at a value of 3, and the only value being written into the Status field is "Unknown." At least these two stuck values are consistent, 3 and "Unknown." Let’s take a look in the Console. Yikes! The script doesn’t work and no errors are being reported. What do we do now?
We can figure this out. We know two things about what’s happening.
Let’s take a closer look at the "if" statement on the first line of the script. Hmm, this is all pretty standard stuff, so let’s check in our handy Core JavaScript Reference to see if we’re using the operators correctly. Bingo, it gives us the answer. The equivalence comparison operator is "==", not "=". What’s happening is that the code is assigning a value of 3 to event.value instead of comparing it to 3. This is why the field is being overwritten. Furthermore, JavaScript converts the 3 into a value of true for the "if", so the first block of code is always run. Here’s the fixed code:
if(event.value == 3){ var oFld = this.getField("Status"); oFld.value = "Unknown"; }else{ oFld.value = "Known"; }
Again, a seemingly tiny change makes a huge difference in how the code runs. After this one is fixed, everything seems to be working correctly. So we’re done, right? No, we’re not done. While we’ve been doing all this testing and rewriting the code, Acrobat has been saving all the values and variables we’ve created along the way in the JavaScript environment.
For the final test, we need a clean JavaScript environment. Enter a value of 3 into the first form field. Save the PDF file you’ve been using for this test, close the file and then reopen it. Now enter a value that is not 3 into the first field and press return. Nothing happens. What’s going on? It was just working. Do you remember the first thing to do if something doesn’t work? Open the Acrobat JavaScript Console Window (Figure 5).
Figure 5 – Some errors can be hidden by the state of the JavaScript environment
Ah-ha, there’s an error reported. It tells us that on line 7, the variable oFld does not have any properties. Basically this means it’s undefined, as if it was never set. And in fact, that’s the case. This variable is set on line 3, which is inside the code block for the "if" statement. In the testing we just did, this code block was never run, so oFld was never defined. In our previous testing, we had run this code block several times. JavaScript is a very forgiving language and it had saved this variable in the local JavaScript environment. So even though oFld was not explicitly defined in the script when the "if" condition (event.value == 3) was false, Acrobat saved its previous value and then let us use it in the "else" code block. This forgiving behavior masked a problem with our code and got us into trouble when the PDF was opened at a later time. It just goes to show that no good deed goes unpunished. You need to do completeness testing, i.e., test as many conditions as you can think of.
To fix the problem, the definition of the variable needs to be moved from the "if" block to the first line of the script, where it will be run every time the script is run.
var oFld = this.getField("Status"); if(event.value == 3){ oFld.value = "Unknown"; }else{ oFld.value = "Known"; }
Now we’ve completed the fixes, the script should work perfectly. Who knew that such a short script could have so many problems. This is the nature of scripting.
Let’s review what we’ve learned.