Xomino

Domino with the new improved X

Memorial Day, take a moment to remember

Posted by MarkyRoden on May 28, 2012

There is no greater sacrifice anyone can make than to die for your country in the name of defending freedom.

Ever country has people who have to made the ultimate sacrifice and given their life for their country.

I am very grateful that I am able to live my life because someone else chose to give theirs. If you think about it, and don’t take life for granted it is quite humbling.

Posted in Just Marky | Leave a Comment »

jQuery in Xpages – development tip – using Firebug Console

Posted by MarkyRoden on May 21, 2012

In this article I will demonstrate how using Firebug can significantly reduce your development time with jQuery (or dojo) when you are trying to use selectors. There is no doubt that that out of the box functionality provided by XPages is very powerful, but to accomplish the capabilities we all know (and love?) the accompanying HTML is complex and not necessarily easy to navigate manually.

For more information on FireBug check this out

Things are busy at home and at work so I won’t have a plugin demonstration this week – just a development tip

Introduction
As I have discussed in this blog, using dojo and JQuery selectors are a very powerful way to facilitate “enhancing” your user experience with some nice UI changes. Many times however this can be a frustratingly slow process if you are doing trial and error development something like this:

  • Write your jQuery selector code in the XPage source panel
    • Save
    • Test In Webpage
      • Nothing happens
  • Change your jQuery code is XPage source panel
    • Save
    • Test in Webpage
      • Nothing happens
    • Curse
  • Change your jQuery code in XPage source Panel
  • Remember what it was like in the good old pre-XPages days
  • *sigh blissfully*
  • Curse
  • continue…..

Using the FireBug console we can significantly speed up this process by enacting real time changes to the webpage and not creating the XPage code until we know we have good working JavaScript.

Example

Here is a sample page with a typeAhead and a ViewPanel

typeAhead and ViewPanel

typeAhead and ViewPanel

and here’s our firebug console with a simple piece of jQuery code to change the css of all input fields, giving them a red border

  • Enter the text
  • Hit run
  • And there we have it – nothing happened ?!?
  • Curse
  • #FAIL :(
fail

fail

What happened?

Well if you look over on the left you can see the jQuery object which has been returned

jQuery object returned

jQuery object returned

Clicking on it shows us the element and look it DID get set……..

Viewing the field through firebug

Viewing the field through firebug

