Patching RichFaces 3.3.3 AJAX.js for IE11

Couple of years ago I wrote about patching RichFaces 3.3.3 AJAX.js for IE9 and as the browser world has moved on, it’s now time to patch RichFaces 3.3.3 AJAX.js for Internet Explorer 11. Of course you could update your web application to JSF 2 and RichFaces 4 or PrimeFaces but it’s neither trivial nor free. The issue with RichFaces 3.3.3 still stands, development has moved to 4.x version and they’ve dropped support for the older versions although at least IE issues could be easily fixed. Fortunately patching RichFaces AJAX.js is relatively easy.

The problem with Internet Explorer 11 is that although you upgraded Sarissa Framework and patched RichFaces AJAX.js file for IE 9 it just isn’t enough anymore as IE 11 uses different User-agent string and disguises itself as “like Gecko”. The User-agent string in Win 8.1 for IE11 is “Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko”. The issue is also discussed on JBoss Forums and on Stack Overflow.

The different User-agent string breakes the “rerender” after Ajax Request. For example using a4j:function in combination with “rerender” is not working on IE 11 and the problem is that after Ajax request the result XML can’t be correctly append to the body. Also the rerendering is abnormal for h:inputTextarea, rich:modalPanel, h:inputTextarea and rich:calendar.

Fortunately the fix is simple as RicFaces issue RF-13443 tells.

Patching RichFaces 3.3.3 AJAX.js for IE 11

Step 1: Do the “Upgrading Sarissa Framework and patching RichFaces AJAX.js file” steps in my Patching RichFaces 3.3.3 AJAX.js for IE9 article.

Step 2: Edit the AJAX_IE9fix.js file you just created and add IE 11 checking to Sarissa._SARISSA_IS_IE and Sarissa._SARISSA_IS_IE9. The changed lines should look like the following:

Sarissa._SARISSA_IS_IE = (document.all && window.ActiveXObject && navigator.userAgent.toLowerCase().indexOf("msie") > -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1) || (navigator.userAgent.toLowerCase().indexOf("like gecko") > -1 && navigator.userAgent.toLowerCase().indexOf("11.") > -1);
 
Sarissa._SARISSA_IS_IE9 = Sarissa._SARISSA_IS_IE && (parseFloat(navigator.appVersion.substring(navigator.appVersion.indexOf("MSIE")+5))) >= 9 || (navigator.userAgent.toLowerCase().indexOf("like gecko") > -1 && navigator.userAgent.toLowerCase().indexOf("11.") > -1);

If all fails you can try to tell IE 11 to emulate IE 10 with adding X-UA-Compatible meta tag to head.

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE10"/>

The other thing I noticed with RichFaces 3.3.3 and modern browsers is that if you use ui:fragment which contains rich:suggestionbox and rerender it, the suggestionbox doesn’t work correctly. It gives an error: SCRIPT5007: Unable to get property 'parentNode' of undefined or null reference. For now I didn’t have time to figure out the issue and just changed my page structure and function.

Although it’s 2015 using JSF 1.2 and RichFaces 3.3.3 is still working quite nicely :)

Patching RichFaces 3.3.3 AJAX.js for IE9

There are always some problems when working with 3rd party frameworks when the world moves forward but the framework you’re using doesn’t. After JSF 2 was released the RichFaces development moved to 4.x version and they dropped support for the older versions although many users are still using the older versions as it’s not trivial or free to update to JSF 2 & RichFaces 4. Now if users have problems with the older versions they just have to patch it themselves as the RichFaces team isn’t going to fix e.g. the IE9 issues.

The problem with Internet Explorer 9 is that with RichFaces 3.3.3.Final the Ajax components doesn’t work and there are errors shown in the JavaScript Console. For example when testing RichFaces Ajax demos the JavaScript is assigning A.outerHTML = new XMLSerializer().serializeToString(C) which gives the following error:

SCRIPT16386: No such interface supported

