Detect touch gestures with Hammer – Improvement for scrolling

Intro

In a previous post I introduced the Hammer library as a tool to detect touch gestures in your Mobile XPage Application (MXA?).

Add touch gestures to your page.

Recognizers

In the sample I demonstrated the Pan gesture. But beside Pan you have other recognizers:

The downsize with the Pan recognizer is that there is no velocity detection like in the swipe recognizer. This makes the pan recognizer less “natural”.

Angle detection and scrolling

When users scroll on their mobile devices the angle in which they scroll is never 100% vertical or horizontal. Your thumb tends to go from left bottom to right top.

So in order to avoid the swipe recognizer kicks in unnecessary you can detect the angle the gesture makes. Here are the values for angles:

  • Values are between -180 and 180:
    • 0 means RIGHT
    • -90 means UP
    • 90 means DOWN
    • 180 means LEFT

If you decide to only perform an action on vertical gestures with a deviation of an angle of 15 degrees your altered code could like as followed:

var mc = new Hammer(main);
mc.get(“swipe“).set({ direction: Hammer.DIRECTION_ALL });
mc.on(“swipe”, function(eventObject) {
if(eventObject.angle > -15 && eventObject.angle < 15){
// SESAME OPEN
$(main).animate({
left: width-20, opacity:1}, options);
$(nav).animate({
left: “0”}, options);
}
if(eventObject.angle > 165 || eventObject.angle < -165){
// SESAME CLOSE
$(main).animate({
left: “0”, opacity:1}, options);
$(nav).animate({
left: -(width-20)}, options);
}
});

In this approach the user can scroll without the opening/closing of the left side menu to kick in unnecessary.

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 😉

JQM & Domino Data Service (3)

In a previous post I demonstrated how to populate a listview for jQuery Mobile from a Notes view via Domino Data Service. In this post I will show how to include a ‘More’ button to load additional loading of more items for the listview and display them.

Screenshot_1

For this you need to make changes in 2 places:

  • mobile.xsp
  • employeelist.js

employeelist.js

In the JavaScript file we need to modify the initial getEmployeeList function and include a new function to load more employees from the list. This function is called from the button on the employeeListPage.

