JQM & Domino Data Service (2)

In a previous post I demonstrated how to use Domino Data Service (as part of Domino Access Service) to populate a listview for jQuery Mobile. Probably the real-world application you want to mobilize has multiple layers of data that is common in Notes (multiple forms & views). I followed Christophe Coenraets’s example and re-used it for the fakenames application.

First let me show you what you get:





At first an overview of the People’s view is presented, from here the user can open the Person details page. In Christophe’s example you can find a more nested navigation but our fakenames application is not setup like that.

What this demo includes:

  • 2 XPages; eg people.xsp and person.xsp.
  • Your modified version of employeelist.js and employeedetails.js in the download from Christophe page.
  • The CSS files in the download from Christophe page.
  • The jQuery JS files in the download from Christophe page.

For convenience I have dropped the files from the download via the Package Explorer in the WebContent folder of the fakenames application. In case you want to mobilize your fakenames application a step further e.g. by packaging it via PhoneGap to access device native api’s you don’t want to store these files here and you don’t want to use XPages to render the HTML.

Of course we assume you have Domino Access Service enabled for the Domino server and the fakenames application.

XPage people.xsp & person.xsp

These files do not differ from the index.html and employeedetails.html files in the download from Christophe page. In case you use XPages you should consider to turn of Dojo and Themes.

JS files employeelist.js & employeedetails.js

Since we will provide the information via Domino Data Service these JS files will be altered. Below you find their code:


var serviceURL = “http://dev1//apps/fakenames.nsf/api/”;

var employees;

