InfoPath Forms with Master Page Applied or Script Enabled

InfoPath 2013Say what you will about InfoPath. It’s a dead technology, it’s not getting any Microsoft love, it’s only for the wealthy (Forms Services is only available only in Enterprise -level CALs of SharePoint), it has no future. I’ve heard all of these and more. That said, it’s a pretty solid technology and works. We can bemoan it’s future, but we can also use it today to Get Good Stuff Done.

All that said, InfoPath browser-based forms don’t do everything one might need. I’ve written in the past about the way my friend Marcel Meth (@marcelmeth) and I worked together to use jQueryUI’s autocomplete function to augment InfoPath’s functionality in my post Using SPServices with jQueryUI’s Autocomplete Function on InfoPath Forms in SharePoint.

I was trying to do this again in an Office365 tenant recently and ran into a roadblock. A similar method worked just fine when filling out a new form, but we also needed it to work with existing form data when we edited it.

By default, InfoPath browser-based forms open up in a “chrome-less” state. By that, I mean that they don’t use the master page so one doesn’t see any of the site branding or tools around the form on the screen. From a branding perspective, that can be annoying but tolerable. However, when one needs to add some additional script to the page, one needs that chrome. This should be something simple to implement, but lo, it is not.

I had an InfoPath form that worked great. One of the things I needed to work in it is a little bit of script which is similar to the one in my blog post above. In this case, the basic idea was to add some autocomplete functionality to a field on the form, choosing (in this case) from a list containing 19000 cities and towns. (Cities seems to be a pretty common use case; there are a lot of them and autocomplete works well.)

To implement the script in the form, I created a new aspx page and dropped an InfoPath Form Web Part on it to display the form. The InfoPath Form Web Part pointed to my InfoPath form and the appropriate Content Type and worked perfectly. I embedded a reference to the script I needed in a Content Editor Web Part (CEWP) and everything worked a charm. By adding a few well-placed links to that page on the site, it was easy to get there to create a new InfoPath item.

The problem came in when we wanted to edit an existing form. Because links to the form go to

, I couldn’t get my script to run in the page. As I mentioned, the FormServer.aspx page doesn’t pick up the chrome or anything else from the master page, which was where I wanted to put the script references.

I found a great suggestion on StackOverflow to simply run some script in the master page which changes the destination of links to the FormServer.aspx page. The posts didn’t give all of the details on how to do it, but I whipped something up pretty quickly.

