Draggable modal

For a project a request was to make a Bootstrap modal (a Dialog control in the Extension Library) as draggable. By default, the modal does not have that feature. So can you make it draggable again?

In xsp-mixin stylesheet the class xsp-responsive-modal is using !important for the left and top properties which prevents that the modal is draggable. A work-around can be achieved by replacing these class properties with your own that does not use !important.

To replace the default class add for the onShow event of the dialog:

<xe:this.onShow>
<![CDATA[
x$(“#{id:dialog1}”).removeClass(“xsp-responsive-modal”).addClass(“draggable-responsive-modal”);
]]>
</xe:this.onShow>

Your custom class could look as followed:

.draggable-responsive-modal {
display: block;
width: auto;
left: 0;
top: 0;
z-index: 1050 !important;
}

The x$() function is the infamous utility function from Mark Roden to work with JQuery.

I added the code to GitHub Gist: https://gist.github.com/PatrickKwinten/1d442e28ff0d59f8e01728bffab13e4f

Add 20 years of experience to your workforce

 

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

Building a search function with DataTables plugin (VI)

Introduction

Often business analists who work with the data in tables eat & sleep in their spreadsheet world so a common asked question is: can we export the data? “Ofcourse you can!” with the datatable component.

Modifications

Below is a short summary of the changed/added items.

Configuration

In order to add the buttons you specify in the datatable configuration which buttons you would like to have e.g.:

dom: ‘Bfrtip’,
buttons: [
‘copy’,
‘csv’,
‘excel’,
{
extend: ‘pdfHtml5’,
orientation: ‘landscape’,
pageSize: ‘LEGAL’,
title: ‘Person Details’
},
‘print’
]

 

Required files

Which files are required for the buttons feature depend on which buttons you would like to display. In the download builder you can specify for which options you would like to go.

AMD fix

An important thing to notice is that most JS files use AMD loading. There have been posted multiple questions and answers how to fix AMD loading with XPages. In my repository I have just removed the amd checking.

Files overview

Probably the files you would like to use will be listed in the Theme design element:

<!– adding buttons –>
<resource>
<content-type>application/x-javascript</content-type>
<href>DataTables/buttons-1.2.1/js/dataTables.buttons.js</href>
</resource>
<resource>
<content-type>application/x-javascript</content-type>
<href>DataTables/buttons-1.2.1/js/buttons.flash.js</href>
</resource>
<resource>
<content-type>application/x-javascript</content-type>
<href>DataTables/jszip-2.5.0/jszip.js</href>
</resource>
<resource>
<content-type>application/x-javascript</content-type>
<href>DataTables/pdfmake-0.1.18/build/pdfmake.js</href>
</resource>
<resource>
<content-type>application/x-javascript</content-type>
<href>DataTables/pdfmake-0.1.18/build/vfs_fonts.js</href>
</resource>
<resource>
<content-type>application/x-javascript</content-type>
<href>DataTables/buttons-1.2.1/js/buttons.html5.js</href>
</resource>
<resource>
<content-type>application/x-javascript</content-type>
<href>DataTables/buttons-1.2.1/js/buttons.print.js</href>
</resource>
<resource>
<content-type>text/css</content-type>
<href>DataTables/buttons-1.2.1/css/buttons.dataTables.min.css</href>
</resource>

 

Result

As a result you will see the buttons displayed above the table. If you apply filtering to the table the buttons will pick up the provided terms.

datatables06

A little less coding than an X-agent to create a PDF or spreadsheet🙂

A downsize might be that users are not aware of the amount of data that might reside in your datatable (the only x-number of entries are displayed). Pressing the PDF button with 40K of documents in your notes view takes a bit of time…

Building a search function with DataTables plugin (V)

Introduction

In the last post we moved the input fields out of the table and into a separate form. In this post I will include a nice usability feature for search term highlighting. This feature is particular helpful if you allow search in columns where values do not differ so much from each other like order numbers or article numbers.

Say hello to Mark()

Mark.js is a JavaScript keyword highlighter function. You can use it as pure JavaScript or jQuery plugin.

First let me show the end-result and then we will walk through the changes:

datatables05

Modifications

In order to enable Mark I made chances to the following items:

  • webcontent folder
  • theme
  • custom stylesheet
  • xpage
  • javascript library

In the webcontent folder I included the distributed files. I included only the jQuery plugin as a resource in the Theme document:

<resource>
<content-type>application/x-javascript</content-type>
<href>mark/jquery.mark.js</href>
</resource>

In the custom stylesheet I added a class definition to distinguish the searched term with some highlight:

span.markSearched {
background: yellow;
color: black;
}

In our xpage I added the name attribute for each input element e.g. name=firstname. This attribute will be used as reference to apply the Mark feature.

The main changes take place in the JavaScript. I have written a separate function for it so we do not need to call it on loading:

$(function() {
var db = $(“#persons”).DataTable();
db.destroy();
localStorage.clear();
// Map inputs with columns (nth-child(X))
var inputMapper = {
“firstname”: 1,
“lastname”: 2,
“company”: 3,
“job”: 4
};

// Initialize DataTables
var db = $(“#persons”).DataTable({

stateSave : saveState,
fixedHeader: true,
“language” : {
“lengthMenu” : “Entries per page _MENU_”,
// “info” : “Page _PAGE_ of _PAGES_”,
“infoEmpty” : “No entries found”,
“infoFiltered” : “”
},
scrollY : yScroll,
“ajax” : “api.xsp/Persons” ,
“columns” : [
{
data : “firstname”,
“defaultContent”: “<i>Not set</i>”
},{
data : “lastname”,
“defaultContent”: “<i>Not set</i>”
},{
data : “company”
}
,{
data : “job”
}
],

// Set elements per page
pageLength: 10,
// Disable entry selection
bLengthChange: false,
// Act on table rendering
drawCallback: function() {
// Iterate over all inputs (by mapper names)
$.each(inputMapper, function(colName, index) {
// Determine the entered keyword

var val = $(“input[name='” + colName + “‘]”).val();
// Determine the column related to the input
var $col = $(“.datatables-table tbody tr > td:nth-child(” + index + “)”);
// Remove marks in related column
$col.unmark({
“element”: “span”,
“className”: “markSearched”
});
// Mark in related column
$col.mark(val, {
// Define mark.js options (see https://markjs.io/)
“element”: “span”,
“className”: “markSearched”
});
})
}
});

// Trigger table redraw on search keyword change
$(“input”).on(“keyup change”, function() {
var db = $(“#persons”).DataTable();
var $this = $(this);
var val = $this.val();
var key = $this.attr(“name”);

// Search inside DataTable column
// subtract -1 because :nth-child starts with 1,
// DataTables with 0
db.columns(inputMapper[key] – 1).search(val).draw();
});

});

In the inputMapper variable we define the input fields and their order appearance in the table.

Instead the initComplete callback we will use the drawCallback function instead. The initComplete runs once, the drawCallback runs everytime the table is redrawn, e.g. when we enter search terms. For each input element we take the entered value and match it with values in the corresponding column. When a match is found a span element with the classname markSearched is applied.

Finally we register events for the input elements so if characters are entered or removed the table gets redrawn.

Wrap up

That’s it! I hope you like this feature. I wonder how easily you could do this with a viewPanel control…

 

 

 

Building a search function with DataTables plugin (III)

Introduction

In the previous post about the datatables jquery component I demonstrated how to create input select controls for each column. In some cases an select control may however not be desired, due to the diversity of the entries in the column (e.g. track number, firstname, date).

Text inputs

In such cases you probably wont to use text inputs where a substring is being matched against the string entries in the column.

To achieve this our function to build the table have to be adapted slightly:

function initPersons(){

$(‘#persons tfoot th’).each( function () {
var title = $(this).text();
$(this).html( ‘<input type=”text” placeholder=”Search ‘+title+'” />’ );
} );

var db = $(“#persons”).DataTable();
db.destroy();
localStorage.clear();
var table = $(“#persons”).DataTable( {
stateSave : saveState,
fixedHeader: true,
“language” : {
“lengthMenu” : “Entries per page _MENU_”,
// “info” : “Page _PAGE_ of _PAGES_”,
“infoEmpty” : “No entries found”,
“infoFiltered” : “”
},
scrollY : yScroll,
“ajax” : “api.xsp/Persons” ,
“columns” : [
{
data : “firstname”,
“defaultContent”: “<i>Not set</i>”
},{
data : “lastname”,
“defaultContent”: “<i>Not set</i>”
},{
data : “company”
}
],
initComplete: function(){
this.api().columns().every( function () {
var column = this;

$( ‘input’, this.footer() ).on( ‘keyup change’, function () {
if ( column.search() !== this.value ) {
column
.search( this.value )
.draw();
}
} );

} );
}
});
}

As a result under each column input boxes which column filter functionality are added underneath the columns:

datatables03

Another Graph sample from Domino Explorer

Introduction

In a previous post I dove into Domino Explorer, an XPages application that cans the Domino Directory and the Catalog by using the Graph capabilities in the OpenNTF Domino API.

In this post will describe another XPage in that application. Perhaps it helps to get a better picture on the Graph db and how to build an application around it using XPages, JavaScript, & Java.

Hopefully I will be ready writing before a European football final kicks off…

AllACL.xsp

The xpage I discuss is allacl.xsp. Basically it displays at start a table with ACL entries for the entire catalog. When I click on an entry or row in the table I get presented a list of applications where the selected ACL entry resides in the ACL.

The result of the scenario above captured in the following screen:

de_acl

As the image suggests the entry [Anonymous] resides in 4 applications. Let’s dive in a little deeper into the XPage to see how it’s done…

On document ready

The XPage contains a JS library whichs fires an AJAX call to collect the data for the DataTable object. The rest service resides on the XPage and is accessed via it’s pathinfo property. The restservice is bound to a custom servicebean which resides in the application.

The AJAX call does not provide a parameter to the restservice, so the java class will collect all the vertices of type DXACLEntry.class. For each vertice a JSONJavaObject containing name and level and placed in a JSONJAVAArray object. This array is placed in a JSONJavaObject and returned as a string to the restservice.

When the data is received in the DataTable object only the name property is placed.

The routing is visualized in the next image:

de_acl_rest

On row click

In the JS library for the DataTable object is also defined what should happen when a row in the table is clicked.

Here the name value for the selected row is used in a function that initiates a second DataTable object. Again a call is made to the same restservice. However now the name value is send as a parameter (“?name=” + name value).

The servicebean picks this parameter up (String name = request.getParameter(“name”)) and get’s all the vertices from the Graph with the provided name, matching the DXACLEntry (DXACLEntry aclEntry = graph.getElement(name, DXACLEntry.class)).

Similar as in the document ready event for each found DXACLEntry object a JSONJavaObject is created, now with some more properties (title,filepath, replicaId,server) and placed in a JSONJavaArray. This array is returned as a string to the restservice.

When the results are received by the DataTable object the four properties are displayed per column.

de_acl_rest2

Summary

The scenario is that simple. The DataTable plugin is really a great plugin that does a lot for you and can save you multiple design elements (view controls) by defining in your code what you want in it as result and in which order.

Now let me enjoy my evening of football with a cold beer! Happy development =)

 

 

prettytime for your XPages

Introduction

I guess most of us believe that not all users are robots, and especially in social applications where you try to approach humans as human friendly as possible the display of date and time in such format can be off added value.

Timeago

There are many scripts to display date and time in a human friendly format. For a project we are using timeago, a jQuery plugin that makes it easy to support automatically updating fuzzy timestamps (e.g. “4 minutes ago” or “about 1 day ago”).

However, in combination with an infinite scroll feature to navigate through document collections we notice that the script not always return instantly the transformed date/time.

That is why I looked at a server-side transformation.

PrettyTime

PrettyTime is an OpenSource time formatting library. Completely customizable, it creates human readable, relative timestamps like those seen on Digg, Twitter, and Facebook.

It turned out the implementation of PrettyTime was quiet simple. I will describe the steps for you.

Install the jar & call the Java class

Download the project jar file and place it in the WebContent \ WEB-INF \ lib folder.  For a computed field on a custom control in a repeat control I have as SSJS code to call the Java class:

<xp:text>
<xp:this.value><![CDATA[#{javascript:var theDate = compositeData.Date;
p = new org.ocpsoft.pretty.time.PrettyTime();
p.format(new Date(theDate))}]]></xp:this.value>
</xp:text>

As a result I get to date displayed as:

prettytime

Pro’s & Con’s

In comparison with client-side solutions I have detect some con’s and pro’s for a server side solution:

  • Static; the client side solutions are capably of updating the date/time value on the fly.
  • Speed; the server side generated human friendly date is there when a document row is presented.
  • Extensibility; the number of display languages is more numerous for most client-side solutions. Also you can often “configure” the text format when desired.
  • Consistency; having a human friendly date in a local language may be in conflict with internationalization of your XPages app since that might require more work.

Anyway: I happy to hear how you have solved the human friendly date issue.

Happy development!

Enforce HTTPS for my XPages

Sometimes it can be benefitial to enforce the https protocol. My use-case was that an XPages app was going to be opened from the mobile Connections app and just the HTTP protocol would case an additional login dialog to appear.

So the following SSJS code is what I came up with:

<xp:this.beforePageLoad><![CDATA[#{javascript:currURL = context.getUrl().getScheme();
if (currURL ==”http”){
var baseURL = context.getUrl().toString().toLowerCase();
safeURL = baseURL.replace(‘http’,’https’);
facesContext.getExternalContext().redirect(safeURL);
}
}]]></xp:this.beforePageLoad>

The code will check if the xpage is called with the http protocol, if so the same page is called but then with the https protocol enforced.

Since the application aggregates a lot of information from Notes views, which the application administrator can add, I also check if any URL’s with the http protocol are being displayed and transform them with the https protocol to reduce the first check:

<xp:eventHandler event=”onClientLoad” submit=”false”>
<xp:this.script><![CDATA[function toSSL() {
var current_host = location.host;
var hosts = [‘serverx.acme.com’,’servery.acme.com’,”serverz.acme.com’,current_host];
$.each( hosts, function( i, l ){
$(‘a[href*=”‘ + l +'”]’, ‘body’).each(function() {
var $a = $(this);
var href = $a.attr(‘href’);
if(href.indexOf(‘https’) == -1) {
var ssl = href.replace(‘http’, ‘https’);
$a.attr(‘href’, ssl);
}
});
});
};
$( document ).ready(function() {
toSSL();
});]]></xp:this.script>
</xp:eventHandler>

Hiding Notes links for Mobile Web users

Probably you are working in a collaborative environment where users access their beloved Notes apps with multiple clients: The Notes client, The Notes Browser plugin, Web browser and mobile devices.

Not all Notes applications have defined business cases in which they should become web-enabled. This could be because of application complexity or that the life-cycle / ownership of the application is poorly defined.

Nevertheless it is hard to prohibit that links to these applications or documents in them are being published on web-pages (probably you want to promote the distribution of information). Luckily there is that lovely tool called the IBM Notes Browser Plugin but this plugin is restricted to normal web browsers and not to all of them.

And then you have your ‘leading’ technology adapting usergroup that prefer to access Notes data in app and web-page form. For them the Notes Browser Plugin is not available so why should you bother to show them links to documents in applications that have not been web-enabled yet?

The following script will hide these Notes link on web-pages  and replace them with just text. It uses the deviceBean which provides an easy to use and easy to program way of identifying a popular range of mobile and tablet devices.

var isMobile = ‘#{javascript:return deviceBean.isMobile()}’;
if (isMobile==’true’){
$( document ).ready(function() {
$(“a[href^=’Notes://’]”).each(function () {
$(this).replaceWith($(this).text());
});
});
}

Add it to the onClientLoad event of your XPage.

Building a Live Search function with Domino Access Services & jQuery Tokeninput

Introduction

A live search is a function where get search results returned while you type. In this post I will describe how you can add such a feature using the jQuery Tokeninput plugin and perform a full text search with Domino Access Services (DAS)as your data provider.

But first let’s take a look at how the end result looks like:

searchres

As in other examples we will apply the live search to the infamous fakenames application. You can grab this directory application from Codestore.

Step 1 – Download the resources and add them to your XPage

Add the following resources to a custom control:

Step 2 – Add the filter and search field

In the result screenshot you noticed the following elements:

  • A filter (People, Groups, Holidays)
  • A search field (with typeahead).
  • A (formatted) result list.

Therefor add the following HTML on your custom control:

As you notice we will be using Bootstrap to make the appearance a bit more attractive.

search_param

The hidden input element search_param will be the container for our selected filter.  The following code will set the value:

livesearch-input

The Tokeninput plugin will inject an (initially empty) unordered result list and input field in the livesearch-input input element.

Include a function to initiate the Tokeninput plugin:

Step 3 – Activate the plugin

In order to activate the Tokeninput plugin you need to include a script block control on your custom control and ad the following script:

function getObjType() {
var val = $(“#search_param”).val();
return val;
}

var restPrefix = “./api/data/collections/name/”
var hintText = “Enter text, use asterix(*) for Fulltext wildcards”;
var searchingText = “Searching…”;
var queryParam = “search”;
var propertyToSearch = “name”;
var searchDelay = 2000;
var minChars = 2;
var resultsLimit = 5; //not working??
var noResultsText = “No matches”;
var tokenLimit = 1;
var preventDuplicates = true;
var onAdd = function(item) {
var objType = getObjType();
var docID = item[‘@unid’];
var URL = “”;
switch (objType) {
case “Groups”:
URL = “Group.xsp?unid=” + docID;
break;
case “Holidays”:
URL = “Holiday.xsp?unid=” + docID;
break;
default:
URL = “Person.xsp?unid=” + docID;
}
window.location = URL;
};
var resultsFormatter = function(item) {
var objType = getObjType();
switch (objType) {
case “Group”:
sub = “dummy group sub”;
break;
case “Holiday”:
sub = “dummy Holiday sub”;
break;
default:
sub = “” + item.job;
}
if (sub == “”) {
sub = “No title available“;
}
if (sub.length > 30) sub = sub.substring(0, 30) + “…”;
var name = item.name;
if (name.length > 30) name = name.substring(0, 30) + “…”;
var img = ‘‘;
var text = ‘

‘ + name + ‘

‘ + sub + ‘


return ‘

  • ‘ + ‘
    ‘ + img + text + ‘

‘;
};

function setPlaceHolder() {
$(“#token-input-livesearch-input”).attr(“placeholder”, “Enter text”);
}

function init() {
$(“#livesearch-input”).tokenInput(restPrefix + getObjType(), {
hintText: hintText,
searchingText: searchingText,
queryParam: queryParam,
propertyToSearch: propertyToSearch,
searchDelay: searchDelay,
minChars: minChars,
resultsLimit: resultsLimit,
noResultsText: noResultsText,
tokenLimit: tokenLimit,
onAdd: onAdd,
preventDuplicates: preventDuplicates,
resultsFormatter: resultsFormatter
});
setPlaceHolder();
}

function re_init(objType) {
$(“#token-input-livesearch-input”).remove();
$(“.token-input-list”).remove();
$(“#livesearch-input”).tokenInput(restPrefix + getObjType(), {
hintText: hintText,
searchingText: searchingText,
queryParam: queryParam,
propertyToSearch: propertyToSearch,
searchDelay: searchDelay,
minChars: minChars,
resultsLimit: resultsLimit,
noResultsText: noResultsText,
tokenLimit: tokenLimit,
onAdd: onAdd,
preventDuplicates: preventDuplicates,
resultsFormatter: resultsFormatter
});
setPlaceHolder();
}

function submitSearch() {
//not required, overwritten by onAdd function.
}

As you see the Tokeinput plugin can be steered via diverse parameters. A clear description of the available options you can read here.

In our case we determine:

Data Provider

var restPrefix = “./api/data/collections/name/”

This defines we will be using Domino Access Services as data provider. We will use the names of the views People, Groups, Holidays which correspond with the options in our filter list.

Remember you need to enable DAS for the database AND the views.

onAdd = function(item)

The variable onAdd is a function that will be called when we select an item from the result list. We have set to allow only one item to be selected and the onAdd function will open a new window location with the corresponding object/document.

URL = “Person.xsp?unid=” + docID;
}
window.location = URL;

resultsFormatter = function(item)

This variable defines how the items in the collection provided by Domino Access Services will be presented in the UI. You can be as creative with it as you like. I choose for the option for an image, distinguished name and a sub-title e.g. the function for a person.

Ready

Are we done already? Yes we are! All the heavy lifting is done by the Tokeninput plugin and Domino Access Services.

Notice that Full Text search in Domino Access Services supports the usage of an asterix. Otherwise you get only results returned with an exact match.

firebugUse a plugin for your browser to check the URL call and response to DAS.

Some final thoughts

Do I found the live search feature useful?

– Yeah. Especially in mobile web applications you want to avoid that a user has to go back to home navigation each and every time. And with large data-sets infinite scrolling is a good option.

Do I find the Tokeninput plugin valuable?

– I find the plugin easy to understand but I noticed not all properties are implemented:-/  An example of something what I expected to be implemented is the number of returned results (fixed to max 10 items).

Can I recommend the plugin?

– In case you use Bootstrap applying a bootstrap theme meant mostly stripping the default style sheet. In case you use the Application Layout control from the Extension Library you will discover some problems:

  • When applying the live search in the search bar you get overlay with the Utility links.
  • When applying a filter option you get scrolling issues when opening the dropdown menu in the navbar section.
  • The result list can not be displayed direct under the search field due to the overlay of the navbar and it’s margins.

The consequence of these problems above made me decide to place the search bar in the main column section instead of the search bar and align it to the right via the pull-right class in Bootstrap. The result looks similar but I loose some white space. (perhaps the solution lies in understanding Bootstrap a bit more).

In case you can live with these conditions or restrictions I think you have an awesome desired feature!

Your turn!

What are your thoughts? In case you have provide a live search feature via some other plugin or custom code I am happy to hear from you.

I have uploaded a sample on Dropbox. I am happy to hear the improvements you have made.

Happy coding =)