What’s happening is that the INPUT we see is actually masked by DIV styles which are pegged as “!important” in the stylesheet and over-ride the inline style :( Can you imagine how long it would have taken you to figure that out just by looking at the code? Using FireBug has given us a quick insight into what’s going on….so if we want a red field then we have to traverse up the Document Model (DOM) tree and color the DIV containing the INPUT field…..

As you can see below I tried a few examples but could not get the field to color – and then I took too many parents and made all the DIVs red. it is also a fascinating way to see in real time how the jQuery DOM navigation works…..still I don’t have what I was looking for – a red field on its own :(

Too many Red DIVS

Too many Red DIVS

Starting again if you look at the HTML in the DOM you can see what needs to be selected and we can make it red….select the DIV with the id ending in inputText1

  • yay !
A Red field!

A Red field!

Now of course you wouldn’t do this in real life – you would set a class and/or style on the field in the XPage client but this is a demonstration ;)

Coloring the viewPanel

We can use a CSS3 selector to style the nth column of the viewPanel – but unfortunately this does not work in IE

	<style>
		TABLE[id$='viewPanel1'] td:nth-child(3) {background-color: yellow}
	</style>

CSS fail in IE

CSS fail in IE

But jQuery rocks browser incompatibility issues and we can use firebug to get the right  jQuery and then apply it through the code to work in IE. Yes we used firebug to make sure the jQuery works – but this is jQuery and it is BUILT to overcome browser incompatibility issues. Instead of using the CSS3 nth-child selector, jQuery detects IE and trverses the table manually looking for the nth child in a loop – this is inefficient but the final effect is the same.

This is a big reason why you should use a library like jQuery or dojo – they were designed to help get around browser incompatibility problems.

jQuery CSS selector

jQuery in the firebug console

	<script>
		$("TABLE[id$='viewPanel1'] td:nth-child(3)").css('background-color', 'green')
	</script>

jQuery rocks around IE CSS support failure

 

Conclusion

I hope you enjoyed this quick(ish) tip. This only scratches the surface on FireBug and there is SO much more to learn/discuss !

 

Posted in jQuery, XPages, jQuery in XPages, Tools, FireBug | Tagged: , , , | Leave a Comment »

jQuery in XPages #10 – JQVMAP (Vector Maps)

Posted by MarkyRoden on May 15, 2012

In this article I will demonstrate how to implement the JQVMAP jQuery plugin into an XPage. There are 4 maps to chose from but users are able to create their own and add more maps if they need to. In addition to the out of the box functionality I have added some XPage lookup data to demonstrate the integration potential. As I have written this article ideas and thoughts have come at me quick and fast and this is much larger than I had originally intended. If you need maps on your website this is a fantastic flexible solution.

JQVMAP

Demonstration

The XPages integration of JQVMAP is demonstrated here

This capability does not yet work properly in IE – Chrome/Safari/Firefox only.

Download

The demonstration database can be downloaded from the link above or from here

Introduction

JQVMAP is a jQuery plugin based on the original jVectorMap plugin. The plugin provides an abundance of parameterized options for the developer to create their own maps, add color, add interactivity and provide a very slick solution to a broad requirement of many customers, to provide mapping capabilities to their website.

JQVMAP in an XPage

Users are able to select either a country or state and pertinent information regarding their selection will be displayed on the page.

How does it work?

I added jQuery and the relevant map files to my database’s WebContent folder.
They are referenced in the XPage as follows:

	<xp:this.resources>
		<xp:script src="js/jquery-1.7.1.min.js" clientSide="true"></xp:script>
		<xp:script src="js/jquery.vmap.js" clientSide="true"></xp:script>
		<xp:script src="js/vmap/jquery.vmap.world.js" clientSide="true"></xp:script>
		<xp:script src="js/vmap/jquery.vmap.sampledata.js"
			clientSide="true"></xp:script>
		<xp:styleSheet href="css/jqvmap.css"></xp:styleSheet>
	</xp:this.resources>

Adding a Map

Implementing the basic JQVMAP is as simple as adding the following jQuery to your XPage

	jQuery(document).ready(function() {
		jQuery('#vmap').vectorMap({
		    map: 'germany_en'
		});
	});

There are many paramaters which can be added to the map, most of which are self explanatory. For a full list of all the parameters available check out the project on github.

Colorizing the map

The example comes with a sampledata.js file which contains 2010 GDP information and is formatted like this:

var sample_data = {“af”:”16.63″,”al”:”11.58″,”dz”:”158.97″,”ao”:”85.81″,”ag”:”1.1″,”ar”:”351.02″,”am”:”8.83″,”au”:”1219.72″,”at”:”366.26″,”az”:”52.17″,”bs”:”7.54″,”bh”:”21.73″,”bd”:”105.4″,”bb”:”3.96″,”by”:”52.89″,”be”:”461.33″,”bz”:”1.43″,”bj”:”6.49″,”bt”:”1.4″,”bo”:”19.18″,”ba”:”16.2″,”bw”:”12.5″,”br”:”2023.53″,”bn”:”11.96″,”bg”:”44.84″,”bf”:”8.67″,”bi”:”1.47″ etc etc

When the map is built the data is passed into the plugin via these parameters

		    values: sample_data,
		    scaleColors: ['#C8EEFF', '#006491'],

The plugin then determines the color scale between the two passed in color parameters and scales each region based on the data provided. And quite frankly that rocks, bigtime! Changing the scale colors can do this to your maps and is very effective if you are looking to show coal usage or rainforest or anything where color enhances the information

Colorizing the map

Colorizing the map

So where’s the XPage stuff?

I have added some XPage functionality to the world map in two ways – a partial refresh and a REST service.

As the user mouses over a region there is a callback to the REST service to provide me some JSON from the database. Adding the REST service creates the JSON from the vwCountryDoc view

<xe:restService id="restService1" pathInfo="countryLookup">
 <xe:this.service>
 <xe:viewJsonService viewName="vwCountryDoc" var="entry"
 contentType="text/plain" count="1" defaultColumns="true">
 <xe:this.keys>
 <![CDATA[#{javascript:return facesContext.getExternalContext().getRequest().getParameter("code")}]]>
 </xe:this.keys>
 </xe:viewJsonService>
 </xe:this.service>
 </xe:restService>

creates http://demo.xomino.com/xomino/jQinX.nsf/xJQVMAP.xsp/countryLookup?code=gb

  {
      "@entryid":"77-A19E1314CB4B2C0B852579FE0047FF23",
      "@unid":"A19E1314CB4B2C0B852579FE0047FF23",
      "@noteid":"505E",
      "@position":"77",
      "@siblings":248,
      "@form":"CountryDoc",
      "CountryCode":"GB",
      "Name":"United Kingdom",
      "POP":"62,262,000",
      "Areakm":"242,900",
      "Aream":"93,800",
      "Life":"79.4",
      "HighestPeak":"Ben Nevis\t1,344 m (4,409 ft)\t"
  }

We add onRegionOver and onRegionOut callbacks to the map creation and with that we are able to read the JSON, parse the information (building a table) into a hidden span, and show it/hide it depending on the callback.

		jQuery('#vmap').vectorMap({
		    map: '#{javascript: getComponent("comboBox1").getValue()}',
		    backgroundColor: '#333333',
		    color: '#ffffff',
		    hoverOpacity: 0.7,
		    selectedColor: '#666666',
		    enableZoom: true,
		    showTooltip: false,
		    values: sample_data,
		    scaleColors: ['#C8EEFF', '#006491'],
		    normalizeFunction: 'polynomial',
		    onRegionOver: function (event, code, region){
		    	url="xJQVMAP.xsp/countryLookup?code="+code;
	    		//get the REST stream
	    	   	$.getJSON(url,
		    		function (data){
				    	temp=region;
				    	temp=temp.replace(/ /g, '_');
				    	var theTable='<table cellpadding=2 border=0 class="hoverTable"><tr><td>'
				    	theTable += '<img src="http://www.flagslist.com/clist/flags/'+temp+'.png" height=30 width=60 border=0/></td>'
		    			theTable += "<td><h1 style='color: white'>"+data[0].Name+"</h1></td></tr>"
		    			theTable += "<tr><td>Population</td><td>"+data[0].POP+"</td></tr>"
		    			theTable += "<tr><td>Life Expectency</td><td>"+data[0].Life+"</td></tr>"
		    			theTable += "<tr><td>Area km/<sup>2</sup></td><td>"+data[0].Areakm+"</td></tr>"
		    			theTable += "<tr><td>Highest Peak</td><td>"+data[0].HighestPeak+"</td></tr>"
			    		theTable+= "</table>"
			    		$(".hoverShow").html(theTable).css('display', 'block')
				    	console.log(theTable)
			    	});
		   		},
		   	onRegionOut: 	function(element, code, region)
		   		{
		   			$(".hoverShow").html('&amp;nsbp;').css('display', 'none')
		   		},
		    onRegionClick: function(element, code, region)
			    {
			    	//message only relevent to the world map
			    	var temp="#{javascript: getComponent("comboBox1").getValue()}"
			    	if (temp != "world_en"){ return false}
				    var message = 'You clicked "'
			            + region
			            + '" which has the code: '
			            + code.toUpperCase()
			            + "\n\n2010 GDP:"
			            + sample_data[code];
			        alert(message);
			        sTemp='<img src="http://www.flagslist.com/clist/flags/'+region.replace(/ /g, '_')+'.png" height=30 width=60 border=0/>'
			        $("[id$=code1]").val(code);
			        $('#flag').html(sTemp)
			        $("[id$=button1]").click()
			    }
		});
	});

And this creates the Top Left table in the example

Displaying information on the fly using JSON REST data

Displaying information on the fly using JSON REST data

You will see there is an onRegionClick event as well. When the user clicks on the region I am triggering a hidden button to be clicked and that runs a partiaRefresh on the bottom left section which displays information from the database via computed fields. This also pulls data from the sampledata.js and displays the GDP value

			<xp:table id="countryData" style="width: 700px" cellpadding="5">
				<xp:tr>

					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td style="width: 100px"></xp:td>
					<xp:td>
						<div id="flag" style="width: 200px">
							<xp:text escape="false" id="computedField4">
								<xp:this.value escaped="false">
									<![CDATA[#{javascript:var temp = getComponent("code1").getValue();
var temp1 = @DbLookup(@DbName(), "vwCountryDoc", temp, 2);

return '<img src="http://www.flagslist.com/clist/flags/'+temp1.replace(/ /g, '_')+'.png" height=30 width=60 border=0 />'
}]]>
								</xp:this.value>
							</xp:text>

						</div>
					</xp:td>
				</xp:tr>
				<xp:tr>
					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td>Country Code:</xp:td>
					<xp:td>
						<xp:inputText id="code1" defaultValue="GB"
							style="border: 1px solid white">

						</xp:inputText>
					</xp:td>
				</xp:tr>
				<xp:tr>
					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td>Name:</xp:td>
					<xp:td>
						<xp:text escape="true" id="computedField1">
							<xp:this.value>
								<![CDATA[#{javascript:var temp = getComponent("code1").getValue();
				@DbLookup(@DbName(), "vwCountryDoc", temp, 2)}]]>
							</xp:this.value>
						</xp:text>
					</xp:td>
				</xp:tr>
				<xp:tr>
					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td>Population</xp:td>
					<xp:td>
						<xp:text escape="true" id="computedField2">
							<xp:this.value>
								<![CDATA[#{javascript:var temp = getComponent("code1").getValue();
				@DbLookup(@DbName(), "vwCountryDoc", temp, 3)}]]>
							</xp:this.value>
						</xp:text>
					</xp:td>
				</xp:tr>
				<xp:tr>
					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td>Area</xp:td>
					<xp:td>
						<xp:text escape="true" id="computedField3">
							<xp:this.value>
								<![CDATA[#{javascript:var temp = getComponent("code1").getValue();
				@DbLookup(@DbName(), "vwCountryDoc", temp, 4)}]]>
							</xp:this.value>
						</xp:text>
					</xp:td>
				</xp:tr>
			</xp:table>