$(‘#employeeListPage’).bind(‘pageinit’, function(event) {

function getEmployeeList() {
$.getJSON(serviceURL + ‘data/collections/name/People?count=1000’, function(data) {
$(‘#employeeList li’).remove();
employees = data;
$.each(employees, function(index, employee) {
var tmp = “”;
tmp+='<a href=”person.xsp?id=’ + employee[“@unid”] + ‘”>’ +
‘<img src=”http://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif”/>&#8217;;
tmp+='<h4>’ + employee.$17 + ‘</h4>’;
tmp+='<p>’ + employee.CompanyName + ‘</p>’;
if (employee.$12!=””) {
tmp+='<span class=”ui-li-count”>’ + employee.$12 + ‘</span>’;
$(‘#employeeList’).append( tmp );


At the page initiation the function getEmployeeList is called. This makes an Ajax request to the REST API of Domino Data Service and a document collection with the first 1000 entries (if available) in the view named People is being called.

I have not taken a look yet how to implement lazy loading or appending additional document collection(s) when scrolling. If you happen to know how to implement such function please drop me a line here or send me an email.

Then list items in the employee ID element on the Xpage people.xsp are removed (when available) and we loop through the results returned by Domino. We create a new list and establish a hyperlink to people.xsp and add the UNID of the document as parameter.

At last the result is appended to the (emptied) employee ID element and the listview is being refreshed. Wow! You got now a nice list of persons in the directory.


$(‘#detailsPage’).live(‘pageshow’, function(event) {
var id = getUrlVars()[“id”];
$.getJSON(serviceURL + ‘data/documents/unid/’+id, displayEmployee);

function displayEmployee(data) {
var employee = data;
$(‘#employeePic’).attr(‘src’, ‘http://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif&#8217;);
$(‘#fullName’).text(employee.FirstName + ‘ ‘ + employee.LastName);
if (employee.Manager) {
$(‘#actionList’).append(‘<li><h3>Manager</h3><p>’ + employee.Manager + ‘</p></li>’);
if (employee.InternetAddress) {
$(‘#actionList’).append(‘<li><a href=”mailto:’ + employee.InternetAddress + ‘”><h3>Email</h3>’ +
‘<p>’ + employee.InternetAddress + ‘</p></a></li>’);
if (employee.OfficePhoneNumber) {
$(‘#actionList’).append(‘<li><a href=”tel:’ + employee.OfficePhoneNumber + ‘”><h3>Call Office</h3>’ +
‘<p>’ + employee.OfficePhoneNumber + ‘</p></a></li>’);
if (employee.CellPhoneNumber) {
$(‘#actionList’).append(‘<li><a href=”tel:’ + employee.CellPhoneNumber + ‘”><h3>Call Cell</h3>’ +
‘<p>’ + employee.CellPhoneNumber + ‘</p></a></li>’);
$(‘#actionList’).append(‘<li><a href=”sms:’ + employee.CellPhoneNumber + ‘”><h3>SMS</h3>’ +
‘<p>’ + employee.CellPhoneNumber + ‘</p></a></li>’);

function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf(‘?’) + 1).split(‘&’);
for(var i = 0; i < hashes.length; i++){
hash = hashes[i].split(‘=’);
vars[hash[0]] = hash[1];
return vars;


The idea behind the details page is similar to the people’s list, except we have now a single object in the JSON.

First the ID provided by the parameter is abstracted from the URL and used in the call to a document source via the Domino Data Service.

From the result a set of details is abstracted and presented to the user. Some HTML5 attributes are used to deliver a more native behavior when clicking anchor links (telephone, mail, SMS).

Some thoughts

jQuery Mobile and Domino Access Service provide good foundations for creating mobile Web apps on Domino. However I am a bit thoughtful on the speed of Domino Access Service. If you happen to know how to speed up performance for this please drop a line here.

In a next post I will write the implementation of CRUD actions with jQuery and DDS.


Introduction Dojo Toolkit & IBM Lotus Domino

Currently I am collecting all sorts of information about implementing the DOJO Toolkit in Lotus Domino applications. Basically for preparing myself for a upcoming project to ‘pimp’ an existing web-application using this JS Framework.

Untill now I have been only working with Prototype and Scriptaculous (which I like a lot) but since IBM’s horizon also shines more and more Dojo I am really curious in DOJO’s capabilities. Especially since documentation seems to be getting better and better.

I have found a good presentation about implementing the Dojo Toolkit in Lotus Domino applications on Slideshare. You can find the presentation here.

Introduction Dojo Toolkit & IBM Lotus Domino - presentation

It seems that more and more Notes related people find the time and effort in uploading their presentations on this site, which is ofcourse a very good thing! (HINT)

Navigation-menu from view (XML Transformation)

In a previous writing I explained how you can create a navigation-menu that collects it’s information straight from a Notes View.

example navigation 

Using the ?ReadViewEntries URL command Notes outputs the view data in XML form which can be the source of a transformation to HTML using the XSLTProcessor in your browser.

When the project became actual again I found some time to improve it’s functionality, since it was not working 100% in Firefox. So here is an example available for downloading.

Here is short summary of the example’s features:

  • the navigator collects it’s source data from a Notes view using the ReadViewEntries command
  • when navigating through the menu for each subcategory (via the + and – icons) a new AJAX request is done to collect the information withing that (sub)category (so the amount of data is being divided into smaller parts)
  • the information is being transformed into HTML via the build XSLTransformator of the browser
  • when clicking on (sub)category a collection of responding documents is collected and presented in another frame
  • the navigator also contains document links which will load the document info in the right frame when clicked
  • documents can be grouped under whatever structure in the View

Very nice, I did not manage to solve 1 thing yet: if a (sub)category contains subcategories AND documents, the documents are being displayed FIRST. I rather would display the subcategories first and then the documents. Maybe you can help me with that one?

example of results

Scriptaculous autocompleter in Domino form

This post describes briefly an implementation of Scriptaculous’ Ajax.Autocompleter class.

It uses an AJAX request using a Page element for an @DBLookup I believe first mentioned at Lotusphere 2007 by Jack Rattcliff and later (?) by Jake Howlett in his http://www.codestore.net/store.nsf/unid/BLOG-20060221 and perfectionized (?) by my collegue Tomas.

Scriptaculous autocompleter needs an unordered list in return. For instance this list might be returned after the user typed the letter “y”:

    <li>your mom</li>

Some examples use an agent to return the unordered list, others describe using a page.

The demo allows you to fill in multiple fields after clicking on one of the presented suggestions by splitting the responseText:

autocompleter form

In demo-mode it would look something like this:

demo autocompletion

For downloading a working example click here.

Download Pagination example

I received a couple of mails saying that the link to Bob Obringers article on his ‘ultimate view navigator’ is dead and if I could send a copy of the example.

I have uploaded a working example > here < including an example for a flat view and an example for a categorized view. In the flat view I have included Prototype’s AJAX request handler, the categorized view still uses Bob’s approach. Good luck!

page navigation example

Updated: the application had local encryption enabled…

Printing documents from a view (web)

I almost have my summer-break so in order not to forget what I have been doing lately here a summary of my activities on development: 

Recently I have been working on some projects which all had one thing in common: ‘printing documents from a web browser’.  In this article I describe you my solution how provide a funtion to print (multiple) documents from a web view, with maintaining the often used previous-next navigation in Domino views.

The function is based upon the idea of a cookie. In the cookie you write the document unique ID of the document you want to add to your print selection, and at the end, the point that the user want to actually print the selection, all documents will be collected via an AJAX request and added on a new window.

I have no idea what the performance impact are and have not tested the application well in IE, I assume there might be some considerations using different browser types and versions.

print expl 01

In the view for each document the user has the option to ‘add’ this document to his ‘selection’. The selected document will be presented in a DIV below the view. Also the icon will change from blue to a more inactive ‘purple’:

print expl 02

In the example above the user has selected 2 documents, which can be deleted in 2 ways from the selection:

– document one by one

– all documents at once

print expl 3

Att the bottom the print icon uses a JavaScript function that makes an AJAX request for each document UNID in the cookie and writes the result (HTML) to a new window:

print expl 4

Since the whole idea is based upon a normal default Domino view and it support the option to use the previous-next navigation I think this approach should be easy to implement in most applications.

A sample of the application can be found here. Since this was only one of my approaches to enable printing Notes documents from a webbrowser there is more to come soon.

Now I can really start backpacking at ease :-)

Bob’s ultimate view navigator – categorized view

I love Bob Obringer’s solution for a view navigator. Especially you can easily improve the user’s navigation experience in existing applications.

For example I received a few days ago a request to investigate the options to improve performance on a view. After a quick investigation default a view was opened with the URL parameter &Count=1000. Happy waiting! Further the application enables you to navigate through the documents by opening a categorized view with &RestrictToCategory= parameter, again followed by the &Count=1000 parameter.

Implementing Bob’s solution for the flat was was piece of cake, just copy & paste.

Unfortunately Bob’s approach only works on a flat view and some of the functionality it is is not suitable for a categorized view:

  • the cookie function
  • the function to enter a page number directly in an input field

 Therefor I rewrote the script a bit, preserving 95% of Bob’s code. Thanks Bob!


URL opened:


// initViewNav function, loaded at onLoad event of form
function initViewNav(range, cache, div1, div2) {
 navRange = range;
 navCache = cache;
 navDiv1 = div1;
 navDiv2 = div2;
 startDoc = queryString(‘start’);
 startDoc = (startDoc == “”) ? 1 : parseInt(startDoc);
 docsPerPage = queryString(‘count’);
 docsPerPage = (docsPerPage == “”) ? 20 : parseInt(docsPerPage);

// QueryString function
function queryString(key){
 var page = new PageQuery(window.location.search);
 return unescape(page.getValue(key.toLowerCase()));

function PageQuery(q) {
 if(q.length > 1) this.q = q.substring(1, q.length);
  else this.q = null;
 this.keyValuePairs = new Array();
 if(q) {
  for(var i=0; i < this.q.split(“&”).length; i++) {
   this.keyValuePairs[i] = this.q.split(“&”)[i].toLowerCase();
 this.getKeyValuePairs = function() { return this.keyValuePairs; }
 this.getValue = function(s) {
  for(var j=0; j < this.keyValuePairs.length; j++) {
   if(this.keyValuePairs[j].split(“=”)[0] == s)
   return this.keyValuePairs[j].split(“=”)[1];
  return “”;
 this.getParameters = function() {
  var a = new Array(this.getLength());
  for(var j=0; j < this.keyValuePairs.length; j++) {
   a[j] = this.keyValuePairs[j].split(“=”)[0];
  return a;
 this.getLength = function() { return this.keyValuePairs.length; }

// Cookie functions
function getExpiryDate(minutes){
 var UTCstring;
 Today = new Date();
 nomilli = Date.parse(Today);
 // Today.setTime(nomilli + (minutes * 60000));
 // Today.setTime(nomilli + (minutes * 0)); 
 // Means Cookies disabled!
 // This done because we work with a &RestrictToCategory command in a categorized view and we switch all the time from categories…
 Today.setTime(nomilli + (minutes * 0));
 UTCstring = Today.toUTCString();
 return UTCstring;

function setCookie(name, value, duration){
 cookiestring = name + “=” + escape(value) + “; EXPIRES=” + getExpiryDate(duration);
 document.cookie = cookiestring;

function getCookie(cookiename) {
 var cookiestring = “” + document.cookie;
 var index1 = cookiestring.indexOf(cookiename);
 if (index1 == -1 || cookiename == “”) return “”;
  var index2 = cookiestring.indexOf(‘;’, index1);
 if (index2 == -1) index2 = cookiestring.length;
  return unescape(cookiestring.substring(index1 + cookiename.length + 1, index2));

// Get View Navigator HTML
var startDoc = 0;
var docsPerPage = 0;
var totalPages = 0;
var navRange = 0;
var navCache = 0;
var navDiv1 = “”;
var navDiv2 = “”;
// var checkCookie = true;
// cookies has not use with a &RestrictToCategory command in a categorized view
var checkCookie = false;
var totalDocs = “”;
function waitForDocCount() {
 if (checkCookie) {
  totalDocs = getCookie(‘totaldocs’);
  checkCookie = false;
 if (totalDocs == “”) {
  setTimeout(“waitForDocCount()”, 100);
 } else {

// Function to get document count using XMLHTTP
function getDocCount () {
 var xmlHttp = getXMLHTTP();
 xmlHttp.open(“GET”, dbPath + viewAlias + “?ReadViewEntries&RestrictToCategory=”+ Categories );
 xmlHttp.onreadystatechange = function() {
  if (xmlHttp.readyState == 4 && xmlHttp.responseText) {
   var resp = xmlHttp.responseText;
   var countTag = resp.toLowerCase().indexOf(‘toplevelentries’);
   if (countTag > 0) {
    resp = resp.substr(resp.indexOf(‘”‘, countTag) + 1);
    totalDocs = resp.substring(0, resp.indexOf(‘”‘));
    setCookie(‘totaldocs’, totalDocs, navCache)

// Wrapper function to get a cross browser XMLHTTP object
function getXMLHTTP() {
 // function to create an XmlHttp object
 var xmlHttp = null
 try {
  xmlHttp = new ActiveXObject(“Msxml2.XMLHTTP”)
 } catch(e) {
  try {
   xmlHttp = new ActiveXObject(“Microsoft.XMLHTTP”)
  } catch(oc) {
   xmlHttp = null
 if(!xmlHttp && typeof XMLHttpRequest != “undefined”) {
  xmlHttp = new XMLHttpRequest()
 return xmlHttp
// viewnav.js file
function drawViewNav() {
 // Do all our calculations to find out where we are in the view
 partialPages = totalDocs / docsPerPage;
 extraPage = (partialPages == Math.floor(partialPages)) ? 0 : 1;
 totalPages = Math.floor(partialPages) + extraPage;
 curPage = Math.floor(startDoc / docsPerPage) + 1
 // Figure out the number of the first and last pages to display on the navigator
 startLink = (curPage < (navRange + 1)) ? 1 : curPage – navRange;
 endLink = ((curPage + navRange) > totalPages) ? totalPages : curPage + navRange;
 // Start writing our menu 
 navHTML = “<div class=’nav’ align=’center’><table class=’navtable’ cellpadding=’0′ cellspacing=’0′><tr>”;
 // Write out the “First”, “Jump”, and “Previous” links when applicable
 if (startLink > 1) {
  navHTML = navHTML + buildLink(1, “First”);
  navHTML = navHTML + buildLink(curPage – (navRange + 1), “<<“);
 if (curPage > 1) navHTML = navHTML + buildLink(curPage – 1, “<“);
 // Generate all the page # links we want to display
 for (i = startLink; i <= endLink; i++) {
  if (i == curPage) {
   navHTML = navHTML + “<td class=’navtablecur’>” + i + “</td>”
  } else {
   navHTML = navHTML + buildLink(i, i);
 // Write out the “End”, “Next”, and “Jump” links when applicable
 if (curPage < totalPages) navHTML = navHTML + buildLink(curPage + 1, “>”);
 if (endLink < totalPages) {
  navHTML = navHTML + buildLink(curPage + (navRange + 1), “>>”);
  navHTML = navHTML + buildLink(totalPages, “Last”);
 // Close the list of links
 navHTML = navHTML + “</td>” 
 // Write out the “Page x of y” text and create input box
 //navHTML = navHTML + “<td class=’navpages’>Page “;
 navHTML = navHTML + “<td class=’navtablelink’>Page “;
 // input field for page number is disabled because this does not work for a categorized view
 //navHTML = navHTML + “<input title=\”Click to enter a new page number here\” onKeyUp=\”void(getPage(event, this))\” “;
 //navHTML = navHTML + “onClick=’this.select()’ onFocus=’this.select()’ type=’text’ value='” + curPage + “‘ />”;
 navHTML = navHTML + curPage ;
 navHTML = navHTML + ” of ” + totalPages + “</td>”;  
 // Close out the menu
 navHTML = navHTML + “</tr></table></div>”;
 // Write out the navigator
 document.getElementById(navDiv1).innerHTML = navHTML;
 if (typeof navDiv2 != “undefined”) document.getElementById(navDiv2).innerHTML = navHTML;
  document.getElementById(‘view’).style.display = ‘block’;

function buildLink(pageNum, text) {
 startLinkDoc = (((pageNum – 1) * docsPerPage) + 1);
 endDoc = (pageNum == totalPages) ? totalDocs : startLinkDoc + docsPerPage  // Check for last page when creating tooltip range
 linkHTML = “<td class=’navtablelink’ onmouseover=\”this.className=’navtablelink_on’\” onmouseout=\”this.className=’navtablelink’\” ”
 linkHTML = linkHTML + “title=’Page ” + pageNum + ” : Documents ” + startLinkDoc + ” through ” + endDoc + “‘ “;
 linkHTML = linkHTML + “onclick=document.location.href='” + viewAlias + “?OpenView&RestrictToCategory=”  + Categories + “&Start=” + startLinkDoc + “&Count=” + docsPerPage + “‘>” + text + “</td>”;
 return linkHTML;

function getPage(event, field) {
 var keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;
 if (keyCode == 13) {
  newPage = field.value;
  if (newPage > totalPages || newPage < 1) {
   return false;
   document.location.href = dbPath + viewAlias + “?OpenView&RestrictToCategory=”  + Categories + “&Start=” + (((newPage – 1) * docsPerPage) + 1) + “&count=” + docsPerPage

The variable Categories is a value I receive from one of the dialoglists on top of the screen. The screen is a frameset with 2 frames.

In the dialoglist I have a JS OnChange event opening the url in the bottom frame:

varURL = “web_internetRegionCat?OpenView&RestrictToCategory=” + varSelect + “&Count=” + varNumPage}

In the bottom frame I open a View via a ‘$$ViewTemplate for viewname’ Form. In the HTML Head section of the Form I calculate the variable Categories, via: