One of the little mysteries with working Acrobat JavaScript is how the JavaScript console handles string literals.
If you open Acrobat and then the JavaScript console, (using Ctrl-J) and type
'abc'.length .. we get a response of 3
and
“abc”.length .. also gives a response of 3
But if we are using the JavaScript console when performing a PDF Preview from LiveCycle Designer then
'abc'.length .. still gives us 3
but
“abc”.length .. returns undefined
Even worse
'a"b"c'.length .. also returns undefined, (that is a double quote within single quotes)
So it seems we can’t use double quotes at all, but we can you just need to escape them (even though JavaScript should not require us to), so
'a\"b\"c'.length .. returns 5
This doesn’t cause problems daily but every now and then … maybe if I’m having trouble with a SOM expression involving a predicate, say something like
Content.dataNode.resolveNode('$.Location.[Level == "Lead"]')
This is a copy of Adobe LiveCycle Designer Cookbooks I had on the Adobe Developer Connection before they stopped being available. There are also some code samples used in answering questions on the LiveCycle Designer forum.
Search This Blog
Thursday, 9 April 2015
Thursday, 2 April 2015
Windows Search and XDP files
By default the Windows Search will only pick up the file properties of XDP files. But since XDP files are really just XML files we can set the search filter handler to the XML Filter and then our search will look in the element and attribute values of the XDP file.
To do this we need to edit the Registry. If we look at the registry key for the .xml file type (which has a key of HKEY_CLASSES_ROOT\.xml) we see
To make Windows Search treat XDP files like XML all we need to do copy the PersistentHandler key with a default value of the GUID for the XML Filter which is {7E9D8D44-6926-426F-AA2B-217A819A5CCE} to the XDP class key HKEY_CLASSES_ROOT\.xdp.
Once we have done that, if you go to Index Options, select Advanced ... File Types and scroll down to XDP you will see the XML Filter is being used.
Make sure you have the "Index Properties and File Contents" selected, then go back to Index Settings tab and click the Rebuild button ... wait several hours and your searches will now include the contents of the XDP files.
To do this we need to edit the Registry. If we look at the registry key for the .xml file type (which has a key of HKEY_CLASSES_ROOT\.xml) we see
To make Windows Search treat XDP files like XML all we need to do copy the PersistentHandler key with a default value of the GUID for the XML Filter which is {7E9D8D44-6926-426F-AA2B-217A819A5CCE} to the XDP class key HKEY_CLASSES_ROOT\.xdp.
Once we have done that, if you go to Index Options, select Advanced ... File Types and scroll down to XDP you will see the XML Filter is being used.
Make sure you have the "Index Properties and File Contents" selected, then go back to Index Settings tab and click the Rebuild button ... wait several hours and your searches will now include the contents of the XDP files.
Thursday, 26 March 2015
Handling a full stop in a XML element name
The “.” (full stop) character is valid in an XML name, this means there can be cases were you need to escape a full stop in a SOM expression.
This is the default format when using SQL Server and the FOR XML AUTO option and the table has
a schema, you will end up with XML like;
<ClientMgmt>
<ClientMgmt.ClientDetails name="..." /> <ClientMgmt.ClientDetails name="..." />
…
In a SOM Expression the full stop needs to be escaped with a “\” (backslash), when we put this in JavaScript code we again need to escape the backslash, so end up with three backslash characters.
$data.resolveNodes('ClientMgmt.ClientMgmt\\\.ClientDetails[*]')
Interestingly when trying the same in the Acrobat JavaScript debugger you need to use four backslash characters.
Thursday, 19 March 2015
Going deeper with floating fields
Floating fields allow variable text to be inserted within a text object. A floating field is referenced within a rich text field using XFA extension to the xHTML <span> element, e.g.
As inserted by the LiveCycle Designer Insert ... Floating Field command you will get something like this in the XML Source
<span xfa:embedType="uri" xfa:embedMode="raw" xfa:embed="#floatingField007456"/>
The full XFA syntax for a floating field is;
<span xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/"
xfa:embedType="uri | som"
xfa:embed="<SOM expr> | <uri>"
xfa:embedMode="raw | formatted"/>
But you can see from the syntax that there are some other options available. Sometimes we might create a floating field bind it to our data connection just to display the value embedded in some text. Say the floating field was bound to $data.formData.firstName then we could change our span element to reference the data connection directly, in which case we don't need the floating field at all.
Another situation you may need to use a SOM expression in an embedded field is when you have multiple levels of repeating data, such as a list of countries within which we have a list of cities
If I wanted to use a floating field to display the country element within the repeating city element, there is no way Designer can make the reference. Designer will give you a binding expression like $record.counties.country, which will always return you the first country. In this case we can again change the embedType to "som" but use a form object SOM expression, something like;
<span xfa:embedType="som" xfa:embedMode="raw" xfa:embed="countries.country"/>
This will resolve to the form object with the SOM expression countries.country, which should be the right country for the city we are processing.
As inserted by the LiveCycle Designer Insert ... Floating Field command you will get something like this in the XML Source
<span xfa:embedType="uri" xfa:embedMode="raw" xfa:embed="#floatingField007456"/>
The full XFA syntax for a floating field is;
<span xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/"
xfa:embedType="uri | som"
xfa:embed="<SOM expr> | <uri>"
xfa:embedMode="raw | formatted"/>
embedType
When the embedType has a value of "uri" then the embed value will be the id of the floating field, prefixed with a "#" symbol.But you can see from the syntax that there are some other options available. Sometimes we might create a floating field bind it to our data connection just to display the value embedded in some text. Say the floating field was bound to $data.formData.firstName then we could change our span element to reference the data connection directly, in which case we don't need the floating field at all.
Another situation you may need to use a SOM expression in an embedded field is when you have multiple levels of repeating data, such as a list of countries within which we have a list of cities
If I wanted to use a floating field to display the country element within the repeating city element, there is no way Designer can make the reference. Designer will give you a binding expression like $record.counties.country, which will always return you the first country. In this case we can again change the embedType to "som" but use a form object SOM expression, something like;
<span xfa:embedType="som" xfa:embedMode="raw" xfa:embed="countries.country"/>
This will resolve to the form object with the SOM expression countries.country, which should be the right country for the city we are processing.
embedMode
By default the embedMode is set to "raw", which means only the floating fields value is inserted, all font formatting is taken from the surrounding text. A value of formatted for embedMode means the font attributes of the inserted text are taken from the inserted field, which can be useful if we need to dynamically update them as in this sample, FloatingFields.pdf.Thursday, 12 March 2015
Adobe LiveCycle Designer Tip #8 - Bringing colour to your workspace (and other settings)
I recently received an upgraded computer, which meant I had to re-install LiveCycle Designer ... and find all my settings again.
Increase the number of files in the Recently Used File List to 10. This seems to be the maximum, although there is no error when a higher number is entered the value will not be saved.
Set the Create Backup Copy on Save, this will create a _BAK.pdf file (or a _BAK.xdp file) which gives you a chance to recover your work if Designer crashes on you. The disadvantage of setting this option is when you edit a fragment the _BAK file will be created and the fragment will be duplicated when viewed in the fragment library. Another way you can recover your work if Designer crashes to look for a PDF file in your Windows %temp% directory with some random looking name _1f7o26cap4d25e8q1t.pdf this is the file that is created whenever you perform a PDF Preview.
Under JavaScript Syntax Formatting ... select a custom color for strings and numbers, I use an orange color. I used to have a boss who set his to red, he never wanted to see any red. I'm not that strict so set mine to an orange. The idea is to make you think if these values would be better off in a form variable or a dataset.
Select the Show Line Numbers checkbox, any runtime errors will refer to a line number so showing line numbers makes it easier to find the right line. Just make sure there are no blank lines at the start of your script as these aren't counted when line numbers are displayed in error messages.
Set the Show Dynamic Properties checkbox, this will enable the option to bind values in a drop down to the data connection
Set the Default Binding for New Subforms to "No Data Binding"
Deselect the Snap to Grid option to allow finer control over the form objects, typically I use flowed subforms but even with a positional subform it is usually easier to align with another object, under the Layout menu or learn the keyboard shortcuts, Ctrl-LeftArrow to align a group of fields on there left boundary, etc.
Add Ctrl-Shift-w for Warp in Subform and Ctrl-Shift-u for Unwarp Subform. The default keyboard shortcuts are listed in Using Designer ES4 / Working with the Keyboard / Default keyboard shortcuts
Tools ... Options ... Document Handling
Increase the number of files in the Recently Used File List to 10. This seems to be the maximum, although there is no error when a higher number is entered the value will not be saved.
Set the Create Backup Copy on Save, this will create a _BAK.pdf file (or a _BAK.xdp file) which gives you a chance to recover your work if Designer crashes on you. The disadvantage of setting this option is when you edit a fragment the _BAK file will be created and the fragment will be duplicated when viewed in the fragment library. Another way you can recover your work if Designer crashes to look for a PDF file in your Windows %temp% directory with some random looking name _1f7o26cap4d25e8q1t.pdf this is the file that is created whenever you perform a PDF Preview.
Tools ... Options ... Workspace
Under JavaScript Syntax Formatting ... select a custom color for strings and numbers, I use an orange color. I used to have a boss who set his to red, he never wanted to see any red. I'm not that strict so set mine to an orange. The idea is to make you think if these values would be better off in a form variable or a dataset.
Select the Show Line Numbers checkbox, any runtime errors will refer to a line number so showing line numbers makes it easier to find the right line. Just make sure there are no blank lines at the start of your script as these aren't counted when line numbers are displayed in error messages.
Tools ... Options ... Data Binding
Set the Show Dynamic Properties checkbox, this will enable the option to bind values in a drop down to the data connection
Set the Default Binding for New Subforms to "No Data Binding"
Window ... Drawing Aids
Deselect the Snap to Grid option to allow finer control over the form objects, typically I use flowed subforms but even with a positional subform it is usually easier to align with another object, under the Layout menu or learn the keyboard shortcuts, Ctrl-LeftArrow to align a group of fields on there left boundary, etc.
Tools ... Keyboard Shoutcuts
Add Ctrl-Shift-w for Warp in Subform and Ctrl-Shift-u for Unwarp Subform. The default keyboard shortcuts are listed in Using Designer ES4 / Working with the Keyboard / Default keyboard shortcuts
Thursday, 5 March 2015
SOM Expressions with Relative Indexes
Most SOM expressions I work with have an absolute index number, something like form1[0].[0].Table1[0].Row1[0].TextField1[0] where all the numbers within the square brackets are absolute numbers. But these numbers can be relative, that is have a positive or negative sign. So if I was on TextField1 of row 2 and wanted to reference TextField1 of row 1 I could use a SOM expression like;
Row1.resolveNode('Row1[-1].TextField1');
Similarly if I have a series of fields with the same name and so have different indexes, say TextField[0], TextFIeld[1] etc, I can reference the next field, in document order, called TextField with a SOM expression
this.resolveNode('TextField[+1]').rawValue
We can also refer to the next field without knowing it's name using the class name reference (that is with a "#" character).
this.resolveNode('#field[+1]').somExpression
or the next button
this.resolveNodes('#field.(ui.oneOfChild.className == "button")').item(1).somExpression
This sample gives examples of these expressions and uses a relative SOM expression to calculate a running total in a table.
RelativeIndex.pdf
Row1.resolveNode('Row1[-1].TextField1');
Similarly if I have a series of fields with the same name and so have different indexes, say TextField[0], TextFIeld[1] etc, I can reference the next field, in document order, called TextField with a SOM expression
this.resolveNode('TextField[+1]').rawValue
We can also refer to the next field without knowing it's name using the class name reference (that is with a "#" character).
this.resolveNode('#field[+1]').somExpression
or the next button
this.resolveNodes('#field.(ui.oneOfChild.className == "button")').item(1).somExpression
This sample gives examples of these expressions and uses a relative SOM expression to calculate a running total in a table.
RelativeIndex.pdf
Saturday, 14 February 2015
Custom Properties
The XFA specification defines customs properties that can be store in a field, exclGroups (or radio buttons) or subforms under the <desc> and <extras> elements. These allow us to write scripts that process a form but allow the individual fields and subforms to control that processing. The example used here is a script that sets all the fields to their default value, except if an allowResetData property is set to false. Other scenarios might be setting all fields to read only, except some, or some have additional processing, validation frameworks, setting min/max values, etc.
There doesn’t seem to be much difference between using the <desc> or <extras> elements but I tend to use <desc> as the names and values are shown in the Info palette.
<field name="TextField1" w="62mm" h="9mm">
I haven’t seen any limits documented, these are the values I have found by playing around. Also remember for decimal and float values that exceed the maximum integer value then the precision starts to fail like all usual float values, e.g.
9007199254740992 + 1 = 9007199254740992
9007199254740994 + 2 = 9007199254740994
9007199254740992 + 3 = 9007199254740996
More about float and decimal values here, http://en.wikipedia.org/wiki/Double-precision_floating-point_format
A value under a <desc> element value can be referenced directly in JavaScript using TextField1.desc.allowResetData.value but if the property does not exist on the form object you will get a “Invalid property get operation; desc doesn't have property 'allowResetData'” exception. To check if a property exists use the namedItem() method, TextField1.desc.nodes.namedItem("allowResetData"), this will return null if the property does not exist or an object with a value property if it does.
So now the JavaScript function to process the form (or part of the form) could look like this;
All subforms can have a script object and this resetData function looks for a script object called “Script” that contains a JavaScript function called “resetData” and if found executes it , the code under the nextNode.className === “variables”.
This allows parts of the form to perform any custom operations required when a form is reset. This example (CustomProperties.pdf) uses this function to remove a file that has been attached to the form.
I use two macros for setting and clearing allowResetData flag. Once installed you will be able to select the appropriate form objects and run a macro instead of editing the XML Source. To run select Tools … Macros … “Set allowResetData Flag to False” or Tools … Macros … “Clear allowResetData Flag”. The macros and macro.xml configuration file are in the zip file (CustomProperties.Macros.zip).
If you haven’t written or installed a macro before then refer to the help page. Designer 10 - Macros
The <exData> element can be used to store rich text and be used to populate a Text control like;
Text1.value.exData.loadXML(TextField1.desc.ExData.saveXML(), true, true);
Likewise the <image> custom property can be used to store image and used to populate an image control like; Image1.value.image.value = TextField1.desc.Image.value;
Form variables created using Form Properties … Variables are stored under a <variables> element, when created by LiveCycle Designer they are always text, but it is valid for them to be any of the types that can be used under the <desc> element. This allows references to the variables in JavaScript to be typed, so by editing the XML Source can make your JavaScript code simpler, at least when dealing with Number and Boolean form variables. Once you have made this change Designer will show them as a “?” icon in the hierarchy palette (see image below) but this has never caused any problem in my forms.
And looks like this in the XML Source window.
<variables>
<text name="Text"/>
<integer name="Integer"/>
</variables>
There doesn’t seem to be much difference between using the <desc> or <extras> elements but I tend to use <desc> as the names and values are shown in the Info palette.
The <desc> element is also were the <xs:annotation> information is stored is you are using a XML Schema for your data connection.
The XFA for a field with this custom property would then look like;
<desc>
<boolean name="allowResetData">1</boolean>
</desc>
<ui>
<textEdit/>
</ui>
...
</field>
There is no support for updating these values in LiveCycle Designer but I will include two macros that can be used to update the allowResetData property and hopefully can be used as a guide for updating your own properties … or you could just edit the XML Source window.
The data stored under a <desc> element can be typed, so JavaScript references are returned in properties with the appropriate data type. The exception is the date/time related elements that are returned as strings.
The data stored under a <desc> element can be typed, so JavaScript references are returned in properties with the appropriate data type. The exception is the date/time related elements that are returned as strings.
desc element
|
JavaScript
Data Type |
Value Range
|
|---|---|---|
| boolean | Boolean | 0 – false, 1 – true |
| date | String | |
| dateTime |
String | |
| decimal | Number | For integers; 9007199254740992 to -9007199254740992; (Same as JavaScript, that is 253) For floats 1.79E+308 to 1E-15 (max 15 decimal digits if specified by the fracDigits attribute e.g. <decimal name="Decimal" fracDigits="15"/>, Otherwise defaults to 2 decimal places |
| exData | String | |
| float | Number | 9007199254740992 to -9007199254740992; (Same as JavaScript) For floats 1.79E+308 to 1E-08 (max 8 decimal digits) |
| image | String | Prints the number 2 in a width of 5 characters with "0" characters padding |
| integer | Number | 2147483647 to -2147483647 Attempting to assign a number outside this range raises an "Operation failed." GeneralError exception |
| text | String | |
| time | String |
I haven’t seen any limits documented, these are the values I have found by playing around. Also remember for decimal and float values that exceed the maximum integer value then the precision starts to fail like all usual float values, e.g.
9007199254740992 + 1 = 9007199254740992
9007199254740994 + 2 = 9007199254740994
9007199254740992 + 3 = 9007199254740996
More about float and decimal values here, http://en.wikipedia.org/wiki/Double-precision_floating-point_format
A value under a <desc> element value can be referenced directly in JavaScript using TextField1.desc.allowResetData.value but if the property does not exist on the form object you will get a “Invalid property get operation; desc doesn't have property 'allowResetData'” exception. To check if a property exists use the namedItem() method, TextField1.desc.nodes.namedItem("allowResetData"), this will return null if the property does not exist or an object with a value property if it does.
So now the JavaScript function to process the form (or part of the form) could look like this;
function resetData(node)
{
function pushResetDataList(node)
{
var allowResetData = node.desc.nodes.namedItem("allowResetData");
// default is too allow reset
if (allowResetData === null || allowResetData.value)
{
resetDataList.push(node.somExpression);
}
}
function resetDataInner(node)
{
if (node.className === "exclGroup" && !node.isNull) // don't reset fields that are null
{
pushResetDataList(node);
}
else
{
if (node.className === "field")
{
if (node.ui.oneOfChild.className !== "button" && !node.isNull) // buttons always null
{
pushResetDataList(node);
}
}
else
{
for (var i = 0; i < node.nodes.length; i++)
{
var nextNode = node.nodes.item(i);
if (nextNode.className === "instanceManager")
{
if (nextNode.count.toString() !== nextNode.occur.min)
{
nextNode.setInstances(nextNode.occur.min);
}
}
else
{
if (nextNode.className === "variables")
{
var scriptObject = nextNode.resolveNode("Script");
if (scriptObject && scriptObject.hasOwnProperty("resetData"))
{
scriptObject.resetData();
}
}
else
{
if (nextNode.isContainer && nextNode.className !== "draw")
{
resetDataInner(nextNode);
}
} }
}
}
}
}
var resetDataList = [];
resetDataInner(node);
if (resetDataList.length > 0)
{
xfa.host.resetData(resetDataList.join(","));
}
}
{
function pushResetDataList(node)
{
var allowResetData = node.desc.nodes.namedItem("allowResetData");
// default is too allow reset
if (allowResetData === null || allowResetData.value)
{
resetDataList.push(node.somExpression);
}
}
function resetDataInner(node)
{
if (node.className === "exclGroup" && !node.isNull) // don't reset fields that are null
{
pushResetDataList(node);
}
else
{
if (node.className === "field")
{
if (node.ui.oneOfChild.className !== "button" && !node.isNull) // buttons always null
{
pushResetDataList(node);
}
}
else
{
for (var i = 0; i < node.nodes.length; i++)
{
var nextNode = node.nodes.item(i);
if (nextNode.className === "instanceManager")
{
if (nextNode.count.toString() !== nextNode.occur.min)
{
nextNode.setInstances(nextNode.occur.min);
}
}
else
{
if (nextNode.className === "variables")
{
var scriptObject = nextNode.resolveNode("Script");
if (scriptObject && scriptObject.hasOwnProperty("resetData"))
{
scriptObject.resetData();
}
}
else
{
if (nextNode.isContainer && nextNode.className !== "draw")
{
resetDataInner(nextNode);
}
} }
}
}
}
}
var resetDataList = [];
resetDataInner(node);
if (resetDataList.length > 0)
{
xfa.host.resetData(resetDataList.join(","));
}
}
All subforms can have a script object and this resetData function looks for a script object called “Script” that contains a JavaScript function called “resetData” and if found executes it , the code under the nextNode.className === “variables”.
var scriptObject = nextNode.resolveNode("Script");
if (scriptObject && scriptObject.hasOwnProperty("resetData"))
{
scriptObject.resetData();
}
if (scriptObject && scriptObject.hasOwnProperty("resetData"))
{
scriptObject.resetData();
}
This allows parts of the form to perform any custom operations required when a form is reset. This example (CustomProperties.pdf) uses this function to remove a file that has been attached to the form.
I use two macros for setting and clearing allowResetData flag. Once installed you will be able to select the appropriate form objects and run a macro instead of editing the XML Source. To run select Tools … Macros … “Set allowResetData Flag to False” or Tools … Macros … “Clear allowResetData Flag”. The macros and macro.xml configuration file are in the zip file (CustomProperties.Macros.zip).
If you haven’t written or installed a macro before then refer to the help page. Designer 10 - Macros
The <exData> element can be used to store rich text and be used to populate a Text control like;
Text1.value.exData.loadXML(TextField1.desc.ExData.saveXML(), true, true);
Likewise the <image> custom property can be used to store image and used to populate an image control like; Image1.value.image.value = TextField1.desc.Image.value;
Form variables created using Form Properties … Variables are stored under a <variables> element, when created by LiveCycle Designer they are always text, but it is valid for them to be any of the types that can be used under the <desc> element. This allows references to the variables in JavaScript to be typed, so by editing the XML Source can make your JavaScript code simpler, at least when dealing with Number and Boolean form variables. Once you have made this change Designer will show them as a “?” icon in the hierarchy palette (see image below) but this has never caused any problem in my forms.
And looks like this in the XML Source window.
<variables>
<text name="Text"/>
<integer name="Integer"/>
</variables>
Subscribe to:
Posts (Atom)





