by John Brinkman
I ran some of the code I’ve written recently through JSLINT and I was… embarrassed. I was determined to make all my code strict. (Hard to find a better motivator than embarrassment.) The problem is that copy/pasting code from Designer to www.jslint.com got pretty tedious. Happily, the author of JSLINT (Douglas Crockford) has made the source code shareable. I took the source for JSLINT and incorporated it into LintCheck.pdf. Clicking the button on the form will prompt you for a PDF or XDP file. It will extract all the script, run JSLINT and report on what it finds. Here is a sample form with some coding errors and the resulting report. (You need Acrobat to run LintCheck.pdf.)
One of the things I quickly discovered is that JSLINT reports many issues that are not necessarily related to language/grammar strictness, but rather are more a matter of enforcing good programming style. Good programming style in turn reduces the likelihood of bugs. eg. At first I was annoyed when JSLINT insisted that all my control blocks be enclosed in braces, but I do understand how that coding practise makes source code more predictable — especially when you consider the potential ambiguity of an expression such as: "if (..) if (..) else (..)"
Some more details on using LintCheck.pdf:
In the script output, global references are highlighted in blue. If you see something highlighted in blue that is not a reference to a form object, then it is likely an undeclared variable. If you want to turn off this highlighting, insert a special comment in your code. e.g. /*global Price Quantity */ will turn off highlighting on script references to the Price and Quantity fields. commonly referenced globals such as "xfa", "console", "app" etc. have already been turned off.
Embed JSLINT directives in your code
If you want specific options to be enforced selectively in your code, you can control them with comments. For example, to turn on white space checking, insert this comment:
/*jslint white: true, indent: 4 */
To help you build up this string, there is a field on the form that shows what the comment would look like for the chosen options.
Conflicts with XFA Properties and Methods
Script writers who use variable names that conflict with XFA properties or methods will run afoul of the "naked field processing" (as explained here). Since JSLINT provides a list of variables and global references, LintCheck.pdf will report cases where users have a conflict in their script.
Tabs in Source Code
JSLINT is unhappy with combinations of spaces and tabs. If your line of code starts with 2 spaces followed by a tab, most code editors will bring you to column 4. JSLINT assumes this to be column 6. It assumes that a tab character always adds 4 spaces. If you mix tabs and spaces you will get complaints from JSLINT when strict white space is on. You will also find that the character position of errors is reported incorrectly.
Lint processing does not apply to FormCalc. But since LintCheck.pdf produces a script report, it seemed best to allow FormCalc scripts to be included in the output. There is an option to control whether FormCalc is included or not.
Suppose you have a calculation that looks like:
Price.rawValue * Quantity.rawValue;
JSLINT will generate an error: "Expected an assignment or function call and instead saw an expression.".
However, this is a valid construct in our forms. Calculations and validations are the result of the last expression evaluated in a script. The version of JSLINT embedded in LintCheck.pdf adds a new option to control this behaviour. To turn this check on (e.g. for script objects) add this directive in your script: /*jslint nonoop: true */
One reason to turn this check on is because for forms to work in Form Guides, calculations must be expressed as an assignment. e.g.
this.rawValue = Price.rawValue * Quantity.rawValue;
var fruits = ["apple",
// truncate the array
fruits.lenght = 2;
/*members length rawValue */
Now JSLINT will complain about the offending line because the "lenght" property is not in the l ist of sanctioned members.
Forms with lots of script will generate very large reports. Very large reports take a long time to generate. Running LintCheck.pdf on itself takes around 15 seconds to complete on my desktop system (and generates 133 pages).
Unfortunately, the parser in JSLINT is not able to handle E4X expressions. Since LintCheck.pdf makes use of E4X to handle XDP files, I needed a workaround to avoid having JSLINT stop when it encountered my E4X expressions. Turns out there are JSLINT friendly variations. The script expression to find the root subform and script elements of an XDP originally looked like:
var sRootSubform = xXDP.*::template.*::subform;
var vScripts = vRootSubform..*::script;
The alternate expressions looked like:
var vRootSubform = xXDP.elements(
var vScripts = vRootSubform.descendants(QName(null,"script"));