Search This Blog

Tuesday, 5 July 2016

Escaping the “&” character in a submission email


Within a mailto URL, as used by the EmailSubmitButton, the character "&" has a special meaning as a separator between the headers (to, cc, subject, etc).  In an HTML page a "&" character within the header value can be escaped as %26, but in the mysterious world of LiveCycle Designer forms the “%” needs to be escaped as well, giving us %2526.

 For example;

mailto:?subject=Q%2526A

Generates an email with the subject of “Q&A”.

This sample form uses the following code in the "Submit by Email" button to generate the mailto URL and uses two calls the encodeURIComponent generate the correct escaping for the "&" character .

var encodedAmpersand = encodeURIComponent(encodeURIComponent("&"));
var headers = [];    

if (!To.isNull) headers.push("to=" + To.rawValue);
if (!CC.isNull) headers.push("cc=" + CC.rawValue);
if (!BCC.isNull) headers.push("bcc=" + BCC.rawValue);
if (!Subject.isNull) headers.push("subject=" + Subject.rawValue.replace(/&/g, encodedAmpersand));

if (!Body.isNull) headers.push("body=" + Body.rawValue.replace(/&/g, encodedAmpersand));
        
var mailtoTarget = "mailto:?" + headers.join("&");
 
EmailSubmitButton.event__click.submit.target = mailtoTarget;
EmailSubmitButton.execEvent("click");

Note: This only applies to submission emails using an Email Submit Button.  If you are using app,mailMsg() or a mailto URL in a rich text hyperlink then you just use %26.

The sample form, mailto&.pdf, just tests the above code
Event propagation

The sample form shows the XML of the Email Submit Button as you type into the form fields for To, CC, BCC, Subject and Body.  This was done using event propagation, so the sample will only work in Reader 9.1 or later, though the technic of escaping the “&” will work in earlier versions.  The trick here was to have the change event on a subform.  A subform does not support the change event so Designer does not allow you to enter the code.  However, it does still seem to work you just need to enter the code in the XML Source view.

Thursday, 30 April 2015

Using the Jasmine testing framework in an Adobe Designer LiveCycle Form

Jasmine is a popular testing framework for JavaScript applications.  There's lots of tutorials on using Jasmine so I wont go into a lot of detail but as an example if we had a form that has a item price field, a quantity field and a total field with Jasmine we can write a test something like;

 describe("Total Calculation: ", function()
 {
  beforeEach(function ()
  {
   TotalPriceCalculation.ItemPrice.rawValue = 1.75;
   TotalPriceCalculation.Quantity = 5000;
   TotalPriceCalculation.Total.execCalculate();
  });

  it("Equals ItemPrice * Quantity", function()
  {
   expect(TotalPriceCalculation.Total.rawValue).toEqual(8750);
  });
  it("Formatted in Australian Dollars ", function()
  {
   expect(TotalPriceCalculation.Total.formattedValue).toEqual("$8,750.00");
  });
 });

This is JavaScript code, describe, is a Jasmine function that takes a description and function as arguments. beforeEach is another function that is executed before every it  function which performs the actual test.  The above test will hopefully give us a result like;


The toEqual function after the expect is one of the standard Jasmine matches, the complete list is;

toBe( null | true | false )
toEqual( value )
toMatch( regex | string )
toBeDefined()
toBeUndefined()
toBeNull()
toBeTruthy()
toBeFalsy()
toContain( string )
toBeLessThan( number )
toBeGreaterThan( number )
toBeNaN()
toBeCloseTo( number, precision )


And the Jasmine framework allows us to add our own matches, so for the XFA environment I have added;

toBeVisible()
toBeInvisible()
toBeHidden()
toBeMandatory()
toBeProtected()
toBeOpen()
toBeValid()
toBeXFANull()
toHaveItemCount( number )
toHaveError( string )
toHaveFontColor ( "r,g,b" )


These are just helper functions target.toBeVisible() is the same as target.presence.toEqual("visible").

Other helper functions added for the XFA environment are

 /**
  * Raises the change event for a specified field
  * @param {XFA object}  field     The target field of the change event
  * @param {string}   newText    The value of the field after the change is applied
  * @param {string}   [change=newText] The value typed or pasted into the field that raises the change event
  * @param {boolean}  [keyDown=false]  The user is using an arrow key (listbox and dropdown only)
  * @param {boolean}  [modifier=false] The user is holding the Ctrl key down
  * @param {boolean}  [shift=false]  The user is holding the shift key down
  */