Fortunately it’s relatively easy to fix it for IE9 by patching the AJAX.js JavaScript file in RichFaces. The process is discussed in RichFaces’s RF-9485 -JIRA ticket and JBoss forum message. In short the patching for AJAX.js contains following steps.

Update 2013-06-13: If you want quick solutions and aren’t interested about how it’s done, you can get the patched AJAX.js from the RF-9485 ticket as Ken Clubok pointed out in the comments.

Upgrading Sarissa Framework and patching RichFaces AJAX.js file

Resources:

1) Instead of patching Sarissa as suggested in the ticket it’s better to upgrade Sarissa to 0.9.9.6 as it has some additional IE9 issues fixed.

Download Sarissa:
http://sourceforge.net/projects/sarissa/files/sarissa/Sarissa%200.9.9.6/sarissa-0.9.9.6.jar/download

Extract sarissa.js from the jar:
sarissa-0.9.9.6.jar/gr/abiss/js/sarissa/sarissa.js

Replace first part of AJAX.js (till EOF comment) or replace sarissa.js in richfaces souce resources.
richfaces-impl-3.3.3.Final.jar/org/ajax4jsf/javascript/scripts/AJAX.js or richfaces-ui-3.3.3.Final\framework\impl\src\main\javascript\ajaxjsf\

2) For richfaces-impl work in non-compatibility mode in IE9, A4J.AJAX.replacePage method should be fixed in RichFaces sources in AJAX.js or JSFAJAX.js because custom document.open method does not work in IE9 document mode.

Replace (line 2125 in AJAX.js, line 1121 in JFSAJAX.js)
	if(isIE) {  
With
	if(isIE&&!Sarissa._SARISSA_IS_IE9) {

3) You will also need to change or comment the next line in AJAX.js or JSFAJAX.js (richfaces-ui-3.3.3.Final\framework\impl\src\main\javascript\ajaxjsf\)

Replace (line 2119 in AJAX.js, line 1044 in JFSAJAX.js)
	LOG.debug("Hidden JSF state fields: "+idsSpan);
to
	LOG.debug("Hidden JSF state fields: ");//+idsSpan);

Otherwise you get the following error in IE9:

	SCRIPT438: Object doesn't support this property or method
	For this line 2648 in framework pack:
	LOG.debug("Hidden JSF state fields: "+Q);

4) Now you have patched AJAX.js and you can either build a new RichFaces Jar, replace the original AJAX.js in RichFaces Jar with the patched one or just override it in your html.

The easiest way is just include the patched AJAX.js file as the last script-include within HEAD and it will take control over the bundled version of RichFaces.

In your template.jspx or similar:

<html>
	<head>
	<a4j:loadScript src="/js/AJAX_IE9fix.js" />
	...
	</head>
	...

5) Done.

Other ways to fix IE9 compatibility issues has been discussed in tickets RF-10774 as forcing IE9 to use another document mode. Also suggested solutions are to implement a browser sniffer and tell the Sarissa library to use its own XMLSerializer when Internet Explorer 9 is detected and forcing IE9 to run in an emulated mode but neither of those worked for me.

Using RichFaces 3 dataScroller and dataTable -components

RichFaces provides some nice AJAX-components for Java Server Faces but the documentation and examples could be better. RichFaces has great documentation compared to some other frameworks but it could be better with adding a little bit of real world and down to earth examples. So here is one example of using RichFaces dataScroller and dataTable -components with custom CSS-styling, backingBean and JSF-page snippets using Richfaces 3.3.2.SR1 and JSF 1.2_12.

Using RichFaces dataScroller and dataTable components has a big negative property: they work nicely if the amount of data is small but when the row count reaches to thousands they become sluggish or stop working. The rich:dataScroller needs the complete datamodel being loaded into memory and only displays a part of it. Not very efficient if the rowcount exceeds 1000 or so.

Anyways here is some real world example. The icons used in the examples for dataScroller are from Crystal Project Icons.