This builds the popup and bottom left information

Reading JSON data deom a js file and using a partialRefresh to display information

Reading JSON data from a .js file and using a partialRefresh to display information

CallBack events
Because of the callback events we are also able to add/display change other data on the screen. Taken directly from the site we have the following events available.

  • onLabelShow function(element, label, code)
    • Callback function which will be called before label is shown. Label DOM object and country code will be passed to the callback as arguments.
  • onRegionOver function(element, code, region)
    • Callback function which will be called when the mouse cursor enters the region path. Country code will be passed to the callback as argument.
  • onRegionOut function(element, code, region)
    • Callback function which will be called when the mouse cursor leaves the region path. Country code will be passed to the callback as argument.
  • onRegionClick function(element, code, region)
    • Callback function which will be called when the user clicks the region path. Country code will be passed to the callback as argument.
Conclusion
This article grew and grew as I was writing it, the possibilities for this capability are astounding and to think that it is free (MIT license) and takes less than an hour to get up and running it is phenomenal. I have really enjoyed making this article and believe me when I say it could have been a lot longer :)

Demonstration

The XPages integration of JQVMAP is demonstrated here

Download

The demonstration database can be downloaded from the link above or from here

Posted in jQuery, jQuery in XPages, XPages | 3 Comments »

XPage keyboard shortcut between design and source tab

Posted by MarkyRoden on May 10, 2012

I just discovered this by sheer accident – the best discoveries are like that. I am using R8.5.3 if it matters…

CTRL-SHIFT-PageDown (or PageUp) will move you from Design Tab

XPage Design Tab

XPage Design Tab

To Source Tab

XPages source Tab

XPages source Tab

AWESOME !!!

Productive!

Posted in Productive or Lazy?, XPages | 4 Comments »

jQuery in XPages #9 – PageSlide

Posted by MarkyRoden on May 8, 2012

In this article I will describe how to implement and provide examples of use the jQuery plugin PageSlide within an XPage. This is a fairly simple plugin with very few parameters but provides an alternate method for displaying information to users

PageSlide.js

Demonstration

The XPages integration of PageSlide is demonstrated here

Download

The demonstration database can be downloaded from the link above or from here

Introduction

PageSlide by Scott Robin is a simple plugin which allows external content to be displayed in a sidebar on the page. There are only two parameters, direction (left or right) and modal (true or false).  It is really quite simple. As you will see my implementation does not use XPage XML markup to add resources or to activate the capability. I found that this caused issues in the browser. The capability works well using the example code from the website, applied in an XPage.

PageSlide showing data from another XPage to the side of the main

PageSlide showing data from another XPage to the side of the main

How does it work?

I added jQuery, the pageslide js library and the pageslide.css stylesheet to my database through the WebContents folder and added them to to my XPage via script and link tags.

		  <script src="js/jquery.pageslide.js"></script>
		  <script src="js/jquery-1.7.1.min.js"></script>
		  <link rel="stylesheet" type="text/css" href="css/jquery.pageslide.css" />

The basic concept works like this:

  • You create a link on the page
    • < a href=”xSomething.xsp” class=”aClass”>
  • You select the link in question
    • $(“a.aClass”).
  • You pageslide it
    • pageslide({direction: “left”, modal: false})

	<a href="xPageSlide_1.xsp" class="myClass">Display user information</a>
	<script>$("a.myClass").pageslide({ direction: "left", modal: false});</script>

When clicking on the link this produces the effect of opening xPageSlide_1.xsp in the right hand panel of the screen (direction: left moves everything to the left).
If the modal = true paramater is included then the “slide” can only be closed programmatically. This forces user interaction with the information. If not included or modal = false then clicking anywhere in the screen will make the slide disappear.

And that is pretty much it….. nothing complicated this week :)

More examples
I created a custom control with custom parameters so that the capability can be added to any page.

Custom Control properties

Custom Control properties

The example page contains a demo of this custom capability and it is build like this:

  • A link with a dynamic href is added to the form along with
  • A scriptBlock which adds the PageSlide capability to the link passing the custom parameters as necessary.

<xp:link escape="true" text="CompositeData example"
	id="compositeDataExample"
	value="${compositeData.content}" styleClass="marky">
</xp:link>
<xp:scriptBlock id="scriptBlock1">
	<xp:this.value><![CDATA[
		bModal = "#{compositeData.modal}"
		bModal == "false" ? false : true
		$("[id$=compositeDataExample]").pageslide(
			{ 	direction: "#{compositeData.direction}",
				modal: bModal
			});
	]]></xp:this.value>
</xp:scriptBlock>

The other example I created involves using sessionScope variables to look up information dynamically.

sliding in a new XPage with dynamic content

sliding in a new XPage with dynamic content

There is a hidden anchor tag on the form which is has PageSlide attached to it in a similar way as described above. It is hidden becasue we are going to “click” it programmatically when a user is selected in the comboBox.

<a href="xPageSlide_1.xsp" class="marky2" style="display:none">Display user information</a>
<script>$("a.marky2").pageslide({ direction: "right", modal: true });</script>

The combobox has a simple SSJS lookup formula showing the first column of the AllContacts view
#{javascript:@DbColumn(“”, “AllContacts”, 1)}

