Mouse over Tooltips was one of those unfortunately difficult features to implement in Siebel HI. List applets were one massive active control, which didn't expose mouse over events on the columns. Form applets were also littered with active controls, which left room for creative configuration around label controls, but on the whole it wasn't ideal.
With Open UI those road blocks are naturally not there anymore, so I set off to implement mouse over tooltips in Open UI and much to my surprise Open UI comes shipped with mouse over Tooltips out of the box!
1. Creating the Tooltip
Rather than implement our own Tooltip from scratch, we should leverage off community plugins that have already been built, and tested. Luckily Siebel includes qTip, a third party jQuery plugin as part of its distribution, but unluckily, it has an undesirable behaviour of displaying automatically, when the browser window is resized, so I suggest readers download and use qTip2.
2. Load qTip
To register this new tooltip JS, add a line in the custom_manifest.xml, to include it on application load.
3. Finding a handler
Instead of creating a physical render for every form and list applet in the application. It would be better if Tooltips could be rendered globally for every applet, which means that we face a similiar challenge that Alex at Siebel Essentials faced, when designing "See-Through Applets".
His challenge was to capture double clicks on every form applet globally, our challenge is to potentially load tooltips on every Applet globally.
Alex highlighted that while we could achieve this by modifying the vanilla ShowUI method of the "phyrenderer.js", he faced an eternal moral battle publicizing a how-to on modifying core siebel files, conveniently I do not work for Oracle, and do not face such a battle! But like Alex, I do have a moral obligation to steer readers from such a practice.
So are there any alternatives to modifying core vanilla framework files?
No doubt Siebel will provide official global handlers in future, but for the early Open UI pioneers, here are a couple of solutions.
a) Postload.js
Postload.js is fired on every view load. Readers of Siebel Essentials would be aware of this method, but isn't that also just another core vanilla file that we need to modify? Not necessarily.
The following line shows this undocumented part of the API, that Siebel uses to register the "OnPostload" function.
SiebelApp.EventManager.addListner( "postload", OnPostload, this );
The above command adds a new function to the list of functions that are called when SiebelApp.EventManager fires the "postload" event. We can copy this design without touching the core siebel files.
i) Copy the postload.js file, and rename it to GloablPostLoadRenderer.js
ii) Edit PostLoadRenderer.js, and rename the "SiebelAppFacade.Postload" to "SiebelAppFacade.PostLoadRenderer"
iii) Edit the custom_manifest.xml, and add the above JS file
To make it clear, “postload” is not equivelent to the “ShowUI” event of the Presentation Model. Firstly it is undocumented, “postload” only fires once per view load, while “ShowUI” fires once for every time an applet is rendered, "postload" also fires before "ShowUI", which is not suitable for attaching mouse over tooltips to Popup applets.
These differences are crucial to this design, as ideally we want the mouse over tooltips to display when the applets are rendered, and not before.
b) Over writing the ShowUI super class
This is trivially done by creating a custom object renderer, but how can this be done globally? As the title suggests, we can override the super class ShowUI method, but more accurately, we need to replace the superclass ShowUI method with our own method definition.
Before jumping into the solution, I must warn readers, that this code should not be blindly copied and pasted into production, you should understand what the code does, and the risks with its usage.
1) Modify the PhysicalRenderer class, and replace ShowUI with our own method
//Replace vanilla ShowUI with our own function SiebelAppFacade.PhysicalRenderer.prototype.ShowUI=(function(){To modify the "PhysicalRender" superclass globally, we need to alter its prototype as shown above.
//Cache the original function var PRShowUI = SiebelAppFacade.PhysicalRenderer.prototype.ShowUI;Rather than clobber or redefine the original ShowUI method, we cache the original function
//apply original ShowUI function, and add Pre/Post handlers Global_PreShowUI.apply(this, arguments);Next, we create a customised PreShowUI handler
PRShowUI.apply(this, arguments);This line fires the original ShowUI method, that we cached earlier
Global_PostShowUI.apply(this, arguments);Finally, we finish off with a custom PostShowUI handler
Heres what it looks like when put together, with some added logic to ensure we only perform this modification once.
//GlobalShowUI.js //Modify PhysicalRenderer only if it hasnt been modified if(typeof( SiebelAppFacade.PhysicalRenderer.prototype.bShowUIProxy ) === "undefined"){ //Set flag to indicate that we have modified the PhysicalRenderer SiebelAppFacade.PhysicalRenderer.prototype.bShowUIProxy=true; //Replace vanilla ShowUI with our own function SiebelAppFacade.PhysicalRenderer.prototype.ShowUI=(function(){ //Cache pointer to original function var PRShowUI = SiebelAppFacade.PhysicalRenderer.prototype.ShowUI; return function(){ //apply original ShowUI function, and add Pre/Post handlers Global_PreShowUI.apply(this, arguments); PRShowUI.apply(this, arguments); Global_PostShowUI.apply(this, arguments); }; }()); } //put Global Pre ShowUI logic here function Global_PreShowUI(){ } //put Global Post ShowUI logic here function Global_PostShowUI(){ }
2) Load the above modification
Create a new JS file, and add it to the custom_manifest.xml.
We now have the capability to hook onto the ShowUI superclass, and have created two new artificial handlers PreShowUI, and PostShowUI.
The only caveat of loading it this way, is that the super classes are constructed before your customisations kick in, and there is a tiny window where vanilla methods will fire before the customisations can take effect. This means that the objects that load at the beginning of application start, such as the Application menus, will not have these handlers.
In the next article we will look into the design of an elegant Tooltip handler, and touch on an old favourite, using a script library.
Open UI: Mouseover Tool Tips - Part 2
Thanks Jason for the Article! I have a question for you... it seems that the postload.js is only triggered on each view load, but not when a pick or mvg applet is triggered.
ReplyDeleteI am trying out your example and it works for applets within a view. But when a pop-up applet (Pick, MVG, Query Assistant) is triggered it bypasses the postload.js.
Is there another js file that captures pop-up applet events?
Genesis
Hi Genesis
ReplyDeleteThat issue is described in the article above under: 3. Finding a handler. My articles are based on using ShowUI.
However ‘postload’ does have its benefits, as it is only fired once per view. This could be complimented with a binding to the “OnLoadPopupContent” method of the PopupPM
http://docs.oracle.com/cd/E14004_01/books/config_open_ui/appendix_a_API18.html
Regards
Jason