Use Hammer JS to add touch gestures to your XPage

Introduction

In a previous post I demonstrated how you can apply a side panel to your interface to provide additional navigation for your application using the Bootstrap UI.

Toggle versus Swipe

The solution was basically a form of toggle, initiated from an element in your UI. For demonstration purpose we choose to include a image (hamburger menu) in the navbar logo section. The downsize of that is that you get 2 similar icons if you include Utiliy links and a click does feel less intuitive than a swipe gesture on a mobile device. 701-one_finger_swipe So what are your options?

Hammer

If you search after javascript libraries fo touch or swipe gestures you will find a dozen. One famous is jQuery Mobile. But on top in my result list appears Hammer JS. While testing library examples I had one condition: it should work well with the Application Layout control from the Extension Library with the responsive layout behaviour. As it turned out, Hammer is pretty easy to integrate with this control.

Step 1 – Download the library

AMD and Dojo

In my first setup I noticed that the Hammer object was not available in the DOM. Thanks to Per Hendrik Lausten I got a quick reply on StackOverflow with a suggested solution. It turns out that AMD loading in the Hammer library conflicts with Dojo in XPages. From other posts on StackOverflow this seems to be a known problem. If you compare the number of questions regarding jQuery compared with Dojo perhaps IBM should consider to ditch this library but hurrah for backward compatibility. So in our local Hammer library we disable the AMD loading as suggested:

