Client-Side JavaScript Debugging Tip — Using XSP.dumpObject() and XSP.log()

The XSP object is a client-side JavaScript object that is built and delivered by the XPages runtime and it contains a number of useful functions, some of which are always available and some of which are context-specific. In this post, I’ll show two functions that are useful for client-side JavaScript debugging.

XSP.dumpObject()

The dumpObject() method returns a string representation of all properties of any object that you pass to it. If you want to track down why something isn’t working as expected, it may be helpful to inspect the properties of the object.

The object can be a variable that you programmatically create and modify or it can be an element on the page.

I used to make frequent use of a small script to display all properties of a JavaScript object, but this is a much simpler way to get that information.

Here’s an example of client-side JavaScript to build an object and use XSP.dumpObject() to write out all properties to the browser console:

var myObject = {}
myObject.property1 = 'One';
myObject.property2 = 'Two';
myObject.property3 = 'Three';
myObject.arrayProperty = ['a', 'b', 'c'];

console.log(XSP.dumpObject(myObject));

This information can be very helpful in debugging, so I would recommend using it when handling errors in a try-catch block.

XSP.log()

In addition to logging to the console, you have another similar option available by another XSP function. XSP.log() is analogous to the standard console.log() function that you use when debugging client-side

JavaScript, but the log() method opens up a new browser window and logs messages there, in descending order.

All we have to do is change the last line in the previous snippet:

XSP.log(XSP.dumpObject(myObject));

And this is what we see in a separate browser window:

XSPLog - 1

The log() method will continue appending any messages that you send until you close the window. The next call to XSP.log() will open a new, blank window and start logging again.

Using dumpObject on an HTML field and XPages control

You can also use dumpObject on an HTML element.

For example, if I have a pure HTML select with 3 options and an onchange event, this will be the output of if I get a handle to the element (the id is HTMLComboBox) and dump the object properties:

XSP.log(XSP.dumpObject(dojo.byId('htmlComboBox')));

