Fixing the XPages R9 dojo define.amd problem once and for all

Last week I blogged about the fact that select2 v4 used define.amd and because of that dojo screws up the jQuery plugin.

This has been a significant issue – not hard to deal with but comes up all the time for XPages eedevelopers.

In the blog post comments Ferry Kranenburg posted a link to a solution he uses which is also an XSnippet. It is brilliant.

What he does in the XSnippet is:

  1. duplicates define.amd to define._amd
  2. deletes define.amd
  3. loads the AMD enabled jquery plugin (successfully)
  4. duplicates define._amd back to define.amd
  5. deletes define._amd

Rather than post the code here I want you all to go to the XSnippet and get it from there – and vote it up while you are there.

f1

Personally I would prefer not to use <xp:this.resources> or at best use them wisely and you could easily write this without using resources should you so chose.

Big Problem – simple and elegant solution – well done Ferry and thanks for Sharing

Now go vote up his XSnippet !!!

 

Select2 v4 needs AMD fixing in Domino R9 XPages

As we have seen in the past AMD (Asynchronous Module Definition) in Dojo causes issues with jQuery plugins and other JavaScript libraries, in that it prevents them from loading correctly.

The select2 version4 – new release contains AMD checking code

https://github.com/select2/select2/blob/master/dist/js/select2.js

As you can see from the start

(function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS
    factory(require('jquery'));
  } else {
    // Browser globals
    factory(jQuery);
  }
}

So we have two choices – edit the select2.js code which you download and add it to your database – change the code to look like this

(function (factory) {
  if (typeof define === 'function' && false) { // removed define.amd and returned false
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS
    factory(require('jquery'));
  } else {
    // Browser globals
    factory(jQuery);
  }
}

Or we have to ensure the database properties are set to do resource aggregation and use the following code to insert jQuery and select2 into the header

<xp:this.resources>

  <xp:headTag tagName="script">
   <xp:this.attributes>
    <xp:parameter name="type" value="text/javascript" />
    <xp:parameter name="src" value="js/jquery-1.9.1.min.js" />
   </xp:this.attributes>
  </xp:headTag>

  <xp:headTag tagName="script">
   <xp:this.attributes>
    <xp:parameter name="type" value="text/javascript" />
    <xp:parameter name="src" value="select2.js" />
   </xp:this.attributes>
  </xp:headTag>
 </xp:this.resources>

Check out Sven’s other blog post on the subject – http://hasselba.ch/blog/?p=1181

Aborting a jQuery ajax request

In this article I will show how you can abort a jQuery ajax request, preventing your user experience from disappearing into space.

Introduction

We have all done it – opened a page which runs an infinite loop consisting of some form of while(true). But no-one means to do that on purpose….That said there may be occasions where we are making requests to pages which we know are really really slow. Keeping a long connection open on the server restricts the number of open threads and potentially causes lockups. It is also a terrible user experience more importantly.

It might not be quite as relevant nowadays as it might have been to a Domino server say 10 years ago but still – pretty cool idea.

You can abort the ajax request with .abort()

Slowly loading XPage

I created a simple XPage with a before render response page which will take a long time to finish (as you can see below is ~30 seconds)

<xp:this.beforeRenderResponse>
	<![CDATA[#{javascript:
		for (var i=0; i<50000000; i++){
			true;
		}
		return true}
	]]>
</xp:this.beforeRenderResponse>

a1

Aborting the Ajax call

Aborting the call is relatively simple – by assigning a variable to the ajax call we can  then just .abort() it

var temp = $.ajax({
  url: 'http://copper.xomino.com/xomino/jQinX2.nsf/xRunForever.xsp'
}).error(function(){
   console.log('hey what happened')
})

temp.abort()

Running it manually we can see this

a2

a3

But a more practical solution is to set a timeout function to test the ajax value and abort if it is taking too long. How long that is, is entirely up to you……

function dontRunForever(url, time){
    var temp = $.ajax({
      url: url,
    }).error(function(){
       console.log('hey what happened');
    }).success(function(){
       console.log('All Done');
       temp = false
    })

    setTimeout(function(){ //Using function() inside the setTimeout means the function is executed after the "time"
      if(temp){
       console.log('aborting')
       temp.abort()
      } else {
       console.log('ajax call completed in time')
      }
    }, time)
}

dontRunForever(
  'http://copper.xomino.com/xomino/jQinX2.nsf/xRunForever.xsp',
  5000)

So when we run this with a timeout of 5 seconds, we see the abort() trigger

a4

But when we use 50 seconds we see a successful completion and no abort message – and eventually the end of the Timeout success message

a5

This is all well and good – BUT…..

Just because the ajax request is aborted – this does not mean the task stops running on the server – this whole concept improves the user experience and nothing else. This will not stop a never ending task running on the server – only the server settings will truly kill that.

a6

 

Conclusion

When I found this and the fact that it has been around for over 4 years I was somewhat annoyed that I did not know about it earlier, but it is pretty cool still all the same 🙂

 

Aligning bootstrap well heights within the same row

On a bootstrapped page layout, misaligned well heights within a single row are ugly at best and downright unprofessional looking at worst

d1

 

So I created a simple jQuery routine which will:

  • Cycle through each row
  • Create a variable = 0
  • For each well in this row
    • Check the height of each well
    • If the height is larger than the current largest value increase it
  • Once finished set the height of all the wells in this row (already selected) to be the largest height

$('.row').each(function(){
  var wellHeight=0
  $(this).find('.well').each( function(){
      var temp = $(this).height()
      if (temp>wellHeight){
       wellHeight=temp
      }
  }).height(wellHeight+"px")
})

d2

 

 

 

IBMSBT in XPages: Custom Business Cards

In this article I will introduce the JavaScript API for Social Business Toolkit and show how to make a simple custom vCard using the jQuery hovercard.js plugin.

Introduction

When you are modifying on premises Connections there is an API interface provided and described by IBM for integration of the Profiles Business Card. I could not find this piece of code however within Smartcloud. The Playground API also does not provide an example of profile vcard within Smartcloud. I guess it must be in there somewhere, but I decided to make my own. If one is available, please point me at it…….

Using the SBT JavaScript API to get Profile information

The SBT JavaScript API is REST based and provides a limited set of functionality to access information from the connections installation. I found the following example in the Playground. Looking at the example in the playground the critical URL for accessing Profile information is this

url: ‘xsp/.sbtservice/proxy/smartcloud/lotuslive-shindig-server/social/rest/people/lotuslive:user:’+userId+’/@self?format=json’,

where userId is the id of the user you wish to see the information from. In my case a JSON object called entry is returned with many options.

a1

Hovercard.js

Using the jQuery plugin hovercard.js I was easily able to create the HTML which I wanted to put within a card. This HTML is hidden on the XPage and the actual HTML I want in all within the selector class “js-vcardHTML”

<div style="display:none" class="js-vcardHTML">
	<div class="row ">
		<div class="col-sm-8">
			<div class="vcardName"></div>
			<div class="vcardOrg"></div>
			<div class="vcardEmail"></div>
			<div class="vcardPhone"></div>
		</div>
		<div class="col-sm-4 vcardImage">
		</div>
	</div>
	<div class="row" style="margin-top: 5px; border-top: 1px dashed #a6a6a6">
		<div class="col-sm-6">
			Profiles
		</div>
		<div class="col-sm-6">
			Files
		</div>
	</div>
</div>

Within the application code I instantiate the hovercard.js code in the following manner. It retrieves the HTML from within js-vcardHTML. Populates it with the data from within the “entry” JavaScript object and then displays it on hover.

$(".contacts").hovercard({
	self: this,
	detailsHTML: $('.js-vcardHTML').html(),
	width: 350,
	cardImgSrc: '',
	onHoverIn: function () {
		// set your twitter id
		var userId = 'markyId'; //calculated outside of this demonstration

		$.ajax({
			  url: 'xsp/.sbtservice/proxy/smartcloud/lotuslive-shindig-server/social/rest/people/lotuslive:user:'+userId+'/@self?format=json',
			  type: 'GET',
			  dataType: 'json',
			  beforeSend: function () {
				$(".vcardEmail").prepend('<p class="loading-text">Loading Business Card...</p>');
				$(".vcardOrg").text("");
				$(".vcardImage").html("");
			  },
			  success: function (data) {
				self.cardImgSrc=data.entry.photo
				console.log(data)
				console.log(data.entry)
				//$(".vcardName").text(data.entry.displayName);
				$(".vcardEmail").text(data.entry.emailAddress);
				$(".vcardOrg").text(data.entry.org.name);
				$(".vcardImage").html("<img src='https://apps.na.collabserv.com/contacts/profiles/photo/"+userId+"'/>");
			  },
			  complete: function () {
				$('.loading-text').remove();
			  }
			});

	}
});

a2

Conclusion

In this article I have shown how we can retrieve profile information from the Smartcloud JavaScript API and how we can create our own custom business card using the hovercard.js jQuery plugin.

Amusing that the API reference still contains a reference to Lotus – draw your own conclusions 🙂

x$ – now a part of XPages Extension Library

I am very flattered to find out that not only is my x$ OpenNTF xSnippet being used more widely than I realized (over 600 downloads). It now being used in the latest release of the OpenNTF Extension library.

If you look here – http://bootstrap4xpages.com/xsp/.ibmxspres/.extlib/responsive/xpages/js/xsp-mixin.js and search for it you will find


//jQuery selector that works with XPages IDs
//See - http://openntf.org/XSnippets.nsf/snippet.xsp?id=x-jquery-selector-for-xpages
function x$(idTag, param){
	idTag = idTag.replace(/:/gi, "\\:") + (param ? param : "");
	return($("#" + idTag));
}

Who knew my first foray into the XPages community would have such an impact.

The lesson here boys and girls should be that you should *share* your work however small and insignificant you think it is. Like all scientific research, very little code is “a completely new way of doing things”. Generally everything grows incrementally and just because you don’t think something is significant, someone else might.

You are reading this – what have you shared recently?

🙂

 

Binding jQuery code to an XPages partialRefresh using DOM Mutation events

Introduction

In this article I will demonstrate how to bind to the event which triggers on the completion of an XPages partialRefresh. Using that binding we will then be able to action to contents of the newly added partialRefresh DOM elements.

Background

In multiple articles I have discussed the use of the onComplete event of a programmatically triggered partialRefresh to be able to re-apply a jQuery plugin’s bindings to an area of an XPage. This works very nicely and integrates with the Dojo events controlling the xhr request to the Domino server.

A problem arises when you do not have a programmatically controlled partialRefresh, say for example in a pager. XPages uses the same technology to execute a partial refresh on a viewPanel – but you and I do not have programmatic access to the onComplete event without hijacking it.

This was brought back to my attention when reading Brad Balassaitis’ excellent article on adding font awesome to his data view. In that case he does not have an event available to him through the XPages designer so he has to hijack the Dojo calls. A practical solution given the tools available.

In general though I have always found using the XPage events a non-elegant way of controlling the page and there has to be a better way – I think upon reflection this is a nice learning experience and “good to know” article but not practical in production.

DOM Mutation events

These events have been around for a while but are now “deprecated” in favor of the new MutationObserver() constructor which is unfortunately not implemented in Internet Explorer until IE11

  • DOMAttrModified
  • DOMAttributeNameChanged
  • DOMCharacterDataModified
  • DOMElementNameChanged
  • DOMNodeInserted
  • DOMNodeInsertedIntoDocument
  • DOMNodeRemoved
  • DOMNodeRemovedFromDocument
  • DOMSubtreeModified