function execChangeEvent(field, newText, change, keyDown, modifier, shift) {


 /**
  * Raises the exit event for a specified field
  * @param {XFA object}  field     The target field of the exit event
  * @param {various}  rawValue    The value of the field before the exit event called, undefined doesn't update the field
  * @param {string}   [commitKey=3]  How the current value of the field was set: 0 - not set, escape key pressed, 1 - mouse click, 2 - enter key, 3 - tabbing
  * @param {boolean}  [modifier=false] The user is holding the Ctrl key down
  * @param {boolean}  [shift=false]  The user is holding the shift key down
  */

function execExitEvent(field, rawValue, commitKey, modifier, shift) {


 /**
  * Reset fields to their default values
  */

function resetData(/*arguments*/) {


In this sample Jasmine.xdp is a script object which is the standalone Jasmine code download with a fairly small modification to cater for the differences in the Adobe JavaScript environment.  The changes are commented at the start of the code but relate to the app.setTimeOut() function taking a string instead of a JavaScript statement.

Hopefully this sample will give you enough to get you going JasmineSample.pdf.  All the tests in this sample pass but when you get some that fail an exception will be thrown, you may want to set "When exception is thrown" to ignore in Acrobat JavaScript options.

The XFA bootstrap is in JasmineXFA.xdp and the Jasmine test are in JasmineSpec.xdp, all zipped in JasmineXDP.zip with the XDP of the sample.

The JasmineSpec.xdp is a form fragment, the tests are run as part of a click event of the "Run Jasmine Specs" button, the results are displayed in a text object in the same fragment.  The click event must start with the statement;

var jasmine = JasmineXFA.Bootstrap({environment:this})

this, adds the Jasmine functions and the XFA helpers and ends with the statement;

jasmine.getEnv().execute(); 

Which starts the actual tests.

The sample by default generates a pass/fail log to the console log as well. If you find generating output to the console log is getting in the way of your own debug statements you can turn the Jasmine output off by passing the includeConsoleReporter:false option.

var jasmine = JasmineXFA.Bootstrap({environment:this, includeConsoleReporter:false})

I tend to develop the tests as an embedded script object which allows me to use the control-click method of referencing fields, then when ready to be released change the script to a fragment and comment it out.  The XDP file being an XML file allows XML comments <!-- -->.  So in the XML Source view will look like;

<!--subform usehref=".\JasmineSpec.xdp#som($template.#subform.JasmineSpec)"></subform-->

I am using the Jasmine framework version 2.0, the latest is 2.2 but I have not been successful in getting later version to run in the Acrobat environment, again because of the differences in the app.setTimeOut() function.  I'm not sure what I am missing but 2.0 has provided enough functionality for me so far.




Thursday, 23 April 2015

Adobe Dialog Manager (ADM) in Acrobat JavaScript: insertEntryInList, removeAllEntriesFromList and insertSeparatorEntryInList

This sample demonstrates the undocumented list methods available when using Adobe Dialog Manager (ADM) in Acrobat JavaScript, also called a custom dialog using the app.execDialog()
method.

These methods, on the JavaScript API > Dialog object, are;

insertEntryInList(object)

removeAllEntriesFromList(item_id)

insertSeparatorEntryInList(item_id)

As they are undocumented they could disappear in future releases of Adobe Reader but they are also used by Reader itself,  if you open a JavaScript debugger console and type IWDistributionServer you will see the source code of one of the JavaScript functions that use it.

This sample implements a ListPicker control, ListPicker.pdf


Once the selections are made the you can move a separator line (the blue line in the selection list) up and down.

The separator line can be added to a list with the load method by using the property name "\-" but seems to always appears at the beginning, so the code which you would expect to add a separator as the second item.

dialog.load({ "LST1" : { "abc" : -1, "\\-" : -2, "def" : -3 } })

Adds it as the first.










To achieve the effect we wanted we would need to use;

dialog.insertEntryInList({ "LST1" : { "abc" : -1 } })
dialog.insertSeparatorEntryInList("LST1");    
dialog.insertEntryInList({ "LST1" : { "def" : -3 } })

Thursday, 16 April 2015

Adobe Dialog Manager (ADM) in Acrobat JavaScript: progress_bar

This sample demonstrates the undocumented progress_bar control when using Adobe Dialog Manager (ADM) in Acrobat JavaScript, also called a custom dialog using the app.execDialog()
method.


The control can be defined with the following code;
 


{

  type: "progress_bar",

  width: 100,

  height: 22,

  item_id: "PBAR"

}



 Updates to the position of the progress bar can be made with 
 
          

dialog.load({"PBAR": (100 / this.fieldCount) * validFieldCount});



 This code sets the progress bar to a value between 0 and 100 depending on how many valid fields there are.



I started playing with the progress bar control when attempting to develop a timed dialog using  app.setInterval()  but the timer seems to be paused when a dialog is display (as it is if a menu is opened)
 


Still there are some other uses and this sample uses a progress bar to show how much of the dialog is complete and another to indicate the password strength.


Progress_Bar.pdf 
 


Thursday, 9 April 2015

Adobe LiveCycle Designer Tip #9 - The JavaScript console and string literals

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"]')

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.

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.