Archive for June, 2009

Away for a week

Ok, this time I really am ignoring you.  After today I’m off to cottage country.  I will have no access to the Internet, but plenty of access to boats, swimming, campfires, fishing, mosquitos,…  Back to work on July 6. Meanwhile, happy Canada Day and happy Independence Day.

200607160046

Client/Server Scripting Differences

One of the brand promises of XFA is that you can design your form once and re-use your form design in multiple contexts.  Most notably: design once and re-use for both data capture and for printing.  Re-use means that the form looks the same whether rendered as interactive PDF or PDF/A or Postscript or PCL or ZPL …  It also means that your calculations and script logic work the same in all environments.

We process XFA documents in multiple environments — in Acrobat/Reader and in LiveCycle server — via Forms and Output.  We refer to Acrobat/Reader as "client" and LiveCycle as "server".  Our challenge is to make sure that the forms get consistent treatment in both client and server.

The good news is that you could design hundreds of forms and never run into any differences between the XFA implementations on the client and server.  But if you are making extensive use of JavaScript, you will want to be aware of some boundaries.  Many users begin designing forms for only one environment — usually for interactive PDF first.  I have witnessed a couple of occasions where users with extensive script frameworks were surprised when their forms did not work on the server right away.  There are some differences.  In this post I will list those that I am aware of.

Different Engines

I have previously pointed out that we use different JavaScript engines on the client and server.  The JavaScript engine inside Adobe Reader is the Mozilla engine.  The engine on the server is an Adobe-implemented engine (Extendscript).  Extendscript is also used in all the Adobe Creative Suite applications.  The good news is that JavaScript is a standard.  Being based on a standard allows differing JavaScript engines to behave compatibly.  The bad news is that standards are not always implemented consistently.  Just ask an experienced web author who writes JavaScript for multiple browsers.

Handling Differences

Before describing specific differences, it is important to note that you can write script conditionally to handle the different environments.  The xfa.host.name property allows you to write code such as:

if (xfa.host.name === "Acrobat") {
    // do client stuff
} else if (xfa.host.name === "XFAPresentationAgent") {
    // do server stuff
}

Different Exceptions

When the XFA runtime encounters an error, we generate a JavaScript exception.  The exception objects have different properties on the client and server.  If all you ever do with an exception is call the toString() method, you don’t need to worry about the differences.  If you want to examine more details of the exception, you need to know about the differences.  Read more here.

Strict Scoping

Lots has been written about strict scoping: here and here.  The main take away is that while the client has support for non-strict scoping, the server has always been strict.  For forms to be compatible, choose strict scoping.  (Choose strict scoping even if you’re not concerned about server compatibility).

Script Objects

I have noticed recently that on the client, script objects inherit properties of their parent subform.  e.g. the expression "this.border" inside a script object returns the border property of the parent subform.   On the server, the script object does not inherit:  "this.border" is undefined.  To ensure compatibility, make sure your script object doesn’t make use of the inherited properties.  If you need to access parent subform properties, reference the subform explicitly (by name).

Global Variables

JavaScript variables declared in calculations have different scope in client and server. Variables used in scripts on the server are global to the form.  Variables used in scripts on the client stay local to the script.  For example, suppose you have two fields F1 and F2 with these calculations:

form1.S1.F1::calculate – (JavaScript, client)
var foo = "field 1 value";
this.rawValue = foo;

and

form1.S1.F2::calculate – (JavaScript, client)
this.rawValue = foo;

On the server, F2 will evaluate to "field 1 value".  While on the client it will return a JavaScript error.

To ensure compatibility, make sure your variables are always declared.  Or better yet, limit the number of global variables you declare.  Put them inside functions.  Regardless, the best practise is to make your code strict by running jslint.

Logging

On the client we frequently use console.println() to issue debugging trace statements.  However console is an Acrobat object that appears only in the client context.  On the server use
xfa.log.message(); and your debug trace will appear in the server log file.

References to "$"