object
  0: [object HTMLOptionElement] (Maximum Depth Reached)
  1: [object HTMLOptionElement] (Maximum Depth Reached)
  2: [object HTMLOptionElement] (Maximum Depth Reached)
  autofocus: false
  disabled: false
  form: [object HTMLFormElement] (Maximum Depth Reached)
  multiple: false
  name: 
  required: false
  size: 0
  type: select-one
  options: [object HTMLOptionsCollection] (Maximum Depth Reached)
  length: 3
  selectedOptions: [object HTMLCollection] (Maximum Depth Reached)
  selectedIndex: 0
  value: v1
  willValidate: true
  validity: [object ValidityState] (Maximum Depth Reached)
  validationMessage: 
  title: 
  lang: 
  dir: 
  dataset: [object DOMStringMap] (Maximum Depth Reached)
  itemScope: false
  itemType:  (Maximum Depth Reached)
  itemId: 
  itemRef:  (Maximum Depth Reached)
  itemProp:  (Maximum Depth Reached)
  properties: [object HTMLPropertiesCollection]... (Maximum Depth Reached)
  itemValue:  (Maximum Depth Reached)
  hidden: false
  tabIndex: 0
  accessKey: 
  accessKeyLabel: 
  draggable: false
  contentEditable: inherit
  isContentEditable: false
  contextMenu:  (Maximum Depth Reached)
  spellcheck: false
  style: [object CSS2Properties] (Maximum Depth Reached)
  className: 
  oncopy:  (Maximum Depth Reached)
  oncut:  (Maximum Depth Reached)
  onpaste:  (Maximum Depth Reached)
  offsetParent: [object HTMLBodyElement] (Maximum Depth Reached)
  offsetTop: 39
  offsetLeft: 0
  offsetWidth: 67
  offsetHeight: 19
  onabort:  (Maximum Depth Reached)
  onblur:  (Maximum Depth Reached)
  onfocus:  (Maximum Depth Reached)
  oncanplay:  (Maximum Depth Reached)
  oncanplaythrough:  (Maximum Depth Reached)
  onchange:  (Maximum Depth Reached)
  onclick:  (Maximum Depth Reached)
  oncontextmenu:  (Maximum Depth Reached)
  ondblclick:  (Maximum Depth Reached)
  ondrag:  (Maximum Depth Reached)
  ondragend:  (Maximum Depth Reached)
  ondragenter:  (Maximum Depth Reached)
  ondragleave:  (Maximum Depth Reached)
  ondragover:  (Maximum Depth Reached)
  ondragstart:  (Maximum Depth Reached)
  ondrop:  (Maximum Depth Reached)
  ondurationchange:  (Maximum Depth Reached)
  onemptied:  (Maximum Depth Reached)
  onended:  (Maximum Depth Reached)
  oninput:  (Maximum Depth Reached)
  oninvalid:  (Maximum Depth Reached)
  onkeydown:  (Maximum Depth Reached)
  onkeypress:  (Maximum Depth Reached)
  onkeyup:  (Maximum Depth Reached)
  onload:  (Maximum Depth Reached)
  onloadeddata:  (Maximum Depth Reached)
  onloadedmetadata:  (Maximum Depth Reached)
  onloadstart:  (Maximum Depth Reached)
  onmousedown:  (Maximum Depth Reached)
  onmouseenter:  (Maximum Depth Reached)
  onmouseleave:  (Maximum Depth Reached)
  onmousemove:  (Maximum Depth Reached)
  onmouseout:  (Maximum Depth Reached)
  onmouseover:  (Maximum Depth Reached)
  onmouseup:  (Maximum Depth Reached)
  onpause:  (Maximum Depth Reached)
  onplay:  (Maximum Depth Reached)
  onplaying:  (Maximum Depth Reached)
  onprogress:  (Maximum Depth Reached)
  onratechange:  (Maximum Depth Reached)
  onreset:  (Maximum Depth Reached)
  onscroll:  (Maximum Depth Reached)
  onseeked:  (Maximum Depth Reached)
  onseeking:  (Maximum Depth Reached)
  onselect:  (Maximum Depth Reached)
  onshow:  (Maximum Depth Reached)
  onstalled:  (Maximum Depth Reached)
  onsubmit:  (Maximum Depth Reached)
  onsuspend:  (Maximum Depth Reached)
  ontimeupdate:  (Maximum Depth Reached)
  onvolumechange:  (Maximum Depth Reached)
  onwaiting:  (Maximum Depth Reached)
  onmozfullscreenchange:  (Maximum Depth Reached)
  onmozfullscreenerror:  (Maximum Depth Reached)
  onmozpointerlockchange:  (Maximum Depth Reached)
  onmozpointerlockerror:  (Maximum Depth Reached)
  onerror:  (Maximum Depth Reached)
  tagName: SELECT
  id: htmlComboBox
  classList:  (Maximum Depth Reached)
  attributes: [object MozNamedAttrMap] (Maximum Depth Reached)
  onwheel:  (Maximum Depth Reached)
  scrollTop: 0
  scrollLeft: 0
  scrollWidth: 67
  scrollHeight: 19
  clientTop: 0
  clientLeft: 0
  clientWidth: 67
  clientHeight: 19
  scrollTopMax: 0
  scrollLeftMax: 0
  innerHTML: <option value="v1">Value 1</option><option value="v2">Value 2</option><option value="v3">Value 3</option>
  outerHTML: <select id="htmlComboBox"><option value="v1">Value 1</option><option value="v2">Value 2</option><option value="v3">Value 3</option></select>
  previousElementSibling: [object HTMLBRElement] (Maximum Depth Reached)
  nextElementSibling: [object HTMLBRElement] (Maximum Depth Reached)
  children: [object HTMLCollection] (Maximum Depth Reached)
  firstElementChild: [object HTMLOptionElement] (Maximum Depth Reached)
  lastElementChild: [object HTMLOptionElement] (Maximum Depth Reached)
  childElementCount: 3
  nodeType: 1
  nodeName: SELECT
  baseURI: http://127.0.0.2/BlogTesting.nsf/dumpObject.xsp
  ownerDocument: [object HTMLDocument] (Maximum Depth Reached)
  parentNode: [object HTMLFormElement] (Maximum Depth Reached)
  parentElement: [object HTMLFormElement] (Maximum Depth Reached)
  childNodes: [object NodeList] (Maximum Depth Reached)
  firstChild: [object HTMLOptionElement] (Maximum Depth Reached)
  lastChild: [object HTMLOptionElement] (Maximum Depth Reached)
  previousSibling: [object Text] (Maximum Depth Reached)
  nextSibling: [object HTMLBRElement] (Maximum Depth Reached)
  nodeValue:  (Maximum Depth Reached)
  textContent: Value 1Value 2Value 3
  namespaceURI: http://www.w3.org/1999/xhtml
  prefix:  (Maximum Depth Reached)
  localName: select
  ELEMENT_NODE: 1
  ATTRIBUTE_NODE: 2
  TEXT_NODE: 3
  CDATA_SECTION_NODE: 4
  ENTITY_REFERENCE_NODE: 5
  ENTITY_NODE: 6
  PROCESSING_INSTRUCTION_NODE: 7
  COMMENT_NODE: 8
  DOCUMENT_NODE: 9
  DOCUMENT_TYPE_NODE: 10
  DOCUMENT_FRAGMENT_NODE: 11
  NOTATION_NODE: 12
  DOCUMENT_POSITION_DISCONNECTED: 1
  DOCUMENT_POSITION_PRECEDING: 2
  DOCUMENT_POSITION_FOLLOWING: 4
  DOCUMENT_POSITION_CONTAINS: 8
  DOCUMENT_POSITION_CONTAINED_BY: 16
  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32

