This problem affects any popup applet in OUI that has a custom tab sequence. To see this problem visually, goto any applet, enter query mode, and click on the Query Assistant applet.
Right click on the first control this applet, and inspect the source for this element.
Next, find this applet in Tools, and add a custom Tab sequence, the actual sequence doesn't matter, as long as it is defined. This causes Siebel to produce the tabIndex attribute, and provide an actual sequence.
After our little modification, inspect the source code again, and it should look something like this.
The tab order doesn't cycle back to the first control as it did before, but it jumps back to the base applet behind the popup applet. This causes a commit, and can produce the above error, depending on what your base applet is.
The reason behind this is, Siebel provides each of the base applets with a starting range for the tab sequence. The first applet starts at 1000, the next starts at 2000, and so on. When a popup is invoked, the controls on that applet are 0, if no tab sequence is specified, when a tab sequence is applied Siebel produces a tab sequence starting from 10000. When the last control is reached on the popup applet, it naturally returns back to the first applet, causing a commit and consequently an error.
Solution
1. Create a global render
2. Detect if the applet is a popup
3. Check for a custom tabIndex sequence
4. Put a custom event handler on the last tab index
5. Move the cursor back to the first control, when it reaches the last control
Steps
The strategy to create a global handler has already been discussed in the Open UI: Mouseover Tool Tips - Part 1 article.
1. In summary create the following JS file
//GlobalShowUI.js if(typeof( SiebelAppFacade.PhysicalRenderer.prototype.bShowUIProxy ) === "undefined"){ SiebelAppFacade.PhysicalRenderer.prototype.bShowUIProxy=true; SiebelAppFacade.PhysicalRenderer.prototype.ShowUI=(function(){ var PRShowUI = SiebelAppFacade.PhysicalRenderer.prototype.ShowUI; return function(){ Global_PreShowUI.apply(this, arguments); PRShowUI.apply(this, arguments); Global_PostShowUI.apply(this, arguments); }; }()); } //put Global Pre ShowUI logic here function Global_PreShowUI(){ var pm=this.GetPM(); if($("div[name='popup']").length>0){ SiebelApp.ABC.fixPopupFormAppletTabOrder(pm); } } //put Global Post ShowUI logic here function Global_PostShowUI(){ }
2. Add an entry into the custom_manifest.xml
3. Add the following code sample into your client side script library.
SiebelApp.ABC.fixPopupFormAppletTabOrder=function (pm){ /************************************* SAMPLE ONLY, NOT FOR PRODUCTION USE! *************************************/ var oAppletTabIndex,aAppletTabIndex; //Get all popup applet controls with tabindex oAppletTabIndex = $("div[name='popup']").find("div.mceGridField>input[tabindex], button[tabindex]"); if(oAppletTabIndex.length>0){ //turn jquery object into array for sorting aAppletTabIndex = jQuery.map (oAppletTabIndex, function( o) { return ($(o).attr("tabindex")>0)?{ tag: $(o).prop("tagName"), seq: $(o).attr("tabindex"), sel: ($(o).prop("tagName")=="BUTTON")?"[id='"+$(o).attr("id")+"_Ctrl']":"[name='"+$(o).attr("name")+"']" }:null; }); //sort on seq aAppletTabIndex.sort(function(o1, o2) { return o1.seq > o2.seq ? 1 : o1.seq < o2.seq ? -1 : 0; }); if(aAppletTabIndex.length>0){ var sSelectorFirst=aAppletTabIndex[0].sel; var sSelectorLast=aAppletTabIndex[aAppletTabIndex.length-1].sel; //attach jquery handler on last tab index, and set focus back to the first tab index $(sSelectorLast).blur(function() { $(sSelectorFirst).focus(); }); $(sSelectorFirst).focus(); } } /************************************* SAMPLE ONLY, NOT FOR PRODUCTION USE! *************************************/ }
Code Explanation
//Get all popup applet controls with tabindex oAppletTabIndex = $("div[name='popup']").find("div.mceGridField>input[tabindex], button[tabindex]"); }Grabs all fields and buttons on the popup applet where there is a tab index defined.
//turn jquery object into array for sorting aAppletTabIndex = jQuery.map (oAppletTabIndex, function( o) { return ($(o).attr("tabindex")>0)?{ tag: $(o).prop("tagName"), seq: $(o).attr("tabindex"), sel: ($(o).prop("tagName")=="BUTTON")?"[id='"+$(o).attr("id")+"_Ctrl']":"[name='"+$(o).attr("name")+"']" }:null; }); }The above command uses jQuery.map, to turn the first jquery object into a filtered array, of objects that contains the control selector, and tab sequence.
//sort on seq aAppletTabIndex.sort(function(o1, o2) { return o1.seq > o2.seq ? 1 : o1.seq < o2.seq ? -1 : 0; });Apply a sort on the new array, based on the sequence property that we created using the jQuery.map command.
var sSelectorFirst=aAppletTabIndex[0].sel; var sSelectorLast=aAppletTabIndex[aAppletTabIndex.length-1].sel; //attach jquery handler on last tab index, and set focus back to the first tab index $(sSelectorLast).blur(function() { $(sSelectorFirst).focus(); });Once we have a sorted array, we can use the selector attribute to attach a handler to the last tab index control, which forces focus back to the first control
4. Restart your client, and now enjoy your new found love for tabIndex
0 comments:
Post a Comment
Comments are open to all, please make it constructive.