/*
* � Copyright IBM Corp. 2014
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*
* Author: Brian Gleeson (brian.gleeson@ie.ibm.com)
* Date: 10 Oct 2014
* NavbarRenderer.java
*/
package com.ibm.xsp.theme.bootstrap.renderkit.html.extlib.containers;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.extlib.renderkit.html_extended.FacesRendererEx;
import com.ibm.xsp.extlib.renderkit.html_extended.outline.tree.AbstractTreeRenderer;
import com.ibm.xsp.extlib.tree.ITree;
import com.ibm.xsp.extlib.tree.impl.TreeImpl;
import com.ibm.xsp.extlib.util.ExtLibUtil;
import com.ibm.xsp.theme.bootstrap.BootstrapLogger;
import com.ibm.xsp.theme.bootstrap.components.responsive.UINavbar;
import com.ibm.xsp.theme.bootstrap.renderkit.html.extlib.containers.tree.NavbarLinksRenderer;
import com.ibm.xsp.util.FacesUtil;
import com.ibm.xsp.util.HtmlUtil;
import com.ibm.xsp.util.JavaScriptUtil;
/**
*
* @author Brian Gleeson (brian.gleeson@ie.ibm.com)
*/
public class NavbarRenderer extends FacesRendererEx {
//Container
protected static final int PROP_CLASSCONTAINER = 1;
protected static final int PROP_STYLECONTAINER = 2;
protected static final int PROP_NAVBARFIXEDTOP = 3;
protected static final int PROP_NAVBARFIXEDBOTTOM = 4;
protected static final int PROP_NAVBARSTATICTOP = 5;
protected static final int PROP_NAVBARBASE_CLASS = 6;
protected static final int PROP_NAVBARDEFAULT = 7;
protected static final int PROP_NAVBARINVERSE = 8;
protected static final int PROP_NAVBARCHILDREN_WRAPPER = 9;
protected static final int PROP_NAVBARHEADER_CLASS = 10;
protected static final int PROP_NAVBARBRAND_CLASS = 11;
protected static final int PROP_NAVBARFIXEDTOP_PADDING = 20;
protected static final int PROP_NAVBARFIXEDBOTTOM_PADDING = 21;
protected static final int PROP_NAVBARCONTAINER_FLUID = 22;
protected static final int PROP_NAVBARCONTAINER_FIXED = 23;
protected static final int PROP_NAVBARCONTAINER_FULL = 24;
protected static final int PROP_NAVBARCOLLAPSE_CONTAINER = 25;
protected static final int PROP_NAVBARCOLLAPSE_TARGET = 26;
protected static final int PROP_NAVBARCOLLAPSEBUTTON_CLASS = 27;
protected static final int PROP_NAVBARITEM_PREFIX = 28;
protected static final int PROP_NAVBARITEM_DEFAULTCLASS = 29;
protected static final int PROP_NAVBARCOLLAPSEBUTTON_ARIALABEL = 30;
protected static final int PROP_NAVBARTITLE = 31;
protected static final int PROP_NAVBARARIAROLE = 32;
@Override
protected Object getProperty(int prop) {
switch(prop) {
// Container div
case PROP_CLASSCONTAINER: return "xspNavbar"; //$NON-NLS-1$
case PROP_STYLECONTAINER: return ""; //$NON-NLS-1$
case PROP_NAVBARFIXEDTOP: return "navbar-fixed-top"; //$NON-NLS-1$
case PROP_NAVBARFIXEDBOTTOM: return "navbar-fixed-bottom"; //$NON-NLS-1$
case PROP_NAVBARSTATICTOP: return "navbar-static-top"; //$NON-NLS-1$
case PROP_NAVBARBASE_CLASS: return "navbar"; //$NON-NLS-1$
case PROP_NAVBARDEFAULT: return "navbar-default"; //$NON-NLS-1$
case PROP_NAVBARINVERSE: return "navbar-inverse"; //$NON-NLS-1$
case PROP_NAVBARCHILDREN_WRAPPER: return "nav navbar-nav"; //$NON-NLS-1$
case PROP_NAVBARHEADER_CLASS: return "navbar-header"; //$NON-NLS-1$
case PROP_NAVBARBRAND_CLASS: return "navbar-brand"; //$NON-NLS-1$
//Fixed navbar padding
case PROP_NAVBARFIXEDTOP_PADDING: return "body {padding-top:51px;} @media (min-width: 768px) {.sidebar{top:52px;bottom:0px;}}"; //$NON-NLS-1$
case PROP_NAVBARFIXEDBOTTOM_PADDING: return "body {padding-bottom:51px;} @media (min-width: 768px) {.sidebar{top:0px;bottom:52px;}}"; //$NON-NLS-1$
// Navbar container classes
case PROP_NAVBARCONTAINER_FLUID: return "container-fluid"; //$NON-NLS-1$
case PROP_NAVBARCONTAINER_FIXED: return "container"; //$NON-NLS-1$
case PROP_NAVBARCONTAINER_FULL: return "container-full"; //$NON-NLS-1$
//Collapse classes
case PROP_NAVBARCOLLAPSE_CONTAINER: return "collapse navbar-collapse"; //$NON-NLS-1$
case PROP_NAVBARCOLLAPSE_TARGET: return true;
case PROP_NAVBARCOLLAPSEBUTTON_CLASS: return "navbar-toggle"; //$NON-NLS-1$
case PROP_NAVBARITEM_PREFIX: return "navbar-"; //$NON-NLS-1$
case PROP_NAVBARITEM_DEFAULTCLASS: return "navbar-text"; //$NON-NLS-1$
//Accessibility properties
case PROP_NAVBARCOLLAPSEBUTTON_ARIALABEL: return "Toggle navigation menu"; // $NLS-NavbarRenderer.Togglenavigationmenu-1$
case PROP_NAVBARTITLE: return "navigation bar"; // $NLS-NavbarRenderer.navigationbar-1$
case PROP_NAVBARARIAROLE: return "navigation"; //$NON-NLS-1$
}
return super.getProperty(prop);
}
// ================================================================
// Nav Bar
// ================================================================
protected void writeNavbar(FacesContext context, ResponseWriter w, UINavbar component) throws IOException{
boolean inverted = false;
String fixed = "";
String style = "";
String styleClass = "";
String title = "";
String pageWidth = null;
boolean hasChildren = false;
ITree beforeLinks = null;
ITree afterLinks = null;
if(component!=null) {
inverted = component.isInverted();
fixed = component.getFixed();
styleClass = component.getStyleClass();
style = component.getStyle();
title = component.getTitle();
pageWidth = component.getPageWidth();
hasChildren = component.getChildCount() > 0;
beforeLinks = TreeImpl.get(component.getNavbarBeforeLinks());
afterLinks = TreeImpl.get(component.getNavbarAfterLinks());
}
// write navbar container
w.startElement("div", component); // $NON-NLS-1$
if(HtmlUtil.isUserId(component.getId())) {
String clientId = component.getClientId(context);
w.writeAttribute("id", clientId, null); // $NON-NLS-1$
}
if(StringUtil.isNotEmpty(title)) {
w.writeAttribute("title", title, null); // $NON-NLS-1$
}else{
w.writeAttribute("title", (String)getProperty(PROP_NAVBARTITLE), null); // $NON-NLS-1$
}
String role = (String)getProperty(PROP_NAVBARARIAROLE);
if(StringUtil.isNotEmpty(role)) {
w.writeAttribute("role", role, null); // $NON-NLS-1$
}
String fixedClass = "";
if(!StringUtil.isEmpty(fixed)){
if(fixed.equals(UINavbar.NAVBAR_FIXED_TOP)) {
fixedClass = (String)getProperty(PROP_NAVBARFIXEDTOP);
}else if(fixed.equals(UINavbar.NAVBAR_FIXED_BOTTOM)) {
fixedClass = (String)getProperty(PROP_NAVBARFIXEDBOTTOM);
}else if(fixed.equals(UINavbar.NAVBAR_UNFIXED_TOP)) {
fixedClass = (String)getProperty(PROP_NAVBARSTATICTOP);
}else{
fixedClass = "";
}
}
//String navClass = ExtLibUtil.concatStyleClasses("navbar " + (inverted ? "navbar-inverse " : "navbar-default ") + fixedClass, styleClass); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
String navColour = inverted ? (String)getProperty(PROP_NAVBARINVERSE) : (String)getProperty(PROP_NAVBARDEFAULT);
String navClass = ExtLibUtil.concatStyleClasses((String)getProperty(PROP_NAVBARBASE_CLASS), navColour);
navClass = ExtLibUtil.concatStyleClasses(navClass, fixedClass);
navClass = ExtLibUtil.concatStyleClasses(navClass, styleClass);
String containerMixinClass = ExtLibUtil.concatStyleClasses(navClass, (String)getProperty(PROP_CLASSCONTAINER));
if(StringUtil.isNotEmpty(containerMixinClass)) {
w.writeAttribute("class", containerMixinClass, null); // $NON-NLS-1$
}
String containerMixinStyle = ExtLibUtil.concatStyleClasses((String)getProperty(PROP_STYLECONTAINER), style);
if(StringUtil.isNotEmpty(containerMixinStyle)) {
w.writeAttribute("style", containerMixinStyle, null); // $NON-NLS-1$
}
newLine(w);
//CSS required for fixed navbar
if (!StringUtil.isEmpty(fixed)) {
if(fixed.equals(UINavbar.NAVBAR_FIXED_TOP) || fixed.equals(UINavbar.NAVBAR_FIXED_BOTTOM)) {
w.startElement("style", component); // $NON-NLS-1$
w.writeAttribute("type", "text/css", null); // $NON-NLS-1$ $NON-NLS-2$
if(fixed.equals(UINavbar.NAVBAR_FIXED_TOP)) {
w.writeText((String)getProperty(PROP_NAVBARFIXEDTOP_PADDING), null);
}else if(fixed.equals(UINavbar.NAVBAR_FIXED_BOTTOM)){ // $NON-NLS-1$
w.writeText((String)getProperty(PROP_NAVBARFIXEDBOTTOM_PADDING), null);
}else{
// don't write any styles
}
w.endElement("style"); // $NON-NLS-1$
newLine(w);
}
}
//container div
w.startElement("div", component); // $NON-NLS-1$
if ( pageWidth != null) {
if (pageWidth.equals(UINavbar.WIDTH_FLUID)) {
w.writeAttribute("class", (String)getProperty(PROP_NAVBARCONTAINER_FLUID), null); // $NON-NLS-1$
} else if (pageWidth.equals(UINavbar.WIDTH_FIXED)) {
w.writeAttribute("class", (String)getProperty(PROP_NAVBARCONTAINER_FIXED), null); // $NON-NLS-1$
} else if ( pageWidth.equals(UINavbar.WIDTH_FULL)) {
w.writeAttribute("class", (String)getProperty(PROP_NAVBARCONTAINER_FULL), null); // $NON-NLS-1$
} else if ( pageWidth.equals(UINavbar.WIDTH_NONE)) {
// don't write a container class to the div
} else {
// default to container-fluid
w.writeAttribute("class", (String)getProperty(PROP_NAVBARCONTAINER_FLUID), null); // $NON-NLS-1$
}
} else {
// default to container-fluid
w.writeAttribute("class", (String)getProperty(PROP_NAVBARCONTAINER_FLUID), null); // $NON-NLS-1$
}
// write navbar-header
boolean collapsible = hasChildren || beforeLinks != null || afterLinks != null;
writeHeading(context, w, component, collapsible);
// write navbar before links
if(beforeLinks != null) {
writeBeforeLinks(context, w, component, beforeLinks);
}
// write navbar children
if(hasChildren && getRendersChildren()) {
w.startElement("div", component); // $NON-NLS-1$
w.writeAttribute("class", (String)getProperty(PROP_NAVBARCHILDREN_WRAPPER), null); // $NON-NLS-1$
renderChildren(context, w, component);
w.endElement("div"); // $NON-NLS-1$
}
// write navbar after links
if(afterLinks != null) {
writeAfterLinks(context, w, component, afterLinks);
}
// close collapse container div
w.endElement("div"); // $NON-NLS-1$
// close container div
w.endElement("div"); // $NON-NLS-1$
// Close the main frame
w.endElement("div"); // $NON-NLS-1$
}
protected void writeHeading(FacesContext context, ResponseWriter w, UINavbar c, boolean linksExist) throws IOException {
String headingText = c.getHeadingText();
String headingStyle = c.getHeadingStyle();
String headingClass = c.getHeadingStyleClass();
//start header div
w.startElement("div", c); // $NON-NLS-1$
String navbarHeader = (String)getProperty(PROP_NAVBARHEADER_CLASS);
if(StringUtil.isNotEmpty(navbarHeader)) {
w.writeAttribute("class", navbarHeader, null); // $NON-NLS-1$
}
//Write hidden div for attaching collapsible menus
if(linksExist) {
writeCollapsedLink(context, w, c);
}
// start brand div
if(StringUtil.isNotEmpty(headingText)) {
w.startElement("div", c); // $NON-NLS-1$
String headerClazz = ExtLibUtil.concatStyleClasses((String)getProperty(PROP_NAVBARBRAND_CLASS), headingClass);
if(StringUtil.isNotEmpty(headerClazz)) {
w.writeAttribute("class", headerClazz, null); // $NON-NLS-1$
}
if(StringUtil.isNotEmpty(headingStyle)) {
w.writeAttribute("style", headingStyle, null); // $NON-NLS-1$
}
w.writeText(headingText, null);
//end brand div
w.endElement("div"); // $NON-NLS-1$
}
// close header div
w.endElement("div"); // $NON-NLS-1$
// start collapse container div
w.startElement("div", c); // $NON-NLS-1$
// SPR #BGLN9ZCMXK Custom collapse class for each navbar needed
String collapseClass = (String)getProperty(PROP_NAVBARCOLLAPSE_CONTAINER);
String collapseTarget = (Boolean)getProperty(PROP_NAVBARCOLLAPSE_TARGET) ? JavaScriptUtil.encodeFunctionName(context, c, "collapse-target") : ""; // $NON-NLS-1$ $NON-NLS-2$
collapseClass = ExtLibUtil.concatStyleClasses(collapseClass, collapseTarget);
if(StringUtil.isNotEmpty(collapseClass)) {
w.writeAttribute("class", collapseClass, null); // $NON-NLS-1$
}
}
protected void writeBeforeLinks(FacesContext context, ResponseWriter w, UINavbar c, ITree beforeLinks) throws IOException {
if(null != beforeLinks) {
AbstractTreeRenderer renderer = new NavbarLinksRenderer(NavbarLinksRenderer.POSITION_LEFT);
if (renderer != null) {
renderer.render(context, c, "navbar_left", beforeLinks, w); // $NON-NLS-1$
}
}
}
protected void writeAfterLinks(FacesContext context, ResponseWriter w, UINavbar c, ITree afterLinks) throws IOException {
if(null != afterLinks) {
AbstractTreeRenderer renderer = new NavbarLinksRenderer(NavbarLinksRenderer.POSITION_RIGHT);
if (renderer != null) {
renderer.render(context, c, "navbar_right", afterLinks, w); // $NON-NLS-1$
}
}
}
protected void writeCollapsedLink(FacesContext context, ResponseWriter w, UINavbar c) throws IOException {
String dataTargetClass = "." + JavaScriptUtil.encodeFunctionName(context, c, "collapse-target"); //$NON-NLS-1$ $NON-NLS-2$
w.startElement("button", c); // $NON-NLS-1$
w.writeAttribute("type", "button", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("aria-label", (String)getProperty(PROP_NAVBARCOLLAPSEBUTTON_ARIALABEL), null); // $NON-NLS-1$
w.writeAttribute("class", (String)getProperty(PROP_NAVBARCOLLAPSEBUTTON_CLASS), null); // $NON-NLS-1$
w.writeAttribute("data-toggle", "collapse", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("data-target", dataTargetClass, null); // $NON-NLS-1$
w.startElement("span", c); // $NON-NLS-1$
w.writeAttribute("class", "sr-only", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeText((String)getProperty(PROP_NAVBARCOLLAPSEBUTTON_ARIALABEL), null);
w.endElement("span"); // $NON-NLS-1$
w.startElement("span", c); // $NON-NLS-1$
w.writeAttribute("class", "icon-bar", null); // $NON-NLS-1$ $NON-NLS-2$
w.endElement("span"); // $NON-NLS-1$
w.startElement("span", c); // $NON-NLS-1$
w.writeAttribute("class", "icon-bar", null); // $NON-NLS-1$ $NON-NLS-2$
w.endElement("span"); // $NON-NLS-1$
w.startElement("span", c); // $NON-NLS-1$
w.writeAttribute("class", "icon-bar", null); // $NON-NLS-1$ $NON-NLS-2$
w.endElement("span"); // $NON-NLS-1$
w.endElement("button"); // $NON-NLS-1$
}
/**
* Render the children of the navbar
* @designer.publicmethod
*/
public void renderChildren(FacesContext context, ResponseWriter w, UIComponent component) throws IOException {
// encode component and children
// for children of the navbar we need to add a CSS class
int count = component.getChildCount();
if(count>0) {
List<?> children = component.getChildren();
for (int i=0; i<count; i++) {
Object child = children.get(i);
boolean addDiv = false;
try{
//Determine if the child has a 'navbar-' CSS class assigned already
Method method = child.getClass().getMethod("getStyleClass", (Class[])null); // $NON-NLS-1$
Object result = method.invoke(child, (Object[])null);
String styleClass = (result != null) ? (String)result : null;
if(styleClass != null && styleClass.contains((String)getProperty(PROP_NAVBARITEM_PREFIX))){
//navbar class already assigned, don't do anything
addDiv = false;
}else{
//no navbar class assigned, wrap the control in a div with class 'navbar-text'
addDiv = true;
w.startElement("div", component); // $NON-NLS-1$
w.writeAttribute("class", (String)getProperty(PROP_NAVBARITEM_DEFAULTCLASS), null); // $NON-NLS-1$
}
} catch (Exception e) {
if(BootstrapLogger.BOOTSTRAP.isErrorEnabled()) {
BootstrapLogger.BOOTSTRAP.errorp(this, "renderChildren", e, "Exception occured while rendering Navbar children"); // $NON-NLS-1$ $NLX-NavbarRenderer.ExceptionoccuredwhilstrenderingNa-2$
}
}
UIComponent compChild = (UIComponent)child;
FacesUtil.renderComponent(context, compChild);
if(addDiv) {
//close containing div
w.endElement("div"); // $NON-NLS-1$
}
}
}
}
@Override
public void decode(FacesContext context, UIComponent component) {
// Nothing to decode here...
}
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
ResponseWriter w = context.getResponseWriter();
UINavbar c = (UINavbar) component;
if (!c.isRendered()) {
return;
}
writeNavbar(context, w, c);
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
//Nothing to do here, all handled in write nav bar
}
@Override
public boolean getRendersChildren() {
return true;
}
@Override
public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
}
}