If you write script in FormCalc you will know that the "$" symbol refers to the current object. It is the equivalent of "this" in JavaScript.  What you may not know is that "$" works just fine in client side JavaScript.  However, it does not work on the server side.  Extendscript reserves the use of "$" as a global object to get and set system information.  e.g. $.stack will return a string with the current call stack.  For compatibility reasons, avoid using "$" in JavaScript.  Do not use it as a shortcut to "this" and do not use it as a variable name.

Script to Data

There are client/server differences in how you access data via script.
The short explanation:

If your root dataNode is named "form1" then on the client you can reference "xfa.datasets.data.form1".  On the server, "form1" does not appear as a child of "xfa.datasets.data".  Instead, you get to your root dataNode using $record (or xfa.record).  Using $record works reliably on both client and server.

The detailed explanation (definitely in the deep end):

There are settings in configuration (xfa.config) that impact how a form is processed.  Some config settings will be different on the server than on the client.  One such setting is xfa.config.present.common.data.adjustData.

Without getting into too much detail, the purpose of turning this setting on is to make sure that form data gets adjusted to match the form template so that we can reliably round-trip our forms — i.e. make sure a form will bind to data the same way after save/close/re-
open.  Having adjustData turned on requires more expensive processing.  On the server we usually set adjustData=false — since round-tripping isn’t a concern.  The impact on script is that when adjustData is false, we disconnect our data records from the datasets.data node.  Since they are disconnected, the expression xfa.datasets.data.form1 (or $data.form1) will not work.  But $record will reliably return the form1 node.

No, I’m Not Ignoring You

I thought it was pretty quiet around here.  About a month ago we updated our internal blogging infrastructure.  Coincidentally, I haven’t seen any comments on my posts since then.  Well, turns out 100% of the comments were flagged as spam.  I have gone into the spam bucket and approved all your comments.  I will reply to them over the next couple days.

Sorry for the inconvenience.  And thanks for the commenting — I really appreciate hearing your perspectives on the technology.

Managing Tab Stops in Fields

When I was working on my previous blog entry, I struggled a bit with rendering JavaScript source code in fields.  The issue was how to accurately display text with the tab stops that are embedded in the code.  The goal was to have the JavaScript code appear with the same spacing you would see in the source editor.  For this to work, the tab spacing needed to be exactly 4 characters.  The solution involved:

  • Use a non-proportional font to display the text (courier new)
  • Figure out the width of 4 characters: a 10pt character in courier new has a width of 6 points.  4 characters is 24 points
  • Set the default tab for the field.  Since Designer doesn’t expose tab properties for fields, the form uses an initialization script to set the default tab: this.para.tabDefault = "24pt";

Tab Leaders

Having accomplished that much, it seems worthwhile to spend some time showing some of the other cool things you can do with tab settings in fields.  In Acrobat/Reader 9 we introduced tab leaders.  To see how this feature works with static text, check out Stephanie Legault’s tutorial.

That’s fine for static text, but what about fields?  If you want to use tab leaders in your form fields, you need to understand the field properties that control tab behaviours.  One way to get there is to read the relevant portions in the XFA specification.  Tab settings are paragraph properties.  A field’s paragraph properties are defined in the <para> element (Part 2/Template Specification/the para element).  Now, you could fill your head with all that book knowledge or you could code from examples

The first page of the sample shows some variations on tab settings.  Each sample field has an initialization script to set the tab properties.  In each case there is a corresponding field that displays the resulting <para> element syntax.

tabDefault

This property on the <para> element controls the default tab setting.  If you want a tab stop every half inch, this would look like:

<field>
    <para tabDefault="0.5in"/>
</field>

To set this via script:

form1.tabExample1.sample::initialize – (JavaScript, client)
this.para.tabDefault = "0.5in";

tab-stops

The general format of a tab leader is: "[alignment] [leader] location ".  You can string a bunch of these together to specify multiple tabs.  If you look at the sample form you will see some variations.  The alignment ( left (before) / right (after) / center / decimal ) determines how the text is aligned at the tab location.  The leader parameter determines how to fill the space leading up to the tab position — space, dots, rule, characters.  The 3rd parameter is the actual tab position.   Hopefully it’s all self-explanatory from the samples.

Tabs and Data Entry