function getEmployeeList() {
$.getJSON(serviceURL + ‘data/collections/name/People?count=20‘, function(data) {
$(‘#employeeList li’).remove();
employees = data;
$.each(employees, function(index, employee) {
var tmp = “”;
tmp+='<li>’;
tmp+='<a href=”mobile_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>’;
}
tmp+='</a></li>’;
$(‘#employeeList’).append( tmp );
});
$(‘#employeeList’).listview(‘refresh’);
});
}

function getMoreEmployeeList(num) {
var n = $(‘#employeeList li’).length;
$.getJSON(serviceURL + ‘data/collections/name/People?start=’ + n + ‘&count=’ + num, function(data) {
employees = data;
$.each(employees, function(index, employee) {
var tmp = “”;
tmp+='<li>’;
tmp+='<a href=”mobile_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>’;
}
tmp+='</a></li>’;
$(‘#employeeList’).append( tmp );
});
$(‘#employeeList’).listview(‘refresh’);
});
}

mobile.xsp

In the xsp file we add a button below the listview in the employeeListPage:

<div id=”employeeListPage” data-role=”page”>

<div data-role=”header” data-position=”fixed”>
<h1>Pretenders Directory</h1>
</div>

<div data-role=”content”>
<ul id=”employeeList” data-role=”listview”
data-filter=”true”>
</ul>
</div>
<xp:button value=”More…” id=”button1″>
<xp:this.attrs>
<xp:attr name=”data-role” value=”button”></xp:attr>
</xp:this.attrs>
<xp:eventHandler event=”onclick” submit=”false”>
<xp:this.script><![CDATA[getMoreEmployeeList(20)]]></xp:this.script>
</xp:eventHandler>
</xp:button>
</div>

 

Addition

You can make it nicer if you add instead the following link/button:

<a id=”btnMore” onClick=”getMoreEmployeeList()” data-role=”button” class=”ui-disabled”>More</a>;

And update the employeelist.js:

//var serviceURL = “http://dev1//apps/fakenames.nsf/api/&#8221;;
//use relative URL instead
var serviceURL = “./api/”;
var count = 20;
var employees;

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

function getEmployeeList() {
$.getJSON(serviceURL + ‘data/collections/name/People?count=’ + count, function (data) {
$(‘#employeeList li’).remove();
employees = data;
$.each(employees, function (index, employee) {
var tmp = “”;
tmp += ‘<li>’;
tmp += ‘<a href=”mobile_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>’;
}
tmp += ‘</a></li>’;
$(‘#employeeList’).append(tmp);
});
$(‘#employeeList’).listview(‘refresh’);
$(‘#btnMore’).removeClass(‘ui-disabled’);
//$(‘#btnMore’).attr(“disabled”, “disabled”);
//$(‘#button2’).attr(“disabled”, “disabled”);
});
}

function getMoreEmployeeList() {
$(‘#btnMore’).addClass(‘ui-disabled’);
var n = $(‘#employeeList li’).length;

var num = count;
//var num = $.getUrlVar(‘count’);
$.getJSON(serviceURL + ‘data/collections/name/People?start=’ + n + ‘&count=’ + num, function (data) {
employees = data;
$.each(employees, function (index, employee) {
var tmp = “”;
tmp += ‘<li>’;
tmp += ‘<a href=”mobile_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>’;
}
tmp += ‘</a></li>’;
$(‘#employeeList’).append(tmp);
});
$(‘#employeeList’).listview(‘refresh’);
$(‘#btnMore’).removeClass(‘ui-disabled’);
});
}

$.extend({
getUrlVars: function () {
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.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
},
getUrlVar: function (name) {
return $.getUrlVars()[name];
}
});

Then the More button get’s disabled / enabled during loading of the data

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:

Screenshot_1

 

Screenshot_2

 

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:

employeelist.js

var serviceURL = “http://dev1//apps/fakenames.nsf/api/&#8221;;

var employees;

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

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+='<li>’;
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>’;
}
tmp+='</a></li>’;
$(‘#employeeList’).append( tmp );
});
$(‘#employeeList’).listview(‘refresh’);
});
}

Walkthrough

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.

employeedetails.js

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

function displayEmployee(data) {
var employee = data;
console.log(employee);
$(‘#employeePic’).attr(‘src’, ‘http://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif&#8217;);
$(‘#fullName’).text(employee.FirstName + ‘ ‘ + employee.LastName);
$(‘#employeeTitle’).text(employee.Title);
$(‘#city’).text(employee.OfficeCity);
console.log(employee.officePhone);
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>’);
}
$(‘#actionList’).listview(‘refresh’);
}

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.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}

Walkthrough

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.

 

listview in JQuery Mobile via Domino Access Service

Here follows an quick example how to create a listview in JQM created with data from a view via DAS. As data source I have used the infamous Fake Names Address book which you can (still) can find on Codestore.

In the example you can navigate between the view (People view) and details (Person view).

people_view

person_view

I have not really find out (yet) how to add the next set of documents when you scroll down the list (to be continued, or drop the solution in the comments or send me an email).

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<xp:this.data>
<xp:dominoView databaseName=”apps\fakenames.nsf”
viewName=”People” var=”viewPeople”>
</xp:dominoView>
</xp:this.data>
<xp:this.resources>
<xp:script src=”/global/jquery/jquery.js” clientSide=”true”></xp:script>
<xp:script src=”/global/jquery/jquery.mobile.js” clientSide=”true”></xp:script>
<xp:styleSheet href=”/global.jquery.mobile.css”></xp:styleSheet>
</xp:this.resources>

<xp:scriptBlock id=”scriptBlock1″>
<xp:this.value><![CDATA[$(document).on(‘pageinit’, function () {

$.ajax({
url: ‘http://dev1//apps/fakenames.nsf/api/data/collections/name/People&#8217;,
type: ‘GET’,
async: true,
success: function (result) {
ajax.handleData(result);
},
error: function (request, error) {
alert(‘Network error has occurred please try again!’);
}
});
});

$(document).on(‘pagebeforeshow’, ‘#headline’, function () {
$(‘#movie-data’).empty();

$.each(movieInfo.result, function (i, row) {
if (row[“@position”] == movieInfo.id) {
$(‘#movie-data’).append(‘<li><img src=”https://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif”></li>&#8217;);
$(‘#movie-data’).append(‘<li>Name: ‘ + row.$17 + ‘</li>’);
$(‘#movie-data’).append(‘<li>Company’ + row.CompanyName + ‘</li>’);
$(‘#movie-data’).append(‘<li>UNID : ‘ + row[“@unid”] + ‘</li>’);
$(‘#movie-data’).listview(‘refresh’);
}

});
});

var movieInfo = {
id: null,
result: null
}

$(document).on(‘vclick’, ‘#movie-list li a’, function () {
movieInfo.id = $(this).attr(‘data-id’);
$.mobile.changePage(“#headline”, {
transition: “slide”,
changeHash: false
});
});

var ajax = {
handleData: function (result) {
movieInfo.result = result;
$.each(result, function (i, row) {
$(‘#movie-list’).append(‘<li><a href=”” data-id=”‘ + result[i][“@position”] + ‘”><img class=”yourProductSprite yourProductSprite-NoPhotoPerson32″ + src=”https://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif”/><h3>&#8217; + result[i].$16 + ‘</h3><p>’ + result[i].CompanyName + ‘ / ‘ + result[i].$12 + ‘</p></a></li>’);
});
$(‘#movie-list’).listview(‘refresh’);
}
}]]></xp:this.value>
</xp:scriptBlock>

<div data-role=”page” id=”home”>
<div data-theme=”a” data-role=”header”>
<h3>
Pretenders Directory
</h3>
</div>
<div data-role=”content”>
<div class=”example-wrapper” >
<ul data-role=”listview” id=”movie-list” data-theme=”a”>

</ul>
</div>
</div>
<div data-theme=”a” data-role=”footer”>
<h1>Fake Names – Codestore</h1>
</div>
</div><!– /page –>

<div data-role=”page” id=”headline”>
<div data-theme=”a” data-role=”header”>
<a href=”#home” class=”ui-btn-left” data-transition=”slide” data-direction=”reverse”>Back</a>
<h3>Person details</h3>
</div>
<div data-role=”content”>
<ul data-role=”listview” id=”movie-data” data-theme=”a”></ul>
</div>
</div><!– /details –>
</xp:view>

Updated Single Page Application Wizard walk-through

I have updated the walk through of the Single Page Application Wizard available in the Extension Library. You can find the document here. The document covers release 901v00_06.20140424-0600 which contains some improvements for the control e.g.:

  • Improved warnings were created to alert the user to missing or badly configured options.
  • Better defaults were created for certain Wizard options.
  • The Document Viewer label position was changed to “above.”

The default CRUD options are a big time saver, also the checkboxes for fields to display on a Document Viewer Page.

Single Page Application tutorial

After watching the video on YouTube on the Single Page Application wizard I became curious and decided to take a test and mobilize an application myself. I choose the infamous “fake names” application for this.

I have written a blog article/tutorial about it. Here you can read it: ‘Single Page Application Wizard‘.

Summary

With the Single Page Application control you can rapidly mobilize a Notes application. The wizard is intuitive although you need to understand some basics of mobile development.

Unfortunately the wizard is not complete and forces you to apply some coding.

Infinite Scroll

The infiniteScroll property for a Data View control is enabled by default in the Singe Page Application. This great new feature allows users to fetch new rows of data by scrolling down the list.

You are almost stupid if you disable this feature.

Recommendations

  • Ability to apply basic CRUD Tab Bar Buttons to a Document Viewer Application Page.
  • On a Document Viewer page it would be easier to add fields instead of removing most of all the fields that would otherwise be applied by default. In mobile apps the number of fields you want to display is probably less than in a Notes client application.
  • Ability to re-open the wizard after you have pressed ‘Finish’.
  • It would be great to have message popups when a user performs an action that he/she is not allowed to do. At the current stage there is default no mechanism for this. Examples: “You are not authorized to perform this action”, “You have insufficient access rights”.

m_mobile

m_people

m_person

jQuery Mobile 1.3 Panel Widget XPages demo (for download)

The last couple of days I have been ‘playing’ with jQuery Mobile and mostly with the panel widget.

I have made some kind of starter-kit (NSF) to build a great XPages app for tablets/smartphones. I have setup a subset of custom controls which accept properties. A simple form in Notes you can use to setup navigation for header, footer and a vertical menu.

You can download the (zipped) NSF on Dropbox.

Below you can see a sample result:

panelinxpages

Please let me know if you experience any problems or if you have suggestions for improvement. Happy coding!

jQuery Mobile in XPages- Panel Widget

jQuery Mobile 1.3 is recently released. This release is focused on elevating responsive web design (RWD) and brings lots of cool new widgets including panels, dual handle range sliders, and two different responsive table modes.

Panel widget

One of the most common mobile UI patterns you see today are Facebook-style panels that open to reveal a menu, form or other content.

The flexible panel widget  allows panels to be positioned on the left or right of the screen. To take this widget responsive, it’s easy to add media queries to make the panel stay open at wider widths instead of auto-closing when you click on the page content. A great demo can be found here.

Currently I am working on a sort of development template that will allow to quickly build an XPages app using the panel widget.

I noticed however some steps you need to take to prepare your XPage application:

  • xsp.html.doctype=html in the xsp properties file sets your doctype.
  • xsp.theme=<empty> in the xsp properties files disables server default theming.
  • createForm=”false” in the XPage will not create the form element. This is important otherwise your relative links will not work.

Below is a sample code you can copy & paste in your XPage to get started:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core&#8221; createForm=”false”>
<head>
<meta charset=”utf-8″ />
<meta name=”viewport”
content=”width=device-width, initial-scale=1″ />
<title>Panel fixed positioning – jQuery Mobile Demos</title>
<link rel=”stylesheet”
href=”http://view.jquerymobile.com/1.3.0/css/themes/default/jquery.mobile.css&#8221; />
<link rel=”stylesheet”
href=”http://view.jquerymobile.com/1.3.0/docs/_assets/css/jqm-demos.css&#8221; />
<link rel=”shortcut icon”
href=”http://view.jquerymobile.com/1.3.0/docs/favicon.ico&#8221; />
<link rel=”stylesheet”
href=”http://fonts.googleapis.com/css?family=Open+Sans:300,400,700&#8243; />
<script src=”http://view.jquerymobile.com/1.3.0/js/jquery.js&#8221; />
<script
src=”http://view.jquerymobile.com/1.3.0/docs/_assets/js/&#8221; />
<script src=”http://view.jquerymobile.com/1.3.0/js/&#8221; />
<style>
.nav-search .ui-btn-up-a { background-image:none;
background-color:#333333; } .nav-search .ui-btn-inner {
border-top: 1px solid #888; border-color: rgba(255, 255,
255, .1); } .nav-search .ui-btn.ui-first-child {
border-top-width: 0; background: #111; } .userform {
padding: .8em 1.2em; } .userform h2 { color: #555; margin:
0.3em 0 .8em 0; padding-bottom: .5em; border-bottom: 1px
solid rgba(0,0,0,.1); } .userform label { display: block;
margin-top: 1.2em; } .switch .ui-slider-switch { width:
6.5em !important; } .ui-grid-a { margin-top: 1em;
padding-top: .8em; margin-top: 1.4em; border-top: 1px solid
rgba(0,0,0,.1); }
</style>
</head>
<body>
<div data-role=”page” class=”jqm-demos ui-responsive-panel”
id=”panel-fixed-page1″>
<div data-role=”header” data-theme=”f”
data-position=”fixed”>
<h1>Fixed header</h1>
<a href=”#nav-panel” data-icon=”bars”
data-iconpos=”notext”>
Menu
</a>
<a href=”#add-form” data-icon=”plus”
data-iconpos=”notext”>
Add
</a>
</div><!– /header –>

<div data-role=”content” class=”jqm-content”>
<h1>Panel</h1>
<h2>Fixed positioning</h2>
<p>
This is a typical page that has two buttons in the
header bar that open panels. The left button opens a
left menu with the reveal display mode. The right
button opens a form in a right overlay panel. We
also set position fixed for the header and footer on
this page.
</p>
<p>
The left panel contains a long menu to demonstrate
that the framework will check the panel contents
height and unfixes the panel so its content can be
scrolled.
</p>
<h2>Responsive</h2>
<p>
To make this responsive, the panel stays open and
causes the page to re-flow at wider widths. This
allows both the menu and page to be used together
when more space is available. This behavior is
controlled by CSS media queries. You can create a
custom one for a specific breakpoint or use the
breakpoint preset by adding the
<code>class=”ui-responsive-panel”</code>
to the page container. We have added this class on
this demo page.
</p>

<a href=”./” class=”jqm-button” data-ajax=”false”
data-role=”button” data-mini=”true” data-inline=”true”
data-icon=”arrow-l” data-iconpos=”left”>
Back to Panels
</a>

<div data-demo-html=”#panel-fixed-page1″
data-demo-css=”true” />
<!–/demo-html –>

</div><!– /content –>

<div data-role=”footer” data-position=”fixed”
data-theme=”f”>
<h4>Fixed footer</h4>
</div><!– /footer –>

<div data-role=”panel” data-position-fixed=”true”
data-theme=”a” id=”nav-panel”>
<ul data-role=”listview” data-theme=”a”
class=”nav-search”>
<li data-icon=”delete”>
<a href=”#” data-rel=”close”>Close menu</a>
</li>
<li>
<a href=”#panel-fixed-page2″>Accessibility</a>
</li>
<li>
<a href=”#panel-fixed-page2″>Accordions</a>
</li>
</ul>
</div><!– /panel –>

<div data-role=”panel” data-position=”right”
data-position-fixed=”true” data-display=”overlay” data-theme=”b”
id=”add-form”>
<form class=”userform”>
<h2>Create new user</h2>
<label for=”name”>Name</label>
<input type=”text” name=”name” id=”name” value=””
data-clear-btn=”true” data-mini=”true” />
<label for=”email”>Email</label>
<input type=”email” name=”email” id=”status”
value=”” data-clear-btn=”true” data-mini=”true” />
<label for=”password”>Password:</label>
<input type=”password” name=”password” id=”password”
value=”” data-clear-btn=”true” autocomplete=”off”
data-mini=”true” />
<div class=”switch”>
<label for=”status”>Status</label>
<select name=”status” id=”slider”
data-role=”slider” data-mini=”true”>
<option value=”off”>Inactive</option>
<option value=”on”>Active</option>
</select>
</div>
<div class=”ui-grid-a”>
<div class=”ui-block-a”>
<a href=”#” data-rel=”close”
data-role=”button” data-theme=”c” data-mini=”true”>
Cancel
</a>
</div>
<div class=”ui-block-b”>
<a href=”#” data-rel=”close”
data-role=”button” data-theme=”b” data-mini=”true”>
Save
</a>
</div>
</div>
</form>

</div><!– /panel –>

</div><!– /page –>

<div data-role=”page” id=”panel-fixed-page2″>

<div data-role=”header” data-theme=”f”>
<h1>Landing page</h1>
</div><!– /header –>
<div data-role=”content” class=”jqm-content”>
<p>This is just a landing page.</p>
<a href=”#panel-fixed-page1″ data-role=”button”
data-inline=”true” data-mini=”true” data-icon=”back”
data-iconpos=”left”>
Back
</a>
</div><!– /content –>
</div><!– /page –>
</body>
</xp:view>