JSF-page

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<h:form id="myForm">
    <rich:dataScroller styleClass="dataScroller" id="scroller" 
        for="resultTable" maxPages="15" fastStep="3"
        renderIfSinglePage="false" immediate="false" 
        binding="#{backingBean.scroller}" page="#{backingBean.scrollerPage}">
        <f:facet name="first" >
            <h:graphicImage id="firstImage" styleClass="scroller" 
            url="images/crystal/tab_first.png" alt="first"/>
        </f:facet>
        <f:facet name="last">
            <h:graphicImage id="lastImage" styleClass="scroller" 
                url="images/crystal/tab_last.png" alt="last"/>
        </f:facet>
        <f:facet name="previous">
            <h:graphicImage id="prevImage" styleClass="scroller" 
                url="images/crystal/tab_left.png" alt="previous"/>
            </f:facet>
        <f:facet name="next">
            <h:graphicImage id="nextImage" styleClass="scroller" 
                url="images/crystal/tab_right.png" alt="previous"/>
        </f:facet>
        <f:facet name="fastforward">
            <h:graphicImage id="ffImage" styleClass="scroller" 
                url="images/crystal/tab_fastf.png" alt="next"/>
        </f:facet>
        <f:facet name="fastrewind">
            <h:graphicImage id="frImage" styleClass="scroller" 
                url="images/crystal/tab_fastr.png" alt="next"/>
        </f:facet>
        <f:facet name="first_disabled" >
            <h:graphicImage id="firstImage_d" styleClass="scroller" 
                url="images/crystal/tab_first.png" alt="first"/>
        </f:facet>
        <f:facet name="last_disabled">
            <h:graphicImage id="lastImage_d" styleClass="scroller" 
                url="images/crystal/tab_last.png" alt="last"/>
        </f:facet>
        <f:facet name="previous_disabled">
            <h:graphicImage id="prevImage_d" styleClass="scroller" 
                url="images/crystal/tab_left.png" alt="previous"/>
        </f:facet>
        <f:facet name="next_disabled">
            <h:graphicImage id="nextImage_d" styleClass="scroller" 
                url="images/crystal/tab_right.png" alt="next"/>
        </f:facet>
        <f:facet name="fastforward_disabled">
            <h:graphicImage id="ffImage_d" styleClass="scroller" 
                url="images/crystal/tab_fastf.png" alt="next"/>
        </f:facet>
        <f:facet name="fastrewind_disabled">
            <h:graphicImage id="frImage_d" styleClass="scroller" 
                url="images/crystal/tab_fastr.png" alt="previous"/>
        </f:facet>
        <f:facet name="controlsSeparator">
            <h:outputText id="sep" value=" " />
        </f:facet>
</rich:dataScroller>
 
<rich:dataTable styleClass="resultTable" id="resultTable" 
    rows="10" rowClasses=",odd"  columnClasses="col"  
    value="#{backingBean.resultList}" binding="#{backingBean.resultData}" 
    var="h" sortMode="multi">
    <rich:column sortBy="#{h.desc}">
        <f:facet name="header">
            <h:outputText value="description" />
        </f:facet>
        <h:commandLink value="#{h.desc}"
            action="#{backingBean.showRowData}">
            <f:param name="selectedRow" value="#{h.desc" />
        </h:commandLink>
    </rich:column>
    <rich:column sortBy="#{h.value}">
        <f:facet name="header">
            <h:outputText value="value" />
        </f:facet>
        <h:outputText value="#{h.value}" />
    </rich:column>
</rich:dataTable>
</h:form>

Backing Bean

Create some variables for dataScroller and getters and setters for them:

1
2
3
4
5
6
7
8
// RichFaces dataScroller variables
private HtmlDatascroller scroller = new HtmlDatascroller();
private String scrollerPage = "";
 
// Getting the clicked row's data
public String showRowdata() {
  MyDataModel current = (myDataModel) getResultData().getRowData();
}

