Extending JavaScript with Prototypes in XFA Forms

Do you sometimes wish you had functions that standard JavaScript does not provide? For instance, a trim or strip function that removes leading and trailing white spaces from a string object. In LiveCycle Designer, the traditional way is to create a script object and a function. Then to use it, you would have a function call similar to the following statement:

var newString = ScriptObject.trim(oldString);

These function calls can become lengthy in situations where the script object is located on a different page than your method call. For example:

var newString = form1.page1.subform1.ScriptObject.trim(oldString);

Having said that, the problem with this approach is that, to reuse this component, the script object name and location must stay the same for each form. If you move the location of the script object, since there is no easy way to refactor it, your references will certainly break. A more elegant solution is to use of the prototype property, you can extend the functionality of the classes in the same fashion you can do with HTML JavaScript in a web browsers.

The prototype property is probably one of the most under-utilized scripting features when it comes to LiveCycle form development. However, it is so powerful as it allows you to easily extend and reuse JavaScript intrinsic classes and your own. For instance, the trim function can be rewritten as follows:

String.prototype.trim = function() { return this.replace(/^\s+/, '').replace(/\s+$/, ''); }

And to use it:


var oldString = "    Hello   ";
var newString = oldString.trim();
// newString -> "Hello";

You may also want to have another method called "strip" that performs the same task.

String.prototype.strip = String.prototype.trim;

As you can see, your function is simpler and easy to use. Using the prototype property for custom functions also prevents your code from breaking in case you change the name or location of the script object and provides a natural way to extend objects even if you did not author them.

Based on my past projects where the requirement was to create resuable Designer library components for multiple teams, I usually wrap my script objects in a subform, make it into a library component (xfo) or a form fragment and share it with other form developers.

Also to make the JavaScript interpreter in Acrobat/Reader aware of your prototype extensions, you must load all your prototype definitions when the form loads. You can achieve this by wrapping the prototype function and property definitions inside a function and call that function in the subform initialize event. For instance:


function initialize()
{
String.prototype.trim...
String.prototype.strip...
...
}

In my sample form, once the extensions have been initialized, it will print out the below messages in the debugger console.

Initializing Array extensions...
Array extensions initialized.
Initializing String extensions...
String extensions initialized.

One last note – as you develop these prototype functions, don’t forget to include documentation comments to increase readability and ease of use. Feel free to share your experiences or ask any question you may have.

Resources