So far, this discussion has been assuming that we’re rendering data that has embedded tabs.  Do tab settings work with interactive fields?  Well, yes and no.  The tab settings work fine.  The problem is, how exactly do you enter a tab character into an interactive field?  The tab key moves you to the next field.  The workaround the example uses is to define a key sequence that will insert a tab character.  Pressing "shift" + "space" will insert a tab character.  The way this works is that the field has a change event to translate this sequence:

form1.#subform[0].tabExample1.sample::change – (JavaScript, client)
if (xfa.event.change === " " && xfa.event.shift) {
    xfa.event.change = "\t";
}

Tabs and Rich Text

Rich text introduces a  couple of wrinkles wrt tabs. 

The tab character is not handled by XHTML.  XHTML processing rules tell us to collapse all white space down to a space character.  To get past this, we define a special style attribute to specify a tab:

<span style=’xfa-tab-count:1′/>

The attributes that exist on the <para> element are also available as style properties: tab-interval and tab-stop.  With a plain text field, the paragraph properties are constant for the whole field.  With rich text, the paragraph properties can be re-specified with each <p> element.

The second page of the sample form has an example where a table of values is formatted into a plain text and a rich text field.  To see the code, look at the form:ready event of each field.  The script for the plain text variation is much simpler, but the script for the rich text field does some more elaborate formatting.  But back up a moment.  Likely the idea of constructing rich text is new to some of you.  The technique is fairly straightforward — build a string representing the XHTML content and then use the loadXML() method to apply the text to the field.  Using XHTML allows the second field to enhance the output:

  • The tab settings for the first line (paragraph) are different from the rest of the field (center aligned with a space leader)
  • The cents portion of the prices are displayed in a superscript
  • The tab leader is styled — the baseline is raised so the dots appear in the middle of the line rather than the bottom.

The overall effect makes the second menu field a fair bit nicer.  Imagine what someone with artistic skills could do.

Aligned Dots

Did you notice?  The dots in the plain text menu line up with the dots used in the rich text menu.  By default, the dot leaders are aligned between different objects on the page. i.e. the dots are aligned on a grid defined at the page level.

The Deep End

Ambient Attributes

When the rich text is rendered, the display properties are determined by combining the field properties with the properties specified in the XHTML.  An example would help:

<field>
  <font typeface="Myriad Pro"/>
  <value>
    <exData contentType="text/html">
      <body>
        <p>Text 1<span style="font-family:Arial">Text 2</span></p>
      </body>
    </exData>
  </value>
</field>

In this example, the XHTML has not specified a font for the string "Text 1".  Whereas the string "Text 2" is explicitly styled as Arial.  This means that the font for "Text 1" is the ambient font — the font inherited from the field definition — in this case Myriad Pro.  Same applies to
tab settings.  By default the XHTML will inherit the tab settings from the field.  If you want to deviate from the default, then specify it inside a <p> element in the rich text.  That explains why in my rich text menu example I explicitly styled the first paragraph, but allowed the remaining text to inherit the field paragraph properties.

Building Big Strings

XHTML strings can grow very large.  You need to be careful how you build the strings.  Using the " += " operator can lead to performance issues.  Appending to a large string is expensive.  It breaks down to these set of operations:

  1. allocate new storage for the larger string
  2. copy the old string to the new storage
  3. append the new string to the new storage

The larger the string and the more append operations, the more expensive this becomes.  A more efficient alternative is to build an array of small strings and at the end, use the join() method to generate one large string.  Here are two examples for comparison:;

var S = "hello world";
S += " more text";
S += " even more text";

In comparison:

var StringArray = [];
StringArray.push("hello world");
StringArray.push(" more text");
StringArray.push(" even more text");
var S = StringArray.join("");

Of course, in this specific example, the strings are so small that the performance difference would not be noticeable. But when the number of append operations grow, the array technique will become much faster.

Enforcing Strict JavaScript