<xp:comboBox id="comboBox1" defaultValue="Pick a person">
	<xp:selectItem itemLabel="Pick a person" itemValue="Pick a person"></xp:selectItem><xp:selectItems>
		<xp:this.value><![CDATA[#{javascript:@DbColumn("", "AllContacts", 1)}]]></xp:this.value>
	</xp:selectItems>
	<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="computedField1">
	</xp:eventHandler>
</xp:comboBox>

In the onChange event I perform a partial refresh of the computedField1. computedField1 looks like this and returns some JavaScript which triggers the link “.click()”

<xp:text escape="false" id="computedField1">
	<xp:this.value><![CDATA[#{javascript:var person = sessionScope.person=getComponent("comboBox1").getValue();
		var disp = "<script>$('a.marky2').click()</script>";
		disp2 = (person == "Pick a person" ? "" : disp);
		return disp2
	}]]>
	</xp:this.value>
</xp:text>

  • If a person is picked the computed field is refreshed
    • The computed field sets a sessionScope variable based on the value selected
      • var person = sessionScope.person=getComponent(“comboBox1″).getValue()
    • The refreshed value contains a script tag which executes when the refresh is loaded.
      • var disp = “// “;
        disp2 = (person == “Pick a person” ? “” : disp);
        return disp2
    • $(‘a.marky2′).click() clicks the hidden link opening the page slide

The xPageSlide_1.xsp page takes the sessionScope variable and looks up the user’s information based on the variable set in the computedField1 above.

<xp:div id="div1" style="display:nonex">
				    <h2 style="color: white">User Information</h2>
				    <xp:br></xp:br><xp:br></xp:br>
					<xp:text escape="true" id="computedField1"><xp:this.value><![CDATA[#{javascript:var per = sessionScope.person;
@DbLookup(@DbName(), "vwContacts", per, 4)}]]></xp:this.value></xp:text>

					<xp:br></xp:br>
					<xp:br></xp:br>
					<xp:text escape="true" id="computedField5"><xp:this.value><![CDATA[#{javascript:var per1 = sessionScope.person;
@DbLookup(@DbName(), "vwContacts", per1, 5)}]]></xp:this.value></xp:text>
					<xp:br></xp:br>
					<xp:br></xp:br>
					<xp:text escape="true" id="computedField3"><xp:this.value><![CDATA[#{javascript:var per2 = sessionScope.person;
@DbLookup(@DbName(), "vwContacts", per2, 6)}]]></xp:this.value></xp:text>
					<xp:br></xp:br>					<xp:br></xp:br>
										<xp:button id="button1" value="Close" style="display: block">
		<xp:eventHandler event="onclick" submit="false">
			<xp:this.script><![CDATA[parent.$.pageslide.close()]]></xp:this.script>
		</xp:eventHandler></xp:button><xp:br></xp:br>

</xp:div>

and with a little styling we are done for a consistent look and feel

<style>
DIV {
    background-color: #333333;
    color: #FFFFFF;
    height: 100%;
    padding: 20px;
    position: fixed;
    top: 0;
    width: 260px;
    z-index: 999999;
}

H2 { color: white }
</style>

Conclusion

I could easily see myself using PageSlide if I wanted to use a hidden menu or if I wanted to have a status update bar running in the background of my webpage which was accessed and made visible to the user when they wanted it to be there.

This is certainly the simplest plugin I have written about, but this is a very elegant solution to displaying hidden information to a user on demand.

Demonstration

The XPages integration of PageSlide is demonstrated here

 

Posted in Just Marky | 1 Comment »

Dynamically Expanding XPage viewPanel Rows on mouseover

Posted by MarkyRoden on May 7, 2012

In this article I will demonstrate a technique for dynamically expanding and contracting viewPanel rows. This provides a nice clean interface for the user and allows content to be made visible as necessary. This article also highlights many of the core jQuery capabilities.

Problem

When moving from a notes client application to the web often times view columns can be large and unwieldy

A boring notes view

A boring notes view

Unwieldy data view on an XPage

Unwieldy data view on an XPage

Solution

What we are going to do is not only neaten up the display but provide some simple user interactivity to allow access to data as needed. Here is a quick video of the capability in action.

Demonstration

You can see a working example of the capability on the xomino site. In Firefox and Chrome the fade effect look just fine but due to issues with alpha filters fade does not work consistently in IE.

How does it work?

As you can see from the pictures above, once we put our view into an xPage and display it on the web, the entire contents of the data field are displayed on the page. This can make for an irritating session with customer who don’t want to have to scroll page after page to find their information but want the same view to be available through the notes client.

Once solution would be to determine the medium through which your data is being viewed and truncate it accordingly. But I went another route and made a compromise where the data is available if the users wants it (adding functionality to the page without them having to click and open the document) but does not take up a large amount of space.

I added jQuery and jQueryUI (CSS not code) to my database through the WebContent folder and to my XPage like this

	<xp:this.resources>
		<xp:script src="js/jquery-1.7.2.min.js" clientSide="true"></xp:script>
		<xp:styleSheet
			href="css/custom-theme/jquery-ui-1.8.19.custom.css">
		</xp:styleSheet>
	</xp:this.resources>

I started out by creating a data source of my view and dragging the fields to the XPage, creating a viewPanel on the form. I then “fixed” the width of someData column:

		<xp:viewColumn columnName="somedata" id="viewColumn4">
			<xp:this.facets>
				<xp:viewColumnHeader value="Somedata" xp:key="header" style="width:300px"
					id="viewColumnHeader4">
				</xp:viewColumnHeader>
			</xp:this.facets>
		</xp:viewColumn>

I then created a scriptblock on the XPage after the viewPanel (controls > other > core controls >scriptblock). Within all Properties > data > value I added the following code….

			 $("#[id$='viewPanel1'] tr td:nth-child(4)").each(function (i) {
			 	if ($(this).innerHeight()>=100){
				 	$(this).append('<span class="ui-icon ui-icon-arrowthick-1-s"></span>').children(":first").wrap('<div class="wrapper" />')
			 	}
		    });

and that on its own made quite a difference….

modified viewPanel

modified viewPanel