As the mozilla article states – “The practical reasons to avoid the mutation events are performance issues…...” – watching the DOM for changes every time a change happen has very processor intensive – believe me in my experiments if you latch onto DOMSubTreeModified and you are using jQuery which is constantly changing the DOM – you can easily drag your browser to its knees.

So in this article I am going to demonstrate how to use the “old” method for IE<11 and the preferred new method. You can then decide for yourself on the right way to do things – Dojo hijacking, degrading DOM performance or if you are lucky enough to not have to support IE – the way of the future 🙂

An example of the general problem

If I have a simple view panel on a page and I use some jQuery to stripe the rows it looks pretty…..(yes there are other ways to stripe the rows this is just to demonstrate the point).

jq1

But as soon as I hit the pager – the striping is lost. The DOM elements are removed and the new elements do not have the striping applied

jq2

The partialRefresh

As I am sure most of you know the partialRefresh as genius as it is, works by POST-ing the field values on the form back to the server where the JSF licecycle processes these POST-ed values and then returns a new set of HTML to the browser. That new HTML is inserted as a direct replacement of the DOM element which was being refreshed. Looking at the response from the server you can see below that when paging through a viewPanel the viewPanel1_OUTER_TABLE is re-downloded from the server and replaces the existing Table element in the DOM.

jw3

So my striped table is deleted from the DOM and replaced – ergo no more striping.

DOM Node insertion

Using the DOM Mutation event DomNodeInserted it is actually relatively easy to re-stripe the table.

I first surrounded the viewPanel with a div wrapper “viewPanelWrapper”. This is what I will use to listen to changes for. Because the whole outer table is replaced I cannot listen to events on it – it is removed along with my binding.

The first piece of code will demonstrate the event listener

$('.viewPanelWrapper').on("DOMNodeInserted", function(){
    console.log('a node was inserted')
})

When I run the above code snippet through firebug you will see that nothing changes (1). But when I click Next the partialRefresh is triggered and “a node was inserted”
jq5

If we then take this a step further we can add in our striping code again

$('.viewPanelWrapper').on("DOMNodeInserted", function(){
    console.log('a node was inserted')
    $('.viewPanel TR:even').css({background: '#FFCCCC'})
})

And that’s pretty much it – pretty simple really.

jq6

So then extending this simple example you can see how a jQuery plugin could be reapplied to any page after a partialRefresh has been triggered – JUST BE AWARE THAT THERE IS A PRICE TO PAY IN PERFORMANCE. If you are going to do this then make sure that you pick the smallest area to check possible and that it does not change every second – your browsers and more importantly users will not thank you. On and applying a jQuery plugin almost certainly also modifies your DOM – be careful not to create an endless loop of pluging in your plugin.

So the “better way”

This article explains the reasoning behind the new MutationObserver and more importantly why it makes more sense than what I just showed you.

DOM MutationObserver – reacting to DOM changes without killing browser performance.

Check out the “So what are these good for” section at the end – obviously they were talking about XPages 😉

Using a slightly modified version of their example we get this

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  var list = document.querySelector('.viewPanelWrapper');

  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {

        console.log(mutation.type);
        $('.viewPanel TR:even').css({background: '#FFCCCC'})
    });
  });

  observer.observe(list, {
  	attributes: true,
  	childList: true,
  	characterData: true
   });

jq7

Which works the same but as the article explains – WAY more efficient and also gives you the control to not screw up your plugins.

Remember though the caveat is modern browsers and that it is IE11 only

Conclusion

Overall this has been a fascinating learning experience for me. I can’t recommend using the DOMNodeInserted event listener because it definitely caused me pain and anguish in browser performance. The MutationObserver is a very interesting concept but I am not convinced I would use it in an application until I better understand it.