I have recently discovered JSLINT (www.jslint.com).  This is lint processing for JavaScript.  For the under-40 crowd, lint is a unix utility we used back in the days when C compilers were not strict.  Used to be that you could do things in C that were blatantly wrong — and detectable — but the C compilers and linkers happily gave you enough rope to hang yourself.  lint analyzed your C source code, reported errors, and encouraged you to make your code more strict. 

Now we have JavaScript.  A wonderful utilitarian language that also has a ready supply of hanging rope.  A sizeable share of our JavaScript programming errors happen because we inadvertently make use of the laxness that is part of JavaScript.

This is where JSLINT comes in.  JSLINT is a utility (written in JavaScript) that will analyze your script and report on errors it finds.  When you visit www.jslint.com you can paste your code into the HTML page, and you will get a report of errors and other information about constructs in your script.  JSLINT will continue to complain about your code until you have reduced it to a strict subset that can be used reliably.

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:

Global References

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.

Options

If you look at www.jslint.com you will see a number of options that make JSLINT more or less strict.  Some of those options are relevant only to JavaScript embedded in HTML pages.  The more generic options are included in LintCheck.pdf.  When I check my code, I turn on the options for "Strict white space", "Disallow undefined variables", "Disallow == and !=" and "Disallow bitwise operators".

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.

FormCalc

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.

No-op expressions

Suppose you have a calculation that looks like:

form1.#subform[0].check[6].result::calculate – (JavaScript, client)
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.

form1.#subform[0].check[6].result::calculate – (JavaScript, client)
this.rawValue = Price.rawValue * Quantity.rawValue;

Track Members

One common JavaScript coding error is to misspell a member name.  Look at this sample script:

var fruits = ["apple",
              "orange",
              "peach",
              "plum"];
// truncate the array
fruits.lenght = 2;

The intention was to truncate the array to 2 — but it doesn’t because I misspelled the length property.  The script runs without a runtime error — because JavaScript assumed we wanted to populate a new property called "lenght".  The solution here is to add a comment at the top of your script listing all the members you expect to see:

/*members length rawValue */

Now JSLINT will complain about the offending line because the "lenght" property is not in the l
ist of sanctioned members.

Performance

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).

E4X Handling

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[0];
var vScripts = vRootSubform..*::script;

The alternate expressions looked like:

var vRootSubform = xXDP.elements(
                                  QName(null,"template")
                                 )[0].elements(
                                  QName(null,"subform")
                                 )[0];
var vScripts = vRootSubform.descendants(QName(null,"script"));

 

 

Displaying Parallel Columns of Text

There is a common form design challenge where we want to place parallel columns of text side-by-side on a page.  We want the text in each column to grow vertically and to split over to the next page(s) as needed.

One Growable, Splittable Field

First the basics:  Before tackling side-by-side fields, how to make a single field growable and splittable:

  1. Make sure you have saved your form as an "Adobe XML Dynamic Form" or as an "Adobe XML Form".
  2. Drag a text field on to your canvas. 
  3. On the Object/Field tab select: "Allow Multiple Lines"
  4. On the layout tab select: "Expand to Fit" under the Y: measurement.
  5. For all subforms above this field in the form hierarchy select "Auto fit" under the layout tab
  6. For all subforms above this field in the form hierarchy select "Allow Page Breaks Within Content" under the Object/subform tab.

If you fill that field with enough data, it will grow to the bottom of the page.  Fill it with more data and it will cause an overflow.  A new page will be added and the field will split between pages.

Two Growable, Splitable, Positioned Fields

The next part of the challenge is to make this work for growable fields situated side-by-side.  If the parent subform has positioned layout, then it’s fairly easy.  Just place the two fields side-by side.

Two Growable, Splitable, Flowed Fields

If the parent is flowed left-to-right (Western text) then you won’t get the effect you want.  If the left-most field has enough data to overflow the page, then the result is that the other growable field doesn’t appear until the second page.  This is because in a flowable environment, the layout algorithm will finish layout for the first field before starting the second.  Once we have spilled over on to a new page we don’t go back and fill in more data on a previous page.

To make parallel columns work in a flowed environment, add the two fields to a table.  Make them sibling cells in the same row.  The layout algorithm for table rows will negotiate the row height between all the cells, and will make sure they start and end together.