If I have a similar Combo Box control in XPages, the output is the same because the HTML element that is generated is virtually identical.

Note: You must pass a fully-qualified client-side ID to dumpObject(), so if you’re passing it an XPages element, use the pass-thru EL syntax "#{id:myControl}" or a client-side query that looks for the element id ending with the name of the control, such as dojo.query('[id~=myControl]')[0].id

console.dir(object)

There’s a method built into browser development tools that performs a similar function. This can be similarly handy when troubleshooting something directly on the page.

For example, to see all of the properties of the first input field on the form, you can use it like this:

console.dir(dojo.query('input')[0]);

Setting the Maximum File Upload Size for an XPages Application

If you want to control the maximum value for an uploaded file, you can use the xsp.upload.maximumsize setting in xsp.properties.

Application-Level Maximum Upload Size

There is no maximum upload set at the application level by default. (Although there is a server-level setting that I’ll come back to later in this post.)

To set it, you can use the Xsp Properties design element and set the Maximum size property under File Upload Options in the first column of the General tab. Specify the value in kilobytes (KB)

MaxFileUploadTesting-0-Setting

This adds the following value to the xsp.properties file (as seen on the Source tab):

xsp.upload.maximumsize=400

Error Message

If you add an Error Message control for the File Upload control or an Error Messages control on the form, you will see an error message regarding the file size setting when you try to submit a form with an attachment that’s too large.

MaxFileUploadTesting-1-Error

Server-wide Limit

As mentioned earlier, there’s no default at the application level. The default is the server-wide setting, which defaults to roughly 10 MB.

Even if you set the maximum at the application level, it can never exceed the server setting.

The server setting is defined on the Server document in the Domino Directory. It can be found on the Internet Protocols tab, HTTP subtab in the HTTP Protocol Limits section.

MaxFileUploadTesting-3-ServerSetting

If you try to upload a file larger than the server-wide limit, it aborts the connection and throws an ugly error. (HTTP 500 – Internal Server Error)

MaxFileUpload-ServerError

Even if you set an application level maximum lower than the server maximum, if you try to upload a file larger than the server maximum, it will throw the same HTTP error.

Domino Usage Survey

As you may have already heard, PSC has posted a survey to gauge current usage and future direction for Domino.

If you haven’t already taken the survey, please take a moment and let us know how Domino is used in your organization. Please also share the link – the more feedback we get, the stronger the results will be.

http://bit.ly/PSCXPagesSurvey

