View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.custom.tabbedpane;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.faces.application.ViewHandler;
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.UIForm;
30  import javax.faces.component.UINamingContainer;
31  import javax.faces.context.FacesContext;
32  import javax.faces.context.ResponseWriter;
33  
34  import org.apache.myfaces.component.UserRoleUtils;
35  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
36  import org.apache.myfaces.renderkit.html.util.AddResource;
37  import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
38  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
39  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
40  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;
41  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
42  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRenderer;
43  
44  /**
45   * @JSFRenderer
46   *   renderKitId = "HTML_BASIC" 
47   *   family = "javax.faces.Panel"
48   *   type = "org.apache.myfaces.TabbedPane"
49   * 
50   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
51   * @version $Revision: 783584 $ $Date: 2009-06-10 19:00:13 -0500 (Wed, 10 Jun 2009) $
52   */
53  public class HtmlTabbedPaneRenderer
54          extends HtmlRenderer
55  {
56      private static final String HEADER_ROW_CLASS = "myFaces_pannelTabbedPane_HeaderRow";
57      private static final String ACTIVE_HEADER_CELL_CLASS = "myFaces_panelTabbedPane_activeHeaderCell";
58      private static final String INACTIVE_HEADER_CELL_CLASS = "myFaces_panelTabbedPane_inactiveHeaderCell";
59      private static final String DISABLED_HEADER_CELL_CLASS = "myFaces_panelTabbedPane_disabledHeaderCell";
60      private static final String EMPTY_HEADER_CELL_CLASS = "myFaces_panelTabbedPane_emptyHeaderCell";
61      private static final String SUB_HEADER_ROW_CLASS = "myFaces_pannelTabbedPane_subHeaderRow";
62      private static final String SUB_HEADER_CELL_CLASS = "myFaces_panelTabbedPane_subHeaderCell";
63      private static final String SUB_HEADER_CELL_CLASS_ACTIVE = "myFaces_panelTabbedPane_subHeaderCell_active";
64      private static final String SUB_HEADER_CELL_CLASS_INACTIVE = "myFaces_panelTabbedPane_subHeaderCell_inactive";
65      private static final String SUB_HEADER_CELL_CLASS_FIRST = "myFaces_panelTabbedPane_subHeaderCell_first";
66      private static final String SUB_HEADER_CELL_CLASS_LAST = "myFaces_panelTabbedPane_subHeaderCell_last";
67      private static final String CONTENT_ROW_CLASS = "myFaces_panelTabbedPane_contentRow";
68      private static final String TAB_PANE_CLASS = "myFaces_panelTabbedPane_pane";
69  
70      private static final String DEFAULT_BG_COLOR = "white";
71  
72      private static final String AUTO_FORM_SUFFIX = ".autoform";
73      private static final String TAB_DIV_SUFFIX = ".content";
74  
75  
76      public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
77      {
78          // NoOp
79      }
80  
81      public boolean getRendersChildren()
82      {
83          return true;
84      }
85  
86      public void encodeChildren(FacesContext facescontext, UIComponent uicomponent) throws IOException
87      {
88          // NoOp
89      }
90  
91      public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
92      {
93          RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlPanelTabbedPane.class);
94          HtmlPanelTabbedPane tabbedPane = (HtmlPanelTabbedPane)uiComponent;
95          if (tabbedPane.getBgcolor() == null)
96          {
97              tabbedPane.setBgcolor(DEFAULT_BG_COLOR);
98          }
99  
100         AddResource addResource = AddResourceFactory.getInstance(facesContext);
101 
102         addResource.addStyleSheet(facesContext,AddResource.HEADER_BEGIN,
103                                   HtmlTabbedPaneRenderer.class, "defaultStyles.css");
104 
105         if( tabbedPane.isClientSide() ){
106                 addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, HtmlTabbedPaneRenderer.class, "dynamicTabs.js");
107                 addResource.addInlineStyleAtPosition(facesContext,AddResource.HEADER_BEGIN,
108                                                      '#'+getTableStylableId(tabbedPane,facesContext)+" ."+ACTIVE_HEADER_CELL_CLASS+" input,\n" +
109                                                      '#'+getTableStylableId(tabbedPane,facesContext)+" ."+TAB_PANE_CLASS+",\n" +
110                                                      '#'+getTableStylableId(tabbedPane,facesContext)+" ."+SUB_HEADER_CELL_CLASS+"{\n"+
111                                                      "background-color:" + tabbedPane.getBgcolor()+";\n"+
112                                                      "}\n");
113         }
114 
115 
116         ResponseWriter writer = facesContext.getResponseWriter();
117 
118         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
119 
120         int selectedIndex = tabbedPane.getSelectedIndex();
121 
122         FormInfo parentFormInfo = RendererUtils.findNestingForm(tabbedPane, facesContext);
123         if (parentFormInfo == null)
124         {
125             writeFormStart(writer, facesContext, tabbedPane);
126         }
127 
128         List children = tabbedPane.getChildren();
129 
130         if( tabbedPane.isClientSide() ){
131             List headerIDs = new ArrayList();
132             List tabIDs = new ArrayList();
133             for (int i = 0, len = children.size(); i < len; i++)
134             {
135                 UIComponent child = getUIComponent((UIComponent)children.get(i));
136                 if (child instanceof HtmlPanelTab && child.isRendered()){
137                     HtmlPanelTab tab = (HtmlPanelTab) child;
138                     tabIDs.add( child.getClientId(facesContext) + TAB_DIV_SUFFIX);
139                     if( ! isDisabled(facesContext, tab) )
140                         headerIDs.add( getHeaderCellID(tab, facesContext) );
141                 }
142             }
143 
144             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
145             writer.startElement(HTML.SCRIPT_ELEM, tabbedPane);
146             writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
147             writer.write('\n');
148 
149             writer.write( getHeaderCellsIDsVar(tabbedPane,facesContext)+"= new Array(" );
150             for(Iterator ids=headerIDs.iterator(); ids.hasNext();){
151                 String id = (String)ids.next();
152                 writer.write('"'+JavascriptUtils.encodeString( id )+'"');
153                 if( ids.hasNext() )
154                     writer.write(',');
155             }
156             writer.write( ");\n" ); // end Array
157 
158             writer.write( getTabsIDsVar(tabbedPane,facesContext)+"= new Array(" );
159             for(Iterator ids=tabIDs.iterator(); ids.hasNext();){
160                 String id = (String)ids.next();
161                 writer.write('"'+JavascriptUtils.encodeString( id )+'"');
162                 if( ids.hasNext() )
163                     writer.write(',');
164             }
165             writer.write( ");\n" ); // end Array
166 
167             writer.endElement(HTML.SCRIPT_ELEM);
168             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
169 
170             String submitFieldIDAndName = getTabIndexSubmitFieldIDAndName(tabbedPane, facesContext);
171             writer.startElement(HTML.INPUT_ELEM, tabbedPane);
172             writer.writeAttribute(HTML.ID_ATTR, submitFieldIDAndName, null);
173             writer.writeAttribute(HTML.NAME_ATTR, submitFieldIDAndName, null);
174             writer.writeAttribute(HTML.STYLE_ATTR, "display:none", null);
175             writer.endElement(HTML.INPUT_ELEM);
176         }
177 
178         writeTableStart(writer, facesContext, tabbedPane);
179         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
180         writer.startElement(HTML.TR_ELEM, tabbedPane);
181         writer.writeAttribute(HTML.CLASS_ATTR, HEADER_ROW_CLASS, null);
182 
183         //Tab headers
184         int tabIdx = 0;
185         int visibleTabCount = 0;
186         int visibleTabSelectedIdx = -1;
187         for (int i = 0, len = children.size(); i < len; i++)
188         {
189             UIComponent child = getUIComponent((UIComponent)children.get(i));
190             if (child instanceof HtmlPanelTab)
191             {
192                 if (child.isRendered())
193                 {
194                     writeHeaderCell(writer,
195                                     facesContext,
196                                     tabbedPane,
197                                     (HtmlPanelTab)child,
198                                     tabIdx,
199                                     visibleTabCount,
200                                     tabIdx == selectedIndex,
201                                     isDisabled(facesContext, (HtmlPanelTab)child));
202                     if (tabIdx == selectedIndex)
203                     {
204                         visibleTabSelectedIdx = visibleTabCount;
205                     }
206                     visibleTabCount++;
207                 }
208                 tabIdx++;
209             }
210         }
211 
212         //Empty tab cell on the right for better look
213         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
214         HtmlRendererUtils.writePrettyIndent(facesContext);
215         writer.startElement(HTML.TD_ELEM, tabbedPane);
216         writer.writeAttribute(HTML.CLASS_ATTR, EMPTY_HEADER_CELL_CLASS, null);
217         writer.write("&#160;");
218         writer.endElement(HTML.TD_ELEM);
219         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
220         writer.endElement(HTML.TR_ELEM);
221 
222         //Sub header cells
223         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
224         writer.startElement(HTML.TR_ELEM, tabbedPane);
225         writer.writeAttribute(HTML.CLASS_ATTR, SUB_HEADER_ROW_CLASS, null);
226         writeSubHeaderCells(writer, facesContext, tabbedPane, visibleTabCount, visibleTabSelectedIdx);
227         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
228         writer.endElement(HTML.TR_ELEM);
229 
230         //Tabs
231         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
232         writer.startElement(HTML.TR_ELEM, tabbedPane);
233         writer.writeAttribute(HTML.CLASS_ATTR, CONTENT_ROW_CLASS, null);
234         writer.startElement(HTML.TD_ELEM, tabbedPane);
235         writer.writeAttribute(HTML.COLSPAN_ATTR, Integer.toString(visibleTabCount + 1), null);
236         String tabContentStyleClass = tabbedPane.getTabContentStyleClass();
237         writer.writeAttribute(HTML.CLASS_ATTR, TAB_PANE_CLASS+(tabContentStyleClass==null ? "" : " "+tabContentStyleClass), null);
238 
239         writeTabsContents(writer, facesContext, tabbedPane, selectedIndex);
240 
241         writer.endElement(HTML.TD_ELEM);
242         writer.endElement(HTML.TR_ELEM);
243 
244         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
245         writer.endElement(HTML.TABLE_ELEM);
246 
247         if (parentFormInfo == null)
248         {
249             writeFormEnd(writer, facesContext);
250         }
251     }
252 
253 
254     public void decode(FacesContext facesContext, UIComponent uiComponent)
255     {
256         RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlPanelTabbedPane.class);
257 
258         HtmlPanelTabbedPane tabbedPane = (HtmlPanelTabbedPane)uiComponent;
259 
260         Map paramMap = facesContext.getExternalContext().getRequestParameterMap();
261 
262         int tabIdx = 0;
263         List children = tabbedPane.getChildren();
264         for (int i = 0, len = children.size(); i < len; i++)
265         {
266             UIComponent child = getUIComponent((UIComponent)children.get(i));
267             if (child instanceof HtmlPanelTab)
268             {
269                 String paramName = tabbedPane.getClientId(facesContext) + "." + tabIdx;
270                 String paramValue = (String)paramMap.get(paramName);
271                 if (paramValue != null && paramValue.length() > 0)
272                 {
273                     tabbedPane.queueEvent(new TabChangeEvent(tabbedPane,
274                                                              tabbedPane.getSelectedIndex(),
275                                                              tabIdx));
276                     return;
277                 }
278                 tabIdx++;
279             }
280         }
281 
282         // No request due to a header button pressed.
283         // Restore a client-side switch
284         if( tabbedPane.isClientSide() ){
285             String clientSideIndex = (String)paramMap.get(getTabIndexSubmitFieldIDAndName(tabbedPane, facesContext));
286             if (clientSideIndex != null && clientSideIndex.length() > 0)
287             {
288                 tabbedPane.setSelectedIndex( Integer.parseInt(clientSideIndex) );
289                 return;
290             }
291         }
292     }
293 
294     protected void writeFormStart(ResponseWriter writer,
295                                   FacesContext facesContext,
296                                   UIComponent tabbedPane)
297         throws IOException
298     {
299         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
300         String viewId = facesContext.getViewRoot().getViewId();
301         String actionURL = viewHandler.getActionURL(facesContext, viewId);
302 
303         //write out auto form
304         writer.startElement(HTML.FORM_ELEM, tabbedPane);
305         writer.writeAttribute(HTML.NAME_ATTR, tabbedPane.getClientId(facesContext) + AUTO_FORM_SUFFIX, null);
306         writer.writeAttribute(HTML.STYLE_ATTR, "display:inline", null);
307         writer.writeAttribute(HTML.METHOD_ATTR, "post", null);
308         writer.writeURIAttribute(HTML.ACTION_ATTR,
309                                  facesContext.getExternalContext().encodeActionURL(actionURL),
310                                  null);
311         writer.flush();
312     }
313 
314     protected String getHeaderCellsIDsVar(HtmlPanelTabbedPane tabbedPane, FacesContext facesContext){
315         return JavascriptUtils.getValidJavascriptName(
316                 "panelTabbedPane_"+tabbedPane.getClientId(facesContext)+"_HeadersIDs",
317                 false);
318     }
319 
320     protected String getTabsIDsVar(HtmlPanelTabbedPane tabbedPane, FacesContext facesContext){
321         return JavascriptUtils.getValidJavascriptName(
322                 "panelTabbedPane_"+tabbedPane.getClientId(facesContext)+"_IDs",
323                 false);
324     }
325 
326     protected String getDefaultActiveHeaderStyleClass(HtmlPanelTabbedPane tabbedPane, FacesContext facesContext){
327         return JavascriptUtils.getValidJavascriptName(
328                 "panelTabbedPane_"+tabbedPane.getClientId(facesContext)+"_ActiveStyle",
329                 false);
330     }
331 
332     protected String getHeaderClasses(HtmlPanelTabbedPane tabbedPane, HtmlPanelTab tab, String defaultClass, String userClass){
333         String headerCellClass = defaultClass;
334         String userTabStyleClass = tab.getStyleClass();
335 
336         if( userClass != null )
337             headerCellClass = headerCellClass + " " + userClass;
338 
339         if( userTabStyleClass != null )
340             headerCellClass = headerCellClass + " " + userTabStyleClass;
341 
342         return headerCellClass;
343     }
344 
345     protected void writeTableStart(ResponseWriter writer,
346                                    FacesContext facesContext,
347                                    HtmlPanelTabbedPane tabbedPane)
348         throws IOException
349     {
350         String oldBgColor = tabbedPane.getBgcolor();
351         tabbedPane.setBgcolor(null);
352 
353         writer.startElement(HTML.TABLE_ELEM, tabbedPane);
354         writer.writeAttribute(HTML.ID_ATTR, getTableStylableId(tabbedPane,facesContext), null);
355         String oldTabbedStyleClass = tabbedPane.getStyleClass();
356         tabbedPane.setStyleClass ((oldTabbedStyleClass == null) ? "myFaces_panelTabbedPane" : "myFaces_panelTabbedPane " + oldTabbedStyleClass);
357         writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null);
358         HtmlRendererUtils.renderHTMLAttributes(writer, tabbedPane, HTML.TABLE_PASSTHROUGH_ATTRIBUTES);
359         writer.flush();
360 
361         tabbedPane.setBgcolor(oldBgColor);
362         tabbedPane.setStyleClass(oldTabbedStyleClass);
363     }
364 
365     /**
366      * As the colon (:) can't be used in CSS, transforms the id to make it safe to use for CSS. 
367      */
368     protected String getTableStylableId(HtmlPanelTabbedPane tabbedPane, FacesContext facesContext){
369         String originalID = tabbedPane.getClientId( facesContext );
370         return originalID.replace(':','_');
371     }
372 
373     protected String getTabIndexSubmitFieldIDAndName(HtmlPanelTabbedPane tabbedPane, FacesContext facesContext){
374         return tabbedPane.getClientId(facesContext)+"_indexSubmit";
375     }
376 
377     protected String getHeaderCellID(HtmlPanelTab tab, FacesContext facesContext){
378         return tab.getClientId(facesContext)+"_headerCell";
379     }
380 
381     // Do not change without modifying the .js
382     private String getSubHeaderCellID(HtmlPanelTab tab, FacesContext facesContext){
383         return tab.getClientId(facesContext)+"_headerCell_sub";
384     }
385 
386     protected void writeHeaderCell(ResponseWriter writer,
387                                    FacesContext facesContext,
388                                    HtmlPanelTabbedPane tabbedPane,
389                                    HtmlPanelTab tab,
390                                    int tabIndex,
391                                    int visibleTabIndex,
392                                    boolean active,
393                                    boolean disabled)
394         throws IOException
395     {
396         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
397         HtmlRendererUtils.writePrettyIndent(facesContext);
398         writer.startElement(HTML.TD_ELEM, tabbedPane);
399         writer.writeAttribute(HTML.ID_ATTR, getHeaderCellID(tab, facesContext), null);
400 
401         if (disabled)
402         {
403             String disabledClass = tabbedPane.getDisabledTabStyleClass();
404             writer.writeAttribute(HTML.CLASS_ATTR,
405                                   DISABLED_HEADER_CELL_CLASS + (disabledClass==null ? "" : ' '+disabledClass),
406                                   null);
407         }
408         else{
409             if (active)
410             {
411                 writer.writeAttribute(HTML.CLASS_ATTR,
412                                       getHeaderClasses(tabbedPane, tab, ACTIVE_HEADER_CELL_CLASS, tabbedPane.getActiveTabStyleClass()),
413                                       null);
414             }
415             else
416             {
417                 writer.writeAttribute(HTML.CLASS_ATTR,
418                                       getHeaderClasses(tabbedPane, tab, INACTIVE_HEADER_CELL_CLASS, tabbedPane.getInactiveTabStyleClass()),
419                                       null);
420             }
421         }
422       
423         if (tab.getStyle() != null) {
424             writer.writeAttribute(HTML.STYLE_ATTR, tab.getStyle(), null);
425         }
426 
427         String label = tab.getLabel();
428         if (label == null || label.length() == 0)
429         {
430             label = "Tab " + tabIndex;
431         }
432 
433         if (disabled) {
434             writer.startElement(HTML.LABEL_ELEM, tabbedPane);
435             // writer.writeAttribute(HTML.NAME_ATTR, tabbedPane.getClientId(facesContext) + "." + tabIndex, null); // Usefull ?
436             writer.writeText(label, null);
437             writer.endElement(HTML.LABEL_ELEM);
438         } else {
439             // Button
440             writer.startElement(HTML.INPUT_ELEM, tabbedPane);
441             writer.writeAttribute(HTML.TYPE_ATTR, "submit", null);
442             writer.writeAttribute(HTML.NAME_ATTR, tabbedPane.getClientId(facesContext) + "." + tabIndex, null);
443             writer.writeAttribute(HTML.VALUE_ATTR, label, null);
444             if( tabbedPane.isClientSide() ){
445                 String activeUserClass = tabbedPane.getActiveTabStyleClass();
446                 String inactiveUserClass = tabbedPane.getInactiveTabStyleClass();
447                 String activeSubStyleUserClass = tabbedPane.getActiveSubStyleClass();
448                 String inactiveSubStyleUserClass = tabbedPane.getInactiveSubStyleClass();
449                 String onclickEvent = tab.getAttributes().get(HTML.ONCLICK_ATTR) != null ? (String) tab.getAttributes().get(HTML.ONCLICK_ATTR) : "";
450                 
451                 if(!("").equals(onclickEvent) && onclickEvent.charAt(onclickEvent.length()-1) !=  ';') {
452                     onclickEvent += ";";
453                 }
454                 
455                 writer.writeAttribute(HTML.ONCLICK_ATTR,
456                                       onclickEvent
457                                       + "return myFaces_showPanelTab("
458                                       +tabIndex+",'"+getTabIndexSubmitFieldIDAndName(tabbedPane, facesContext)+"',"
459                                       +'\''+getHeaderCellID(tab, facesContext)+"','"+tab.getClientId(facesContext) + TAB_DIV_SUFFIX +"',"
460                                       +getHeaderCellsIDsVar(tabbedPane,facesContext)+','+getTabsIDsVar(tabbedPane,facesContext)+','
461                                       + (activeUserClass==null ? "null" : '\''+activeUserClass+'\'')+','+ (inactiveUserClass==null ? "null" : '\''+inactiveUserClass+'\'')+','
462                                       + (activeSubStyleUserClass==null ? "null" : '\''+activeSubStyleUserClass+'\'')+','+ (inactiveSubStyleUserClass==null ? "null" : '\''+inactiveSubStyleUserClass+'\'')+");",
463                                       null);
464             }
465 
466             writer.endElement(HTML.INPUT_ELEM);
467         }
468         writer.endElement(HTML.TD_ELEM);
469     }
470 
471 
472     protected void writeSubHeaderCells(ResponseWriter writer,
473                                        FacesContext facesContext,
474                                        HtmlPanelTabbedPane tabbedPane,
475                                        int visibleTabCount,
476                                        int visibleTabSelectedIndex)
477             throws IOException
478     {
479         String activeSubStyleUserClass = tabbedPane.getActiveSubStyleClass();
480         String inactiveSubStyleUserClass = tabbedPane.getInactiveSubStyleClass();
481 
482         List children = tabbedPane.getChildren();
483         StringBuffer classes = new StringBuffer();
484         for (int i = 0, len = children.size(), renderedIndex = 0; i < len; i++)
485         {
486             UIComponent child = getUIComponent((UIComponent)children.get(i));
487             if (child instanceof HtmlPanelTab && child.isRendered())
488             {
489                 HtmlRendererUtils.writePrettyLineSeparator(facesContext);
490                 HtmlRendererUtils.writePrettyIndent(facesContext);
491                 writer.startElement(HTML.TD_ELEM, tabbedPane);
492                 writer.writeAttribute(HTML.ID_ATTR, getSubHeaderCellID((HtmlPanelTab)child, facesContext), null);
493                 classes.setLength(0);
494                 classes.append(SUB_HEADER_CELL_CLASS);
495                 if( renderedIndex == 0 ){
496                     classes.append(' ');
497                     classes.append(SUB_HEADER_CELL_CLASS_FIRST);
498                 }
499                 if( renderedIndex == visibleTabCount ){
500                     classes.append(' ');
501                     classes.append(SUB_HEADER_CELL_CLASS_LAST);
502                 }
503                 if( renderedIndex == visibleTabSelectedIndex ){
504                     if( activeSubStyleUserClass != null ){
505                         classes.append(' ');
506                         classes.append(activeSubStyleUserClass);
507                     }
508                 }else if( inactiveSubStyleUserClass != null ){
509                     classes.append(' ');
510                     classes.append(inactiveSubStyleUserClass);
511                 }
512 
513                 classes.append(' ');
514                 classes.append(renderedIndex == visibleTabSelectedIndex ? SUB_HEADER_CELL_CLASS_ACTIVE : SUB_HEADER_CELL_CLASS_INACTIVE);
515 
516                 writer.writeAttribute(HTML.CLASS_ATTR, classes.toString(), null);
517 
518                 writer.write("&#160;");
519                 writer.endElement(HTML.TD_ELEM);
520 
521                 renderedIndex++;
522             }
523         }
524 
525         // Empty Cell Sub
526         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
527         HtmlRendererUtils.writePrettyIndent(facesContext);
528         writer.startElement(HTML.TD_ELEM, tabbedPane);
529         writer.writeAttribute(
530                 HTML.CLASS_ATTR,
531                 SUB_HEADER_CELL_CLASS+' '+SUB_HEADER_CELL_CLASS_LAST
532                 +(inactiveSubStyleUserClass != null ? ' '+inactiveSubStyleUserClass : ""),
533                 null);
534         writer.write("&#160;");
535         writer.endElement(HTML.TD_ELEM);
536     }
537 
538     protected void writeTabsContents(ResponseWriter writer, FacesContext facesContext, HtmlPanelTabbedPane tabbedPane,
539                                      int selectedIndex) throws IOException {
540         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
541 
542         int tabIdx = 0;
543         List children = tabbedPane.getChildren();
544         for (int i = 0, len = children.size(); i < len; i++) {
545             UIComponent child = getUIComponent((UIComponent) children.get(i));
546             if (child instanceof HtmlPanelTab) {
547                 String activeTabVar = tabbedPane.getActiveTabVar();
548                 if (activeTabVar != null) {
549                     Map requestMap = facesContext.getExternalContext().getRequestMap();
550                     requestMap.put(activeTabVar, Boolean.valueOf(tabIdx == selectedIndex));
551                 }
552 
553                 HtmlPanelTab tab = (HtmlPanelTab)child;
554                 writer.startElement(HTML.DIV_ELEM, tabbedPane);
555                writer.writeAttribute(HTML.ID_ATTR, tab.getClientId(facesContext) + TAB_DIV_SUFFIX, null);
556                 // the inactive tabs are hidden with a div-tag
557                 if (tabIdx != selectedIndex) {
558                     writer.writeAttribute(HTML.STYLE_ATTR, "display:none", null);
559                 }
560                 if (tabbedPane.isClientSide() || selectedIndex == tabIdx) {
561                   // render all content in client side mode or only the selected in server side mode
562                   RendererUtils.renderChild(facesContext, child);
563                 }
564                 writer.endElement(HTML.DIV_ELEM);
565 
566                 tabIdx++;
567                 if (activeTabVar != null) {
568                     Map requestMap = facesContext.getExternalContext().getRequestMap();
569                     requestMap.remove(tabbedPane.getActiveTabVar());
570                 }
571             } else {
572                 RendererUtils.renderChild(facesContext, child);
573             }
574         }
575    }
576 
577     private UIComponent getUIComponent(UIComponent uiComponent)
578     {
579         /* todo: handle forms other than UIForm */
580         if (uiComponent instanceof UIForm || uiComponent instanceof UINamingContainer)
581         {
582             List children = uiComponent.getChildren();
583             for (int i = 0, len = children.size(); i < len; i++)
584             {
585                 uiComponent = getUIComponent((UIComponent)children.get(i));
586             }
587         }
588         return uiComponent;
589     }
590 
591     protected void writeFormEnd(ResponseWriter writer,
592                                 FacesContext facesContext)
593         throws IOException
594     {
595         //write state marker
596         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
597         viewHandler.writeState(facesContext);
598 
599         writer.endElement(HTML.FORM_ELEM);
600     }
601 
602     protected boolean isDisabled(FacesContext facesContext, HtmlPanelTab tab)
603     {
604         return !UserRoleUtils.isEnabledOnUserRole(tab) || tab.isDisabled();
605     }
606 }