Splitting Problems

When the layout algorithm wants to split content, between pages, there’s a step where it negotiates a split point.  When there are side-by-side objects, they need to be splittable at the same point in order to successfully split.  Some objects are not splittable: e.g. rectangles, images.  If they are side-by side with a splittable object, the form will not split. 

Multi-line fields are splittable by default.  However, for side-by-side multi-line fields to split, we need to find a common baseline.  i.e. we cannot split in the middle of a line of text.  We either split above or below a line.  Side-by-side text fields need to have lines that end at the same point in order to be splittable.  There are a large number of properties that can cause lines to be offset from one another — including: top margin, paragraph space before/space after, line spacing, vertical justification, typeface, font size, vertical scale. For example, if two side-by-side fields have top margins that differ by 3mm, then they will not share any common baselines — and we will not find a split point.  We are working on changes to the split point calculation that will be more tolerant of baseline mismatches.  But until then, you need to be very careful to make the properties of your fields exactly the same.

Blank Pages in Output

Once you have a form where content is not splittable and is too big for a page, you will often see blank pages in your output.  The reason for this is because of the way the layout algorithm fills pages.  It works something like this:

  1. Attempt to place an object on a page
  2. If the object is too large for the remaining space on a page, attempt to split the object to fit on the page
  3. If the object does not have a split point in the available space, start a new page
  4. If the object does not fit on the new page, attempt to split it.  If it does not have a split point in the available space, start another new page.
  5. If the object still does not fit, then place the object on a page and truncate the content

Newer versions of the XFA processor have managed to reduce the number of blank pages we see.  In a sense, I regret that we "fixed" those conditions because they are usually a warning sign that there is a layout problem that needs to be fixed.

Sample

A common use for side-by-side growable fields is to show text side-by-side with a translation.  The attached sample shows the preamble to the Declaration of Human Rights in French and English.  It shows them a second time with the fields offset slightly so that they couldn’t split.

Debugger Support

The debugger has been updated to include a new warning for splittable table rows that have more than one multi-line field.  Eventually the same warning should appear for positioned content.

BTW if you’re using the debugger, you’ll want to monitor the post where it was introduced for occasional updates.

Collected Form Development and Debugging Tips

In this post I have consolidated a bunch of tools and tips for developing and debugging XFA/PDF forms.

Use the console Object Effectively

Always show the console on errors

When your form encounters a JavaScript error, the error text shows up in the console.  But by default the console doesn’t appear — which means you could be getting errors without being aware of them.  To see errors as soon as they happen, configure Acrobat to pop up the console as soon as a message appears.  Under Edit/Preferences, set the options highlighted below…

consoleDialog

 

The console methods

console.println() is an indispensable tool for tracing what is happening in your form script logic.  It is much more effective than xfa.host.messageBox() or app.alert(), since it doesn’t require dismissing a dialog with every message.

Did you know there are other methods on the console object?  clear(), hide(), show().  You might want to place a call to console.clear() when your form initializes.  When it’s time to deploy your form, make sure you haven’t left any extraneous messages in the console.

The Server Alternative

Since the console object is an acrobat-specific tool, you need to use something different to emit trace message for server-based XFA forms.  For the server, use xfa.log.message() to write your message to the server log file.

Trace Function Calls

As long as you’re in the mode of dumping information to the console or log file, you may as well make it as easy as possible.  This blog entry describes a method for tracing the input parameters to JavaScript functions.

Have a look at the data DOM

It’s often useful to see what the data currently looks like.  One way to get there is to add a big text field that displays the data dom.  Give it this one line calculation: xfa.datasets.saveXML("pretty");  Set the field as bind="none".  Now you will have an up-to-date snapshot of the state of the data dom.

Re-factor your form

Design for re-usability.  Develop common patterns that are shared between forms.  Create script frameworks inside script objects.  Put the framework script objects in your fragment library.

Use the Merge/Layout Debugger

If your problems are in merge or layout, then try this.  (Try it even if you’re not having problems with merge/layout — it’s cool.)

Exception Handling