The survey will be open until October 3rd and results will be posted publicly on October 6th.

Modifying an XSP Property fo a Single XPage

I’m familiar with setting XSP properties in xsp.properties at the application or server level and I’ve used tags to do the same within a theme, but I did not realize until recently that you can also set these properties at the XPage level to override the broader settings on a page-by-page basis.

Select the root tag for the page and go to All Properties in the Properties view. Under the data subtab, click on properties and click the plus (+) button to add a property.

This adds a new parameter and defaults the name attribute to param and the value attribute to val. In the source, it adds an xp:this.properties tag containing an xp:parameter tag.

XSP Property Page Level - 1

Let’s see this in action with a few examples.

Here’s the first fiew lines of a brand new, unmodified XPage on my test server. The application leaves the server defaults for the HTML doctype and character set (although the latter is not visible in this output).

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<title></title>

If you want to add a meta tag to the page that displays the doctype and character set, you can add the xsp.html.meta.contenttype property to the page and set its value to true.

You can also override the HTML doctype and character set for the page with additional properties. (Click on the picture to enlarge.)

XSP Property Page Level - 2

Here’s the updated first few lines of output, including the meta tag and reflecting the updated doctype and character set:

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title></title>

There are many more properties that can be modified at the page level (though not all of them). If you have the XPages Portable Command Guide, you can find them all described in depth there (The book covers through 8.5.3. Sven Hasselbach has a good post on some new properties that were added in Notes 9.)

It appears that you can similarly modify properties at the custom control level, but I have not tested the effectiveness or how conflicts are handled.

Allowing 0 Rows in a Repeat

The Allow zero rows in repeat controls setting in XSP Properties can be used to cause a Repeat control not to display any rows, giving you the ability to compute the row count as desired, even when there is data that would otherwise have been displayed. In this post, I’ll show how to update the setting and describe its effect.

XSP Properties

In Notes 9, you can find the XSP properties under Application Configuration in the Applications view of Domino Designer.

ZeroRowsInRepeat-A

This UI gives you a nicer front end to modify a number of values in the xsp.properties file, which contains a lot of configuration information for the application.

There are 4 tabs, 3 of which contain several properties that you can modify and the fourth tab which displays the full source of the properties file.

There are many, many properties that can be set in the file, but there are only a few specified by default. The rest are treated as default values when not otherwise specified.

ZeroRowsInRepeat-B

As you change properties in the UI, they will be added to the source.

Allow zero rows in repeat controls

The Allow zero rows in repeat controls setting on the Page Generation tab is not selected (i.e. false) by default.

ZeroRowsInRepeat-C

The effect is that if you have a Repeat control where the rows property is computed to 0, it will revert to the default row count of 30.

If you select that option, it adds this line to xsp.properties:

xsp.repeat.allowZeroRowsPerPage=true

With this setting, you can compute a Repeat control’s row count to 0 and it will not show any rows. This effectively hides it from the UI, but still loads the repeat control into the JSF component tree so it can be modified programmatically, if necessary.

Applicability

The XPages Portable Command Guide says that this property applies to View Panel, Data Table, and Repeat controls.

It works as expected with a Repeat control, but did not work properly with a View Panel in my testing (with Notes 9).

With the property set to false, a View Panel with the rows property set to 0 would show 30 rows. With the property set to true, the View Panel throws a divide by zero error.

ZeroRowsInRepeat-D

Getting the Base URL of the Current Database with SSJS

It’s easy to get the current page URL in SSJS with context.getUrl().toString(). However, it’s a little less straightforward if you just want to determine the base URL of the current application. This can be useful when you need to build a link to some other page within the current application and send it out in an e-mail notification. In this post, I’ll look at the results of some methods in the XSPUrl object and show how to achieve the desired effect.

In the last post, I looked at URL @functions in the Extension Library. There are some useful functions there, but none that retrieve what I’m looking for.

XSPUrl

The global context object has a getUrl() method that makes it easy to get a handle to an XSPUrl object representing the current URL. You can retrieve the full URL (including the querystring) or use any of the methods available to get pieces of the URL ( getAddress(), getHost(),getParameter(), getPath(), getPort(), getQueryString()).