This is what the jQuery code just did

  • Select all the 4th column Table cells in the viewPanel and cycle through each of them

 //select all elements (one in this case) where the id ends with (id$=) viewPanel1
 //within that select all rows (tr)
 //within that select all the table cells (td)
 //within that select only the cells which are the 4th child element (:nth-child(4))
 //cycle through each of them (.each(function(i)) where i is the nth item in the cycle
 $("#[id$='viewPanel1'] tr td:nth-child(4)").each(function (i)

  • Because we have fixed the column width we are able to accurately ascertain the height of the cell contents on the page
  • As we then cycle through the viewPanel TD cells we determine if the cell contents are larger than 100pixels. If they are then we surround the contents with a wrapper with a fixed height.

 //$(this) means each jQuery object (representing the TD as we cycle through the column
 //.append is as it sounds - append to the end this SPAN (with the arrow in it)
 //.children(:first) selects the first SPAN within the TD we are working on and
 //.wrap means surround the SPAN with the DIV
	$(this).append('<span class="ui-icon ui-icon-arrowthick-1-s"></span>').children(":first").wrap('<div class="wrapper" />')

ok trying to visualize what is going on we start with something which looks like this

<td>
  <span #1>
  </span>
</td>

We .append() another span

<td>
  <span #1>
  </span>
  <span #2>
  </span>
</td>

We then .wrap() the :first Child of the TD with a Div

<td>
  <div>
    <span #1>
    </span>
  </div>
  <span #2>
  </span>
</td>

And this is what we have made (looking at it through FireFox FireBug

The new Table Cell viewed in Firebug

The new Table Cell viewed in Firebug

You have to admire the ability to “chain” jQuery objects and methods together :)

Styling the wrapper

We add the following stylesheet to the XPage to ensure that the DIV .wrapper class keeps the display in check

	<style>
		.wrapper {
			height: 50px;
			overflow: hidden;
			padding-bottom: 5px;
		}
	</style>

Animating the Table Rows

So once we now have our reformatted table rows they look better but anything over 50px cannot be “read” as it is hidden.

What we are going to do is add an mouseenter and mouseleave event to the DIV class=wrapper we just added inside our table cell.

Here’s the breakdown in English and the code beneath it

on mouse enter

  • Select all table rows which are NOT the one we are currently over
    • Fade the whole row to 20%
  • Get the SPAN next in the DOM below the wrapper DIV (the one with the down arrow in it)
    • Toggle OFF the down arrow class (ui-icon-arrowthick-1-s)
    • Toggle ON the up arrow class (ui-icon-arrowthick-1-n)
  • Animate the height of the TD to match the height of the .wrapper

on mouse leave

  • Select all table rows which are NOT the one we are currently over
    • Fade the whole row back to 100%
  • Get the SPAN next in the DOM below the wrapper DIV (the one with the up arrow in it)
    • Toggle ON  arrow class (ui-icon-arrowthick-1-s)
    • Toggle OFF the up arrow class (ui-icon-arrowthick-1-n)
  • Animate the height of the TD to match the initial height of the .wrapper

			initHeight = $('.wrapper').height();

		    $('.wrapper').bind('mouseenter', function() {
		        $("#[id$='viewPanel1'] TR:gt(0)").not($(this).closest('tr')).stop(true, true).fadeTo('normal', 0.2);
		        $(this).closest('Div').next('span').toggleClass('ui-icon-arrowthick-1-n').toggleClass('ui-icon-arrowthick-1-s')
		        $(this).stop(true, true).animate({
		            height: this.scrollHeight
		        });
		    });
		    $('.wrapper').bind('mouseleave', function() {
		        $("#[id$='viewPanel1'] TR").not($(this).closest('tr')).stop(true, true).fadeTo('normal', 1);
		        $(this).closest('Div').next('span').toggleClass('ui-icon-arrowthick-1-n').toggleClass('ui-icon-arrowthick-1-s')
		        $(this).stop(true, true).animate({
		            height: initHeight
		        });
		    });

In summary

And there you have it. We have looked at a broad range of jQuery capabilities and brought them all together to create a great looking interface for the end user. All these capabilities are part of the jQuery core and I only used jQueryUI to get the cute up and down arrows. If you wanted to use an icon there is nothing stopping you removing the jQueryUI dependency.

We have looked at jQuery Selectors

  • attribute selector $(“#id$=
  • class selector $(‘.wrapper
  • :nth-child
  • :first

We have looked at DOM traversing and manipulation techniques

  • .children()
  • .next()
  • .closest()
  • .not()
  • .append()
  • .wrap()
We have looked at attaching events to DOM elements
  • .bind()
We have looked at object properties
  • .innerHeight()
  • .height()
We have looked at multiple visual manipulation capabilities
  • .fadeTo()
  • .animate()
  • .toggleClass()
  • .stop()

Demonstration

You can see a working example of the capability on the xomino site.

Posted in jQuery, XPages | 3 Comments »

jQuery in XPages #8 – Tokeninput autocomplete

Posted by MarkyRoden on May 1, 2012

In these two articles I am going to illustrate and describe how to implement an alternative to the XPage typeAhead, using the “jQuery Tokeninput“. Unlike the XPages typeahead the jQuery Tokeninput has a large and flexible paramaterized API which allows for control over how the results are displayed, how the results are created and the source of the data. We are going to look at how to search a basic JSON string, a notes agent generated JSON feed, a twitter JSON feed, and an XPage REST JSON feed.

Tokeninput.js

Introduction
Tokeninput provides a typeAhead-like capability whereby a user is presented with matching options as they start to type into a text field. The source of the data can either be a local JSON object or a web provided JSON object.

Basic Tokeninput user interaction

Tokeninput with facebook look and feel

Tokeninput with facebook look and feel

Tokeninput with mac look and feel

Tokeninput with mac look and feel

The search results are determined from a JSON string and can come from any web source; flickr, twitter, facebook and domino to name but a few examples. What is really nice about Tokeninput is the control and flexibility provided to the developer out of the box allows you to make some very impressing, interactive webpages with very little effort. Unlike the XPage typeahead Tokeninput will also allow you to select multiple values and the display of these values can be controlled using a CSS.

Example Twitter feed with formatted results

Example Twitter feed with formatted results

Demonstration

The XPages integration of Tokeninput is demonstrated here

Download

The demonstration database can be downloaded from the link above or from here

How does it work in XPages?

For a more detailed dive into how the examples can be recreated and function in an XPage please check out the how does it work article

Tokeninput API

Tokeninput is extremely versatile and has a large number of parameters (as of v1.6) – these are taken straight from the website and you should check out all the examples on the website to get a good idea of all the capabilities

Search Settings

  • method
    • The HTTP method (eg. GET, POST) to use for the server request. default: “GET”.
  • queryParam
    • The name of the query param which you expect to contain the search term on the server-side. default: “q”.
  • searchDelay
    • The delay, in milliseconds, between the user finishing typing and the search being performed. default: 300 (demo).
  • minChars
    • The minimum number of characters the user must enter before a search is performed. default: 1 (demo).
  • propertyToSearch
    • The javascript/json object attribute to search. default: “name” (demo).
  • jsonContainer
    • The name of the json object in the response which contains the search results. This is typically used when your endpoint returns other data in addition to your search results. Use null to use the top level response object. default: null.
  • crossDomain
    • Force JSONP cross-domain communication to the server instead of a normal ajax request. Note: JSONP is automatically enabled if we detect the search request is a cross-domain request. default: false.

Pre-population Settings

  • prePopulate
    • Prepopulate the tokeninput with existing data. Set to an array of JSON objects, eg: [{id: 3, name: "test"}, {id: 5, name: "awesome"}] to pre-fill the input. default: null (demo).

Display Settings

  • hintText
    • The text to show in the dropdown label which appears when you first click in the search field. default: “Type in a search term” (demo).
  • noResultsText
    • The text to show in the dropdown label when no results are found which match the current query. default: “No results” (demo).
  • searchingText
    • The text to show in the dropdown label when a search is currently in progress. default: “Searching…” (demo).
  • deleteText
    • The text to show on each token which deletes the token when clicked. If you wish to hide the delete link, provide an empty string here. Alternatively you can provide a html string here if you would like to show an image for deleting tokens. default: × (demo).
  • animateDropdown
    • Set this to false to disable animation of the dropdown default: true (demo).
  • theme
    • Set this to a string, eg “facebook” when including theme css files to set the css class suffix (demo).
  • resultsFormatter
    • A function that returns an interpolated HTML string for each result. Use this function with a templating system of your choice, such as jresig microtemplates or mustache.js. Use this when you want to include images or multiline formatted results default: function(item){ return “<li>” + item.propertyToSearch + “</li>” } (demo).
  • tokenFormatter
    • A function that returns an interpolated HTML string for each token. Use this function with a templating system of your choice, such as jresig microtemplates or mustache.js. Use this when you want to include images or multiline formatted tokens. Quora’s people invite token field that returns avatar tokens is a good example of what can be done this option. default: function(item){ return “<li><p>” + item.propertyToSearch + “</p></li>” } (demo).

Tokenization Settings

  • tokenLimit
    • The maximum number of results allowed to be selected by the user. Use null to allow unlimited selections. default: null (demo).
  • tokenDelimiter
    • The separator to use when sending the results back to the server. default: “,”.
  • preventDuplicates
    • Prevent user from selecting duplicate values by setting this to true. default: false (demo).
  • tokenValue
    • The value of the token input when the input is submitted. Set it to id in order to get a concatenation of token IDs, or to name in order to get a concatenation of names. default: id

Callbacks

  • onResult
    • A function to call whenever we receive results back from the server. You can use this function to pre-process results from the server before they are displayed to the user. default: null (demo).
  • onAdd
    • A function to call whenever the user adds another token to their selections. defaut: null (demo).
  • onDelete
    • A function to call whenever the user removes a token from their selections. default: null (demo).
  • onReady
    • A function to call after initialization is done and the tokeninput is ready to use. default: null


Posted in Just Marky | 2 Comments »

jQuery in XPages #8 – Tokeninput autocomplete – how does it work?

Posted by MarkyRoden on May 1, 2012

This article is written in support of the original jQuery in XPages #8 – Tokeninput autocomplete article

Demonstration

The XPages integration of Tokeninput is demonstrated here

Download

The demonstration database can be downloaded from the link above or from here

How to add Tokeninput to your database

Tokeninput consists of one js file and three css files. These can be added to your database by dragging and dropping them into the WebContents folder of the database (available through the Package Explorer)

Token2

The js files can be added to our database (along with the jQuery library) using the following XML markup in the XPage source

	<xp:this.resources>
		<xp:script src="js/jquery.tokeninput.js" clientSide="true"></xp:script>
		<xp:script src="js/jquery-1.7.1.min.js" clientSide="true"></xp:script>
		<xp:styleSheet href="css/Tokeninput/token-input.css"></xp:styleSheet>
		<xp:styleSheet href="css/Tokeninput/token-input-facebook.css"></xp:styleSheet>
		<xp:styleSheet href="css/Tokeninput/token-input-mac.css"></xp:styleSheet>
	</xp:this.resources>

Basic inner workings of Tokeninput

Tokeninput searches a given JSON string for a specified search parameter. As with many jQuery plugins there are set default which can be over-riden with user provided parmeters. By default the expected string format is:

	{{"id": "12345", "name": "Marky Roden"}]

and the code to add this to your XPage is as simple as the following where inputText3 is our text field and .tokenInput is out plugin instantiation:

	<xp:inputText value="#{sample.Custom}" id="inputText3">
	</xp:inputText>

	<xp:scriptBlock id="scriptBlock4">
		<xp:this.value>
		<![CDATA[
			$(document).ready(function() {
				$("#[id$=inputText3]").tokenInput("xTokeninput.xsp/contactjson", {
				  propertyToSearch: "email"
				});
			});
		]]>
		</xp:this.value>
	</xp:scriptBlock>

As we will see later, “xTokeninput.xsp/contactjson” is a REST service we are going to create on the page and propertyToSearch: “email” is the JSON string property we want to search

Example selecting from a REST service

Example selecting from a REST service

Changing the theme
Using the “theme” paramater we are able to determine the style in which the selected “tokens” are displayed to the user. Adding the facebook theme which comes with the plugin we change the way the results are displayed (not the results themselves).

			$(document).ready(function() {
				$("#[id$=inputText3]").tokenInput("xTokeninput.xsp/contactjson", {
				  propertyToSearch: "email",
				  theme: facebook
				});
			});

Example REST data with facebook theme

Example REST data with facebook theme

Data sources for Tokeninput

As you can see from the provided example we are able to GET (or POST) the JSON data from many sources, I have provided just a few examples, but really the options are only bounded by your needs and imagination.

A boring old Notes Agent

Before I get any grief for not using an XAgent the reason I use this as my first example is that it is probably the easiest to understand and will demonstrate how Tokeninput can generate and manage the search results itself (using it’s own internal ajax calls).
By default if you provide a URL (not a JSON object) to Tokeninput it will assume that is the URL to search and it will add “q=” to the end of the provide URL string, and look for the results. This really is as simple as it gets for Tokeninput, not a single parameter, all assumed default values. custom1 is the name of our text field and peopleLookup is our notes agent.

		$(document).ready(function() {
			$("#[id$=custom1]").tokenInput("peopleLookup?openagent?open&", {
			})
	    });

The peopleLookup agent provides a JSON string based on document fields values in a view, a snippet is below and the original is provided in the sample database

sQuery = StrRight(doc.Query_String_Decoded(0), "&q=")
Set docNAB = viewNAB.getdocumentbykey(sQuery)
i=0
Print {content-type: text/json}
	Print "["
	'loop for 10 results
	Do While Not docNAB Is Nothing And i<10

		'Because this ia JSON string each entry needs to have a comma after it except the first and last
		'so we add a comma because we know we are adding another value here
		If i>0 Then
			Print {, }
		End If
			Print |{"id":"|+docNAB.id(0)+|","name":"|+docNAB.FirstName(0)+| |+docNab.MiddleInitial(0)+| |+docNAB.LastName(0)+|"}|
			Set docNAB=viewNAB.getnextdocument(docNAB)
		i=i+1
	Loop
	Print "]"

As you can see from the picture below the JSON string maps directly to the Tokeninput displayed user options. The URL this JSON came from was http://demo.xomino.com/xomino/jQinX.nsf/peopleLookup?openagent?open&q=b

JSON data shown in firebug along with results

JSON data shown in firebug along with results

XPage REST Data Source

Using the fantastic OpenNTF extension library (and included out of the box in 8.5.3 UP1) we are able to add a REST service to the XPages. What does that mean? Well what the REST service allows us to do at its most basic is it allows us to create a JSON object from a notesview in a format that makes sense. Unlike the readviewentries option (discussed later in the article) the format created is easy to parse, easy to read and most importantly for Tokeninput, flat. If you want to take a look at the feed you will notice that it within the url of the example page (xTokeninput.xsp)

The code for the REST service is shown below:

	<xe:restService id="restService1" pathInfo="contactjson">
		<xe:this.service>
			<xe:viewJsonService viewName="AllContacts2" var="entry"
				contentType="text/plain" count="10">
				<xe:this.columns>
					<xe:restViewColumn name="id" columnName="id">
					</xe:restViewColumn>
					<xe:restViewColumn name="email"
						columnName="email">
					</xe:restViewColumn>
				</xe:this.columns>
				<xe:this.keys><![CDATA[#{javascript:return facesContext.getExternalContext().getRequest().getParameter("q")}]]></xe:this.keys>
			</xe:viewJsonService>
		</xe:this.service>
	</xe:restService>

and this is how it breaks down…..

  • viewName=”AllContacts2″ specifies the view which we want to pull the data from
  • var = Entry gives us access to the viewentry for further manipulation (see OpenNTF sample database for examples)
  • name = “name” is the name of the JSON Object [{"name": "marky@whatever.com"}]
  • columnName = “email” is the programmatic name of the view column which is the object value [{"name": "marky@whatever.com"}]
  • count=10 determines the number of results returned.

For each column we want provided in the feed we add a restViewColumn and give it the matching columnName and the JSON object value to display as (name=”id”)

The View Column Programmatic name must be used not the column Header

The View Column Programmatic name must be used not the column Header

	<xe:restViewColumn name="id" columnName="id"></xe:restViewColumn>

We can provide a “key” to the REST service which acts likes a getDocumentByKey.

	<xe:this.keys><![CDATA[#{javascript:return facesContext.getExternalContext().getRequest().getParameter("q")}]]></xe:this.keys>

The service is called every time the user enters a new key value “ma” would create the URL request to

http://demo.xomino.com/xomino/jQinX.nsf/xTokeninput.xsp/contactjson?q=ma

The key then extract the “ma” from the Query_String and provides the first document matching “ma” and then 10 documents after that. These become the displayed results from Tokeninput. A picture of the results was shown above in the initial discussion.

Standard Domino ?ReadViewEntries&OutputFormat=JSON

Since version 8 (officially) we have been able to get view data in a JSON format using the following type of URL
blah.nsf/viewname?ReadViewEntries&StarkKey=x&OutputFormat=JSON. But unfortunately the domino creation wizards at IBM decided to make the output format look more like a notesview than anything which works in Javascript (kinda the point of JSON). It ugly. So we have to work to get it into the format we need for Tokeninput. Click here to see an example of the JSON format and then you’ll have to take it to jsBeautifier.org to see the indentation. The major headache is that “@” values are properties and not objects values which makes for confusing code.

I have working proof of concept for this but I do not like the solution so it is really hard for me to write about it. With so many “better” ways to get the data from a notes view this is not something I recommend. If anyone is interested please contact me directly (@MarkyRoden) and we can discuss.

Twitter Data Source

The specification for the publically available twitter API is available from and the simplified format looks like this (looking at the feed for @MarkyRoden)

Click Here for the Feed link

[{
    "created_at": "Sat Apr 28 15:42:51 +0000 2012",
    "id": 196263215288684545,
    "id_str": "196263215288684545",
    "text": "@byrd1089 jQuery Tokeninput - hopefully will figure out how to parse readviewentires to the necessary format :)",
    "source": "\u003ca href=\"http:\/\/www.tweetdeck.com\" rel=\"nofollow\"\u003eTweetDeck\u003c\/a\u003e",
    "truncated": false,
    "in_reply_to_status_id": 196254762453303296,
    "in_reply_to_status_id_str": "196254762453303296",
    "in_reply_to_user_id": 92247920,
    "in_reply_to_user_id_str": "92247920",
    "in_reply_to_screen_name": "byrd1089",
    "user": {
        "id": 463110112,
        "id_str": "463110112",
        "name": "Mark Roden",
        "screen_name": "MarkyRoden",
        "location": "Reston, VA USA",

The source code for my example twitter pull is below. What I am doing is loading the data via “screen_name” value (e.g. edbrill or openntf) for the last 100 entries and returning the 10 entries matching the search parameter.  The parameters do the following thing:

  • propertyToSearch: the field to be searched “text”
  • queryParam: the Object name to query from the API “screen_name”
  • tokenFormatted: the HTML describing the way the results are displayed to the user
  • results formatted: the HTML describing the way the select result is stored
  • onResult: a callback function (in this case correcting a bug in Tokeninput replacing ( and ) in the return string)

			$(document).ready(function() {
				$("#[id$=inputText6]").tokenInput("https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&count=10", {
					propertyToSearch: "text",
					queryParam: "screen_name",
					resultsFormatter: function(item){ return "<li>" + "<img src='" + item.user.profile_image_url + "' title='" + item.user.screen_name + "' height='40px' width='40px' />" + "<div style='display: inline-block; padding-left: 10px; width:80%'><div class='full_name'>" + item.text  + "</div></div></li>" },
					tokenFormatter: function(item) { return "<li>" + "<img src='" + item.user.profile_image_url + "' title='" + item.user.screen_name + "' height='40px' width='40px' />" + "<div style='display: inline-block; padding-left: 10px; width:80%'><div class='full_name'>" + item.text  + "</div></div></li>" },
					onResult: function (results) {
      					jQuery.each(results, function (index, value) {
				        	value.text = value.text.replace(/\(/g, '(').replace(/\)/g, ')');
						});
				      return results;
				    }
			    });
		    });

And this to me with where Tokeninput takes the typeahead autocomplete to the next level…..

Example Twitter feed with formatted results

Example Twitter feed with formatted results

so what happens when this is submitted? If you remember the default submitted value in the Tokeninput is “id”….and If you look at the feed there is an id tag for every tweet…..

([{
    "created_at": "Sat Apr 28 15:42:51 +0000 2012",
    "id": 196263215288684545,
    "id_str": "196263215288684545",
    "text": "@byrd1089 jQuery Tokeninput - hopefully will figure out how to parse readviewentires to the necessary format :)",
    "source": "\u003ca href=\"http:\/\/www.tweetdeck.com\" rel=\"nofollow\"\u003eTweetDeck\u003c\/a\u003e",
    "truncated": false,

and when we submit the value back to the database we get a multivalue field containing all the tweet ids. If you want to change the field stored use the “tokenValue” parameter. The Tokeninput field is mapped to the “Custom” field in the XPage through the data source.

Showing the submitted tweet ids stored in the notesdocument

Showing the submitted tweet ids stored in the notesdocument

Scratching the surface

The possibilities with Tokeninput are only limited by the developers imagination and the data source available. I have demonstrated how we can create our own JSON Feed using a REST feed, how to create a simple JSON feed using a notesagent, how to parse an existing notes view readviewentries JSON feed and how to parse an external feed (twitter) and display some fantastic result results while maintaining data control.

In my opinion (from a user experience) this trumps the XPages typeAhead field capability hands down . This also trumps the XPages typeAhead from a developer perspective as well because it will take data from sources other than an internal notes view and will return multiple values which the current type ahead will not.

Demonstration site

This article is published in conjunction with the original post and  demonstration site

And then what?

To be submitted to OpenNTF – I am working on a custom control to add a dynamic Tokeninput field to any XPage. It will provide a direct alternative to the dojo typeAhead and provide greater flexibility for users.

It’s just going to be ready for this week’s article :)

Work in progress - custom control for adding tokenizer to an XPage using REST

Work in progress - custom control for adding tokenizer to an XPage using REST

Posted in jQuery, jQuery in XPages, REST, XPages | 1 Comment »

jQuery in XPages #7 – Pines Notify

Posted by MarkyRoden on April 24, 2012

In this article I will demonstrate how to implement a cool notification technique using Pines Notify.  This small (7k min) js library provides a wealth of flexibility and a feature set second to none. It can use multiple different CSS libraries (bootstrap, jQueryUI and others) so integration into your site is quick and simple.

Introduction

Pines Notify is a notification popup capability which is easily integrated into a website. Like most jQuery plugins there is a methods to instantiate the capability and the ability to pass in multiple parameters.  There are certainly other popup style plugins but this is easy to grasp and the examples are very good.

We are going to look at how to make some complex function popups like these…

Examples of Pines Notify popups

Examples of Pines Notify popups

Demonstration

There are two demonstration pages this week

The first demonstration is a basic port of the Pines Notify buttons from the original example into an XPage.

The second demonstration shows how the Pines Notify could be used in a real application

Download 

Click on the link to download the complete jQuery in XPages demonstration database (including Pines Notify).

Pines Notify

Pines Notify provides a basic shell for popup creation and the capability for multiple custom configurations. A “popup” by default is created on the top right of the screen and disappears after a fixed period of time. The the position, CSS, length of time shown, transparency, contents, callbacks and other features can be controlled through the use of parameters. There are too many to mention in one article but they can all be seen at the example website.

Sample Notification

Sample Notification

Adding Pines Notify to an XPage

The Pines Notify download contains the .js files (readable and minified) and a basic css file. These are easily added to our database as files in the WebContent folder.

Adding Pines Notify js and css fields to our database

Adding Pines Notify js and css fields to our database

Once we have added the js and css files to the database they can be added to our XPage as a resource(s)

	<xp:this.resources>
		<xp:script src="js/jquery.pnotify.min.js" clientSide="true"></xp:script>
		<xp:styleSheet href="css/jquery.pnotify.default.css"></xp:styleSheet>
	</xp:this.resources>

jQuery UI and jQuery

Pines Notify uses the jQuery library and jQuery UI CSS for display. These are added to the XPage as additional resources

	<xp:this.resources>
		<xp:script src="js/jquery-1.7.1.min.js" clientSide="true"></xp:script>
		<xp:styleSheet href="css/jquery.custom-theme/images/jquery-ui-1.8.18.custom.css"></xp:styleSheet>
	</xp:this.resources>

Using Pines Notify

Pines Notify has a basic format for activating. Here is a sample button from the website:

<button class="btn source" onclick="$.pnotify({
 	pnotify_title: 'Regular Notice',
 	pnotify_text: 'Check me out! I\'m a notice.'
});">Regular Notice</button>

In this format the button does not work correctly when added to an XPage as it causes a page refresh when clicked. To turn this into an functioning button we must use an <xp:button> like this one

<xp:button value="xPages Lower Timer" id="button1" styleClass="btn source">
<xp:eventHandler event="onclick" submit="false">
	<xp:this.script>
		<![CDATA[$.pnotify({
			pnotify_title: 'Regular Notice',
		 	pnotify_text: 'Check me out! I\'m a notice.'
		});]]>
	</xp:this.script>
</xp:eventHandler>
</xp:button>

The Pines Notify examples website gives us the code we need to create each button

Taking the sample code from the Pines Notify examples page

Taking the sample code from the Pines Notify examples page

And to convert this to our XPages button we just need to copy and paste this code and insert it into the <![CDATA[ code section above. Using this we can quickly and easily convert the examples into functioning XPages buttons.

Pinning Notifications

By default all Notifications have the ability to "pin" them to the screen. Should they contain information the user wishes to retain, they can do so

Pinning your notification

Pinning your notification

Closing Notifications

By default all notifications can be closed before they fade out using the X in the notification

Stacking notifications

Pines Notify notifications always stack on top of each other, so you never have to worry about position or overlaying issues, they will organize themselves nicely.

Showing all notifications

By default there is a bar added to the screen which allows user to see the last and/or all previous notifications.

Showing all previous notificaitons

Showing all previous notificaitons

Working this into XPages functionality

Javascript alert boxes have been used since the start of the web but they require a user click and an unnecessary interaction from the user if the point of the notification is just that – to notify the user of something.

In our XPages applications we could potentially notify the user after:

  • Successful REST update from the server
  • partialRefresh completion
  • form submission
  • Pager completion
  • General application workflow progress
  • Validation failure

really the possibilities are endless and really up to you the developer.

Examples

My demonstration site examples page has illustrated a number of buttons taken directly from the website and I have also added some notifications to the other jQuery in XPages examples (linked in through the menu).

On my second Demonstration page There are 4 simple examples of real application uses for Pines Notify

Dojo Toaster Widget

On the XPages server without the need for jQuery you can use the dojo toaster widget to provide a notification capability. You should take a look at Chris Toohey’s well written article on Mastering the Dojo Toaster for XPages to get a comparison.

Thanks

To Alan Hurt for pointing me in the direction of this plugin :)

Posted in jQuery, jQuery in XPages, XPages | Tagged: , , | Leave a Comment »

Re-styling dynamic content with jQuery Mobile

Posted by MarkyRoden on April 24, 2012

We are going to quickly look at how using trigger(“create”) can solve the problem of adding dynamic content to a jQuery Mobile application within an XPage.

I will get to writing an article on jQuery Mobile in the future but in the mean time here is something which I came across – how do you re-style the page when new content is created?

Problem

How to re-style newly added content in a jQuery Mobile application.

Situation

I have an acceptance form which I am creating in my XPage application. I have a mobile only view which is determined using a re-direct based on the browser’s user.agent when the device access the application.

jQuery Mobile acceptance form

jQuery Mobile acceptance form

Users are able to add an unknown number of guests. To achieve this functionality I am inserting a new row into the Guest Name table using normal jQuery

$('[id$=add]').click(function() { // when you click the add link
        $('[id$=theTable] tr:last').before('<tr><td><input name="person"'+i+' id=person'+i+' type="text" value="" /></td><td><label id="label'+i+'" for="check'+i+'">Yes</label><input type=checkbox name=check1 id=check'+i+'></td><td><center><img border=0 id=img'+i+' onclick="removeInput(this.id)" src="images/badge-circle-cross-24-ns.png"></center></td></tr>');
        i++;
    });

Unfortunately when I do this out of the box the sweet looking jQuery mobile look and feel is not applied to the new content.

(This problem also occurs if you perform a partialRefresh on the content) and you get something not so nice looking

Adding dynamic content does not style well

Adding dynamic content does not style well

Solution

Fortunately the guys at jQuery Mobile, smart fellows that they are, overcome this issue very easily with a simple one line command which re-applies the mobile style to any element within the supplied container.

Selecting the parent table and applying .trigger(“create”) solves the problem nicely, and it does it so fast it is imperceptible to the naked eye.

$('[id$=theTable]').trigger("create")

And here’s the result – this “looks” so good I would almost rather replace the desktop browser version of this application and have everyone use this look and feel!

jQuery mobile page with the correct styling applied

jQuery mobile page with the correct styling applied

Posted in jQuery, jQuery in XPages, jQuery Mobile, XPages | Tagged: , , , | Leave a Comment »