When an exception is thrown and displayed in the console (or in the server log file) the resulting message can be frustratingly cryptic. To get higher quality information (including a stack trace), catch errors and parse them into human-readable text.  There is sample code here to get you started.

String Manipulations

If you are manipulating lots of strings — especially if you’re parsing them, get to know the power of regular expression processing in JavaScript.  Have a look at this regular expression testing tool.

Be Version Aware

If you make extensive use of script objects or if you’ve dabbled in using dynamic properties on XFA objects then get educated about the effects of strict scoping and how it behaves in different versions of XFA and Reader.  Read lots more here and here.

Summarize your form

This post shows how to get a "big picture" summary of the form meta data and the content in the form.

Document your Script

Take the time to properly document your JavaScript methods.  Use JavaDoc conventions. Six months from now when you (or someone else) needs to modify the form, you’ll be glad you took the time to explain your form logic.  To generate a report of all the script in the form, look here.

Reduce and Isolate your problem

If you are struggling with a particular problem, try to reproduce the problem in a new, simplified form.  The act of isolating will often lead to discovering any outside influences are affecting your form behaviour.  If you’re still having the problem in your new simple form, then you have a simplified version of the problem that makes it easier to share with others when you ask for help.

Designer Script Editor

Turn on Line Numbers

In your designer script window, turn on line numbers by right clicking on the script window and selecting "Show line numbers" from the context menu.

Don’t Leave Blank Lines at the Top of your Script

Blank lines at the beginning of a script get trimmed before the JavaScript interpreter processes the code.  If there are blank lines, then any line numbers reported by exceptions won’t agree with the line numbers you see in Designer.

Shortcut Keys

<control>+f — bring up the find dialog. Any highlighted text is automatically used as the find string.  <control>+h — brings up the find/replace dialog.  F3 — find next.  <shift>+F3 — find previous.

Multiple monitors

Convince your boss you need two monitors.  Keep Designer in one monitor and place your script editor and Acrobat console in the second monitor.

"foo.bar is not a function"

Your script makes a call to foo.bar() and you get the mysterious message: "foo.bar is not a function".  But you look in your script object and it sure looks like it is defined.  The problem is that you have a syntax error somewhere in your foo script object.  This prevents the runtime from discovering the foo.bar() function.  When you see this message, run Designer’s script syntax checker.

Declare your Variables

Don’t code: for (i=0, i<myList.length; i++)
Instead, code: for (var i=0, i<myList.length; i++)
The JavaScript interpreters process explicitly declared variables more efficiently.

While you’re at it, use variable names that are unlikely to collide with names of objects on your forms. Such as prefixing variables with a "v" or "n" or "s".  (vObject, nOffset, sStringValue).

Beware of Ambiguous Expressions

If you’re writing code that is intended to be re-usable in multiple forms, beware of potential conflicts between property names and form objects.  For example, the expression subform.border.fill.color becomes ambiguous if this script in a context where the subform has a field child named &quot
;border".  To avoid ambiguity, use the expression: subform["#border"].fill.color.  The hash symbol specifies that what follows is a className instead of a container name.

Find the Root Subform

If you have re-usable script that needs to locate the root subform, you can use this expression:

xfa.form.resolveNode("#subform")

Or the slightly more terse:

xfa.form["#subform"]

Don’t Modify Your Form in XML Source Mode

If you need to change a property that Designer doesn’t expose in one of it’s property editors, then change the property via script.  For example: Designer doesn’t support making a positioned subform repeatable.  Rather than adding an <occur> element in XML source, set this property in script:

Subform1.occur.max = "2";
var vNewSubform = _Subform1.addInstance();

Batch Changes to your Form

For advanced users — if you need to make global changes to the contents of your form, this blog entry shows how a form author can write JavaScript to modify their template in Designer.  Likely a bit easier than writing XSLT.

Use FormCalc Functions — from JavaScript

FormCalc has some built-in functions that give access to some powerful functionality.  There is a way to bridge between JavaScript and FormCalc in order to use these functions.  Look here.

June 16, 2009 update

Use Strict JavaScript

Run lint against your JavaScript.  Choose options that make it as strict as possible.  Details here.