Here are some examples of the output, given this url: http://127.0.0.2/BlogTesting.nsf/URL.xsp?parameter1=a

Method Result
context.getUrl().toString() http://127.0.0.2/BlogTesting.nsf/URL.xsp?parameter1=a
context.getUrl().getPath() /BlogTesting.nsf/URL.xsp
context.getUrl().getAddress() http://127.0.0.2/BlogTesting.nsf/URL.xsp
context.getUrl().getHost() 127.0.0.2
context.getUrl().getSiteRelativeAddress(context) /URL.xsp

There are several handy methods, but none providing exactly what I’m looking for.

Options for Getting the Base URL

The getAddress() method gets the URL up to, but not including, the querystring. However, it still includes the current page name. The global view object has a getPageName() method that returns the name of the currently-displayed XPage.

The snippet that I’ve been using recently uses the getAddress() method and replaces the current page name with an empty string, leaving us with the base URL:

context.getUrl().getAddress().replace(view.getPageName(), '')

You could just as easily use Javascript’s substr or substring methods to look for the ‘.nsf’ and only retrieve characters up to the end of it in most cases, but that may be problematic if you have routing that masks the NSF name.

What’s Your Solution?

This seems like something that probably has a number of workarounds. How have you solved it? Is there something simpler that I’ve overlooked?

Update

Check out David Leedy’s XPages URL Cheatsheet for more URL-related functions http://xpagescheatsheet.com/cheatsheet.nsf/url.xsp

There are two listed on there that sounded like they might provide what I was looking for, but did not.

  • database.getHttpURL() – includes the database replica ID rather than name and appends ?OpenDatabase to the end
  • view.getBaseURL() – sounds perfect, but doesn’t return anything

URL @Functions in the Extension Library

There are a few @Functions for working with URLs available in the Extension library. They can come in handy if you need to work with URLs related to any element in your database.

Availability

These functions are part of the extension library. They’re available if you’ve installed the Extension Library, or if you have Upgrade Pack 1 for 8.5.3. They’re also part of the extension library features built directly into Notes9.

@FunctionsEx

The functions are available on the Reference tab in the SSJS Script Editor. Under Libraries, select @FunctionsEx to see the additional functions.

ExtLib URL Functions - 1

The following URL functions are available:

  • @EncodeUrl() – Encodes spaces and symbols in the URL
  • @FullUrl() – Displays the full path to a resource in the database (relative to the server)
  • @AbsoluteUrl() – Displays the absolute path to a resource in the database*
  • @IsAbsoluteUrl() – Boolean value for whether a given URL is absolute

Examples

Here are some examples of how they can be used in an application:

Code Result
@FullUrl(view.getPageName()) /BlogTesting.nsf/ExtLibURLFormulas.xsp
@AbsoluteUrl(view.getPageName()) http://127.0.0.2/ExtLibURLFormulas.xsp
@EncodeUrl(context.getUrl().toString());

Where the url is: http://127.0.0.2/BlogTesting.nsf/ExtLibURLFormulas.xsp?my_Parameter=spaces and $ymbol$
http://127.0.0.2/BlogTesting.nsf/ExtLibURLFormulas.xsp?my_Parameter=spaces+and+%24ymbol%24
@IsAbsoluteUrl(view.getPageName()) false
@IsAbsoluteUrl(context.getUrl().toString()) true

*@AbsoluteUrl

I generally use context.getUrl() and work with that to parse and build URLs, so I haven’t run into this before, but the output of @AbsoluteUrl() wasn’t what I expected. At first glance, it appeared to give the full url to the specified design element, but it actually just gives the protocol, server, and design element name that was supplied.

It looks like I can get the real absolute url to the design element by combining @FullUrl() with @AbsoluteUrl()

Code Result
@AbsoluteURL(@FullUrl(view.getPageName())) http://127.0.0.2/BlogTesting.nsf/ExtLibURLFormulas.xsp
Follow

Get every new post delivered to your Inbox.

Join 53 other followers