//if (typeof define == TYPE_FUNCTION && define.amd) { // define(function() { // return Hammer; // }); //} else if (typeof module != ‘undefined’ && module.exports) { if (typeof module != ‘undefined’ && module.exports) { module.exports = Hammer; } else { window[exportName] = Hammer; }

Step 2 – Set up your HTML structure

In my example we have 1 side panel and 1 main content section. My structure looks as followed:

Panel

The panel simply contains a number of list items:

and with a bit of CSS results as followed: panel The CSS:

.navigation { list-style: none; background: #eee; width: 200px; height: 100%; position: fixed; top: 50px; right: 0; bottom: 0; left: -200px; z-index: 0; }

So our panel is 200 pixels wide but is set at left-position -200px so we do not see it on the screen. The top-position is set to 50px which ensures it does not appear under the sticky navbar.

Main section

The main content section contains my Application Layout control from the Extension Library. You can set it up as you like. I chose a sticky navbar on the top. I filled the main column with some text and Boostrap panels. main The CSS is as followed:

.site-wrap { min-width: 100%; min-height: 100%; background-color: #fff; position: relative; top: -50px; bottom: 100%; left: 0; z-index: 1; padding: 4em; }

Step 3 – Create a Hammer instance and apply event listeners

In a scriptblock control we setup a new instance of a Hammer object, bind it to our main element (containing the Application layout control) and set event listeners for pan, tap and press gestures.

var main = document.getElementById(‘content’); var nav = document.getElementById(‘navigator’); //new instance var mc = new Hammer(main); // listen to events… mc.on(“panright”, function(ev) { main.style.left = “200px”; nav.style.left = “0px”; }); mc.on(“panleft tap press”, function(ev) { main.style.left = “0px”; nav.style.left = “-200px”; });

All set… go!

This is basically all you need. Include the Hammer JS and stylesheet in your resources and preview the xsp file in a browser. This is how the screen would look like when initially opened: normal   And this is how it looks like when you made a gesture to the right: open

Device emulator

While testing multiple libraries I experienced unexpected behavior in different browsers. I suggest you use a device emulator to minimize disillusion when viewing (worse: presenting) the end-result.

Animation effect

The appearance of the side panel is now pretty direct. You can easily apply some animation via jQuery.

var options = { duration: 150, easing: ‘swing’ }; mc.on(“panright”, function(ev) { $(main).animate({ left: “200”, opacity:0.4}, options); $(nav).animate({ left: “0”}, options); }); mc.on(“panleft tap press”, function(ev) { $(main).animate({ left: “0”, opacity:1}, options); $(nav).animate({ left: “-200”}, options); });

Download

You can find a download under the following link. The NSF contains the adapted Hammer library a CSS stylesheet, an XPage and a custom control. Feel free to try and modify it and send me the results😉