//Rewrite Form Links to Open in Custom Page
$("[href*='/sites/Projects/Sales Orders']").each(function() {
  var formFileName = $(this).attr("href");
  var formServerUrl = "/sites/Projects/SitePages/Manage%20Sales%20Orders.aspx";
  $(this).bind("click", function(e) {
    var destination = formServerUrl + "?XmlLocation=" + formFileName + "&Source=" + location.href;
    location.href = destination;

The script worked, in that it rewired all of the existing links to the FormsServer.aspx page correctly, sending me to a link that looked something like this, which was basically what I wanted:

Unfortunately, the InfoPath Web Part then showed the error below.

Error Loading InfoPath Form

There has been an error while loading the form.
Click Try again to attempt to load the form again. If this error persists, contact the support team for the Web site.

Click Close to exit this message.
Hide error details
XmlLocation and XsnLocation have both been set to non-empty values. It is an error to set both to non-empty values. Set XsnLocation to open a new copy of the form template. Set XmlLocation to open the xml file corresponding to an existing InfoPath document.

There had to be some way to pass the XmlLocation to the InfoPath Web Part successfully.

The trick turned out to be pretty simple, but I don’t know if I ever would have gotten to it. Luckily I have friends in far away places. John Liu (@johnnliu) in Sydney, Australia saw my #SPHelp on Twitter which pointed to my query on the MSDN Forums. (John’s a newly awarded MVP, so congratulations to him! Well deserved.)

1-13-2014 10-39-33 AMJohn’s suggestion seemed cryptic at first. (Hey, he was limited to 140 characters on Twitter.) I was passing in the XmlLocation, so why wasn’t it working?

After some thought (OK, a lot of thought), I decided to try something which seemed crazy. I created a new page and dropped an InfoPath Form Web Part on it, but I didn’t configure it at all. It looked like this on the page:

Unconfigured InfoPath Form Web PartThis seemed to be asking for trouble, right? Without configuring the Web Part, how would it work?

But I tried my URL with this page, and voila, it worked!

The issue with the first page was that it was configured to point to the XSN file for the InfoPath form. By omitting that configuration, the InfoPath Form Web Part was open to discussion about what to load.

As far as I know, this isn’t documented *anywhere* on the InterWebz. At least I couldn’t find it with my Bingling skillz.

Now I have two pages: New Sales Order.aspx which has the InfoPath Form Web Part configured as usual, and Edit Sales Order.aspx with the InfoPath Form Web Part not configured. Going to New Sales Order.aspx with no XsnLocation or XmlLocation on the query string loads the form to create a new item. Going to Edit Sales Order.aspx with the XmlLocation on the Query string opens an existing form for editing.

MetaVis SharePoint MVP Webinars Series – Single-Page Applications (SPAs) in SharePoint Using SPServices

Thanks to all who attended the webinar that Dave Coleman (@davecoleman146) and I did today about Single-Page Applications (SPAs) in SharePoint Using SPServices. The webinar was based on my series here of the same name, so if you’re interested in more detail on how I go about building these things, give that a gander. Thanks to Dave for playing the master of ceremonies role as well as typing in English football player names during the demo.

MetaVisMetaVis is great to sponsor the SharePoint MVP Webinars Series, so a big shout out to them as well.

I’ve ZIPped up the most relevant files from the demo and you can download them here. Several alert viewers pointed out some inconsistencies in the code I showed in the demo, so I’ve done some cleanup. Also note that I’ve included a working version of SPServices named jquery.SPServices-2013.02b. In building up the demo, I found a few more lingering bugs in 2013.02a (for which I am sorely ashamed and will do penance), so this working copy contains fixes for those issues. I will release it shortly, but please do not consider it a released version; it is for demo purposes only.

I’ve also saved the subsite from Office365 into a WSP, which you can also download. I used a vanilla Team Site, so hopefully it can be instantiated relatively easily, though I’ve had some issues with feature mismatch between Office365 and on premises installations in the past.

If you’d like to see the slides, they are posted on SlideShare, embedded below, as is a link to the recording.

Again, thanks to all of you who attended and feel free to post any outstanding questions here and I’ll try to get back to you in a timely manner.


jQuery Library for SharePoint Web Services (SPServices) 2013.02 Re-Released

Back on December 28, 2013, I released SPServices 2013.02, just squeaking in under the wire to be able to name it thusly.

Soon after the release, Paul Tavares (@paul_tavares) and I noticed a few significant issues. These issues are fixed in 2013.02a. If you downloaded 2013.02, please replace it with 2013.02a.

For the record, the issues were:

  • In SharePoint 2010, the current site context was not calculated correctly
  • GetListItemChanges and GetListItemChangesWithToken failed as they had in previous versions
  • In SPDisplayRelatedInfo, if the relatedListColumn was not specified in relatedColumns, the function would fail with an error. This issue was probably present in previous versions, but as far as I know had not been reported.

Sorry for the inconvenience, and thanks to Paul (yet again!) for his help and support.

The link to SPServices 2013.02a on cdnjs is coming. Thanks to Josh McCarty (@joshmcrty) for his continuing help with git, which for some reason remains unfathomable to me.

Single-Page Applications (SPAs) in SharePoint Using SPServices – Part 4 – GetListItemChangesSinceToken

As I mentioned in the last part of the series, when we build a Single Page Application (SPA), we’ll usually want to keep the data we’re displaying up to date. GetListItems and GetListItemChanges are two of the operations that can help with this, but the more robust option is GetListItemChangesSinceToken (MSDN documentation). In fact, it’s becoming my favorite list-data-getting operation, even over the rock solid GetListItems.

GetListItems is great for simply grabbing items from a list, but if you want to monitor the list for changes efficiently, you’ll need GetListItemChanges and/or GetListItemChangesSinceToken. Of the two, GetListItemChangesSinceToken is the far more powerful. In fact, once you start using it, you may well stop using GetListItems altogether.

[important]GetListItemChanges and GetListItemChangesSinceToken do not work in versions of SPServices before 2013.02.[/important]

(I wish I had gotten these two operations working long ago, now that I realize how powerful they are.)

In this post, let’s take a look at the GetListItemChangesSinceToken operation.


GetListItemChangesSinceToken is clearly related to its siblings, GetListItems and GetListItemChanges, with a cross between the two of their characteristics, plus some extra goodness. We can be very specific about what we request, as with GetListItems, but we also can decide to only receive changes since a specific database token (more on this below).


See the GetListItems post in this series.


See the GetListItems post in this series.


See the GetListItems post in this series.


See the GetListItems post in this series.


See the GetListItems post in this series.


See the GetListItems post in this series.


See the GetListItems post in this series. Note that the MSDN documentation for GetListItemChangesSinceToken provides far more parameters than the MSDN documentation for GetListItems. Most (if not all – I haven’t tested everything) of these parameters work with GetListItems as well.


A string that contains the change token for the request. For a description of the format that is used in this string, see Overview: Change Tokens, Object Types, and Change Types. If null is passed, all items in the list are returned.


A Contains element that defines custom filtering for the query and that can be assigned to a System.Xml.XmlNode object, as in the following example.

  <FieldRef Name="Status"/>
  <Value Type="Text">Complete</Value>

This parameter can contain null.

In simpler terms, this can be a snippet of CAML to add an additional filter to the request.


Contrary to the statements above about passing nulls for changeToken and contains, you cannot do so from the client (it may be allowable in server side code, but I’m not sure). If you pass an empty node in your SOAP request, the request will fail. SPServices – as of version 2013.02 – will handle empty values for you. This is the bug I fixed.

GetListItemChangesSinceToken Returns

In your first call to GetListItemChangesSinceToken, you won’t have a token yet. Or, you may choose not to pass a token that you do have. In that case, you get a robust amount of information returned to you.

The simplest call looks like this:

  operation: "GetListItemChangesSinceToken",
  listName: "Sales Opportunities"

Here, I’m just asking for the information for the Sales Opportunities list; I’m not providing any parameter values at all. On that first call, the results will look something like this (inside the SOAP envelope and GetListItemChangesSinceTokenResult):

<listitems xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema" MinTimeBetweenSyncs="0" RecommendedTimeBetweenSyncs="180" MaxBulkDocumentSyncSize="500" AlternateUrls=",,," EffectivePermMask="FullMask">
  <Changes LastChangeToken="1;3;37920121-19b2-4c77-92ff-8b3e07853114;635243604504600000;33126">
    <List DocTemplateUrl="" DefaultViewUrl="/Intranet/JQueryLib/Lists/Sales Opportunities/AllItems.aspx" MobileDefaultViewUrl="" ID="{37920121-19B2-4C77-92FF-8B3E07853114}" Title="Sales Opportunities" Description="" ImageUrl="/_layouts/images/itgen.gif" Name="{37920121-19B2-4C77-92FF-8B3E07853114}" BaseType="0" FeatureId="00bfea71-de22-43b2-a848-c05709900100" ServerTemplate="100" Created="20090825 06:24:48" Modified="20131216 03:52:25" LastDeleted="20120627 07:54:25" Version="362" Direction="none" ThumbnailSize="" WebImageWidth="" WebImageHeight="" Flags="612372480" ItemCount="7" AnonymousPermMask="0" RootFolder="/Intranet/JQueryLib/Lists/Sales Opportunities" ReadSecurity="1" WriteSecurity="1" Author="3" EventSinkAssembly="" EventSinkClass="" EventSinkData="" EmailInsertsFolder="" EmailAlias="" WebFullUrl="/Intranet/JQueryLib" WebId="42f65d3f-343d-4627-a9a3-abf3d4d6491f" SendToLocation="" ScopeId="c8b78fea-7952-433a-be20-cda628ea6cbb" MajorVersionLimit="0" MajorWithMinorVersionsLimit="0" WorkFlowId="" HasUniqueScopes="False" AllowDeletion="True" AllowMultiResponses="False" EnableAttachments="True" EnableModeration="False" EnableVersioning="False" Hidden="False" MultipleDataList="False" Ordered="False" ShowUser="True" EnableMinorVersion="False" RequireCheckout="False">
        <Field ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Type="Text" Name="Title" DisplayName="Title" Required="TRUE" SourceID="" StaticName="Title" FromBaseType="TRUE" ColName="nvarchar1"/>
        <Field Name="Lead_x0020_Source" FromBaseType="FALSE" Type="MultiChoice" DisplayName="Lead Source" Required="TRUE" FillInChoice="TRUE" ID="{0ab3481c-a7b4-432f-8292-c1617744a167}" Version="6" StaticName="Lead_x0020_Source" SourceID="{f4ebb20a-91e0-4306-997d-208e1d1920b7}" ColName="ntext3" RowOrdinal="0">
            <CHOICE>Newspaper Advertising</CHOICE>
            <CHOICE>Web Site</CHOICE>
            <CHOICE>Personal Referral</CHOICE>
        <Field Type="Currency" DisplayName="Potential Value" Required="FALSE" Decimals="2" LCID="1033" ID="{e9058e0c-b2e8-4af6-882c-9551a5f21451}" SourceID="{f4ebb20a-91e0-4306-997d-208e1d1920b7}" StaticName="Potential_x0020_Value" Name="Potential_x0020_Value" ColName="float1" RowOrdinal="0" Version="1">
  <rs:data ItemCount="7">
    <z:row ows_Attachments="1" ows_LinkTitle="Tue Feb 14 15:58:39 EST 2012" ows_Modified="2013-12-11 10:04:38" ows_Author="3;#Marc D Anderson" ows_Lead_x0020_Source=";#Newspaper Advertising;#" ows_Potential_x0020_Value="12.6100000000000" ows_Lead_x0020_Date="2013-04-20 00:00:00" ows_Country="1;#United States" ows_Region="1;#Northeast" ows_State="2;#Rhode Island" ows_City="706;#Providence" ows_Course_x0020_Title="1;#ss" ows_System="3;#Alienware" ows_StateID="North Dakota" ows_Domain="7;" ows_Notes="&lt;div&gt;&lt;/div&gt;" ows_Web_x0020_Service_x0020_Operatio="13;#GetList" ows_Radio_x0020_Buttons="C" ows_Sales_x0020_Rep="104;#Marc Anderson;#89;#Eric Mullerbeck;#91;#Renzo Grande" ows_a_x0020_b_x0020_bcb_x0020_n="" ows_CalcTest="float;#151.320000000000" ows_CalcTestDate="datetime;#2009-08-25 14:24:48" ows_CalcTestYesNo="boolean;#1" ows_YesNo="1" ows_UpdateList_Field_Sun_x0020_Oct_x="lk;k;lk;lk" ows__Level="1" ows_UniqueId="5;#{3345A2C2-0B2D-4D79-9CB8-4FEFA993F5FA}" ows_FSObjType="5;#0" ows_Created_x0020_Date="5;#2009-08-25 14:24:48" ows_Title="Tue Feb 14 15:58:39 EST 2012" ows_Created="2009-08-25 14:24:48" ows_ID="5" ows_owshiddenversion="256" ows_FileLeafRef="5;#5_.000" ows_FileRef="5;#Intranet/JQueryLib/Lists/Sales Opportunities/5_.000" ows__ModerationStatus="0"/>

Yes, that is a heck of a lot of XML, but it’s not even all of it (note the ellipses). This may seem like overkill in many cases, and it may well be. In those cases, GetListItems is still the best way to go. But in the cases where you want to build an SPA, GetListItemsWithToken may be the ticket.

Like I said, you get a lot of robust information. Here are some more details on what’s there:

  • A bunch of info about what frequency and size requests are allowed. Generally you won’t need to care about these values, but they are there when you get to that point.
  • AlternateUrls – These are the URLs for each access path you might take to your list. Sweet! (The security folks will probably blanch at this.)
  • In any call to GetListItemChangesSinceToken we get a new value for LastChangeToken whether we have passed a changeToken or not. This token isn’t something we’re supposed to pick apart or understand; it represents a certain point in the database life. If you really want to understand what the pieces of the token mean, you can check out the article Overview: Change Tokens, Object Types, and Change Types. You’ll want to grab that value, as you will use it in subsequent calls. You can decipher what each part of it means from the documentation, but you really shouldn’t care. Basically, it’s *like* a timestamp, but in database terms.
  • The full list schema. You may not ever need this, but it’s great in cases where you find yourself doing a GetList/GetListItems pair of calls to set up further work in your script.
  • Server information (lines 17-31 above). This is really cool. I’ve never been able to figure out a consistent way to determine the language and locale settings, the time zone offset, etc. from the client, but here they are. Keep in mind that these are the *server* settings, not the client settings.
  • All of the list items. With large lists (not so large that they hit the 5000 item throttling limit – that’s another story entirely, and another post), you probably don’t want to get all of the list items. That’s where the contains parameter comes in handy. You might make the first call specifying that you only want the item with ID=1 or only items where the Title is null (generally this will result in no items) or something.

In the next call, you’re probably going to pass in the LastChangeToken value from the first call:

  operation: "GetListItemChangesSinceToken",
  listName: "Sales Opportunities",
  changeToken: "1;3;37920121-19b2-4c77-92ff-8b3e07853114;635243604504600000;33126"

If there have been no changes, the results will look something like this. Nice and simple.

<listitems MinTimeBetweenSyncs="0" RecommendedTimeBetweenSyncs="180" MaxBulkDocumentSyncSize="500" AlternateUrls=",,," EffectivePermMask="FullMask" xmlns:rs="urn:schemas-microsoft-com:rowset">
<Changes LastChangeToken="1;3;37920121-19b2-4c77-92ff-8b3e07853114;635175524963570000;31260"> </Changes>
<rs:data ItemCount="0"></rs:data>

If there have been changes to items in the list, the results will look something like this:

<listitems MinTimeBetweenSyncs="0" RecommendedTimeBetweenSyncs="180" MaxBulkDocumentSyncSize="500" AlternateUrls=",,," EffectivePermMask="FullMask" xmlns:rs="urn:schemas-microsoft-com:rowset">
<Changes LastChangeToken="1;3;37920121-19b2-4c77-92ff-8b3e07853114;635175524963570000;31260"> </Changes>
<rs:data ItemCount="2">
	<z:row ows_Attachments="0" ows_LinkTitle="MyItem1" ows_MetaInfo="3;#" ows__ModerationStatus="0"
		ows__Level="1" ows_Title="MyItem1" ows_ID="3" ows_owshiddenversion="2"
		ows_UniqueId="3;#{9153FDD3-7C00-47E9-9194-956BB20AAA8D}" ows_FSObjType="3;#0"
		ows_Created_x0020_Date="3;#2007-08-31T21:34:59Z" ows_Created="2007-08-31T21:34:59Z"
		ows_FileLeafRef="3;#3_.000" ows_FileRef="3;#sites/MyWebSite/Lists/MyList/3_.000"
		ows_ServerRedirected="0" />
	<z:row ows_Attachments="0" ows_LinkTitle="MyItem2" ows_MetaInfo="5;#" ows__ModerationStatus="0"
		ows__Level="1" ows_Title="MyItem2" ows_ID="5" ows_owshiddenversion="3"
		ows_UniqueId="5;#{5BDBB1C0-194D-4878-B716-E397B0C1318C}" ows_FSObjType="5;#0"
		ows_Created_x0020_Date="5;#2007-08-31T21:43:23Z" ows_Created="2007-08-31T21:43:23Z"
		ows_FileLeafRef="5;#5_.000" ows_FileRef="5;#sites/MyWebSite/Lists/MyList/5_.000"
		ows_ServerRedirected="0" />

If there have been any item deletions, the Changes node will look a little different (you may see deletes, update, and adds depending on what’s been going on and how long it’s been since you asked):

<Changes LastChangeToken="1;3;641d61d7-b03e-4078-9e6c-379fa0208d6f;635243635233730000;33127">
  <Id ChangeType="Delete">1</Id>

This is just about the only place I know of where you can get information about deletes from the client side. Generally the only way to “catch” deletes is to write an event receiver to run on the server. Of course, you’ll only see the deletes since the last time you passed in a token value, but it allows you to reflect those deletes on the client.

If there have been any changes to the list settings (schema), we’ll get the schema again before the changed items. The results in this case look like what we get back from GetList and from the first call above. We receive everything we need to know to work with the list. For this reason, we would generally make a call to GetListItemChangesSinceToken first to get the list schema and all of the list items – at least those that match our filters – and then use either GetListItemChangesSinceToken, GetListItems, or GetListItemChanges for subsequent calls for our SPA.


As you can see, GetListItemsWithToken is incredibly powerful and returns a wealth of information. In fact, I’m not sure that you could get all of this from a REST call, but probably from CSOM. These old, crufty SOAP Web Services still have their place in the world.

Even better, they can provide you with what you need to get going on SPAs of you own. Hopefully you’re starting to see the possibilities.

jQuery Library for SharePoint Web Services (SPServices) 2013.02 Released

Yesterday I released SPServices 2013.02.

Here are the release headlines:

  • Improved compatibility with SharePoint 2013, whether on premises or on Office365, especially in the value-added functions (SPCascadeDropdowns, SPDisplayRelatedInfo, etc.)
  • Better API stability, including fixes for GetListItemChanges, GetListItemChangeSinceToken, and others
  • Bug fixes and performance improvements

While this is primarily a maintenance release, if you are using an earlier version of SPServices, I strongly suggest that you upgrade to this version, as you will see some performance improvements and there is some new functionality. Thanks to everyone who downloaded the beta and provided feedback.

Both releases in 2013 (2013.01 and 2013.02) work equally well – at least as best as I have been able to ensure – with SharePoint 2007, 2010, and 2013. My next planned release version will be 2014.01, which should make it obvious that the primary version number in my recent scheme is just the year of release.

One thing I wanted to mention that’s old news, but some people may have missed it. By far the most exciting thing in the 2013.01 release was jQuery promises, or deferred objects. All calls to the core operations return jQuery promises as of 2013.01. If you’re interested in increasing the efficiency of your code and getting ready for the way you are likely to work with the REST Web Services in SharePoint 2013, you should get familiar with using promises.

There are other goodies in this release, and you can see the full list of enhancements and improvements on the download page. Note the link to the Issue Tracker items for this release. For posterity, here are links to the release notes showing all the fixes and improvements from the Issue Tracker on Codeplex.

New Functionality

Alpha Issue Tracker Item Function Description
ALPHA5 10195 $().SPServices.SPGetQueryString Case insensitive SPGetQueryString?

Bug Fixes and Efficiency

Alpha Issue Tracker Item Function Description
ALPHA1 10152 $().SPServices.SPCascadeDropdown Multi-Select Lookup Cascading Dropdown’s Not Working
ALPHA1 10153 $().SPServices.SPComplexToSimpleDropdown Make SPComplexToSimpleDropdown Work in 2013 Publishing Pages
ALPHA1 10144 $().SPServices.SPXmlToJson userToJsonObject userId broken in 2013.01
ALPHA1 10146 $().SPServices ResolvePrincipals with addToUserInfoList=true requires SOAPAction
ALPHA2 10184 $().SPServices Lists.GetListItemChangeSinceToken operation input parameters not defined
ALPHA2 10183 $().SPServices Webs.GetColumns – Passing Unneeded webUrl Parameter
ALPHA2 10148 $().SPServices async on version 2013.01
ALPHA2 10177 $().SPServices First 2 arguments for the of addToPayload method for the GetTermSets are incorrect
ALPHA2 10154 $().SPServices SPCascadeDropdown: matchOnId not working with multi-select parents
ALPHA3 10165 $().SPServices SPServices() uses wrong server relative URL on SP2010
ALPHA3 10162 $().SPServices.SPRedirectWithID Bug in SPRedirectWithID
ALPHA3 10067 $().SPServices.SPAutocomplete SPAutocomplete dropdown issue
ALPHA3 10167 $().SPServices Caching bug if making same call to different webURLs
ALPHA4 10200 $().SPServices SPConvertDateToISO invalid TZD
ALPHA5 10147 $().SPServices.SPAutocomplete SPServices / jQuery ID Selectors in SP 2013, SPAutoComplete
ALPHA5 10189 $().SPServices.SPGetCurrentUser SPGetCurrentUser not being async
ALPHA5 10189 $().SPServices.SPGetCurrentUser SPGetCurrentUser data on iPhone
BETA2 10206 $().SPServices.SPCascadeDropdowns Multi-Select Dropdown Controls Don’t Have Name Attributes
BETA3 NA $().SPServices.SPDisplayRelatedInfo Fixed issues with lookup column handling