CSS styling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* =RichFaces DataScroller
----------------------------------------------- */
.rich-datascr {font-size: 1.1em;border: 0;}
.rich-table-cell {font-size: 1.0em;}
.rich-table-sortable-header {font-size: 1.1em;font-weight: bold;}
td.rich-datascr-button {background-color: #fff;border: 0px solid #ccc;text-decoration: none;}
td.rich-datascr-button-dsbld {background-color: #fff;}
.rich-datascr-ctrls-separator {padding-right: 5px;}
.rich-dtascroller-table {background: #fff;border: 0;}
.scroller {display: block;background-color: #fff;border: 1px solid #ccc;padding: 3px 3px;margin: 0px 5px 5px 5px;text-decoration: none;}
.scroller:hover {background-color: #eee;}
td.rich-datascr-button-dsbld .scroller {background-color: #eee;}
td.rich-datascr-inact {font-size: 1.2em;color: #000;border: 0;}
td.rich-datascr-inact:hover {text-decoration: underline;}
td.rich-datascr-act {font-size: 1.2em;text-decoration: underline;}
td.rich-datascr-act {border: 0;font-weight: bold;}

Selecting All rows with JavaScript

Add to the JSF-page a new column which has the checkbox. We are using JavaScript to loop through the input fields which are after :tu -ending id-field.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<rich:column>
  <f:facet name="header">
    <h:panelGroup layout="block">
      <script type="text/javascript">
        //<![CDATA[
        // RichFaces datatable select all -checkbox
        function checkAllCheckboxesInTable( inputId, state ){
          var commonIdPart = inputId.substr(0, inputId.lastIndexOf(':'));
          var tableId = commonIdPart + ':tu'
          var tableElement = document.getElementById( tableId );
          var inputs = tableElement.getElementsByTagName('input');
          for (var i = 0; i <= inputs.length; i++){
            var input = inputs[i];
            if (input != undefined) {
              if( input.getAttribute('type') == 'checkbox' && state){
                input.setAttribute('checked', state);
              } else{
                input.setAttribute('checked', false);
                input.removeAttribute('checked');
              }
            }
          }
        }
        //]]>
      </script>
      <h:selectBooleanCheckbox id="t0" onclick="checkAllCheckboxesInTable( this.id, this.checked );">
        <a4j:support event="onchange" reRender="resultTable"/>
      </h:selectBooleanCheckbox>
    </h:panelGroup>
  </f:facet>
  <h:selectBooleanCheckbox id="t1" value="#{h.selected}" />
</rich:column>

Selecting All rows in backing bean

You can also check all the checkboxes from the backingBean but it has problems with table ordering and when the lists order changes the selection goes wrong.

Add to the JSF-page a new column:

1
2
3
4
5
6
7
8
<rich:column>
  <f:facet name="header">
    <h:selectBooleanCheckbox id="t0" value="#{backingBean.selectedAll}" onclick="this.blur()">
        <a4j:support event="onchange" actionListener="#{backingBean.selectAll}" reRender="resultTable, t0, t1"/>
      </h:selectBooleanCheckbox>
    </f:facet>
  <h:selectBooleanCheckbox id="t1" value="#{h.selected}" />
</rich:column>

Make a new method to your backingBean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void selectAll(ActionEvent event) {
	logger.info("*** backingBean.selectAll(): " + scrollerPage + " ***");
 
	// get the current scroller page
	int page = Integer.valueOf(scrollerPage).intValue();
	if (page != 0) {
	    page = page - 1;
	}
	int start = page * 10;
	int stop = (page * 10) + 10;
	if (stop > getResultList().size()) {
	    stop = getResultList().size();
	}
	logger.debug("> page: " + page + "; start: " + start + "; stop: " + stop);
 
	// check the boxes on the active page
	for (int i = start; i < stop; i++) {
	    logger.debug("> valitaan: " + i + "; " + selectedAll);
	    getResultList().get(i).setSelected(selectedAll);
	}
}