/*
* � Copyright IBM Corp. 2014, 2015
*
* 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.
*/
package com.ibm.xsp.theme.bootstrap.renderkit.html.extlib.layout;
import java.io.IOException;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.component.UICallback;
import com.ibm.xsp.component.xp.XspEventHandler;
import com.ibm.xsp.extlib.component.layout.ApplicationConfiguration;
import com.ibm.xsp.extlib.component.layout.UIApplicationLayout;
import com.ibm.xsp.extlib.component.layout.impl.SearchBar;
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.renderkit.html_extended.outline.tree.ComboBoxRenderer;
import com.ibm.xsp.extlib.tree.ITree;
import com.ibm.xsp.extlib.tree.ITreeNode;
import com.ibm.xsp.extlib.tree.impl.TreeImpl;
import com.ibm.xsp.extlib.util.ExtLibRenderUtil;
import com.ibm.xsp.extlib.util.ExtLibUtil;
import com.ibm.xsp.renderkit.html_basic.HtmlRendererUtil;
import com.ibm.xsp.theme.bootstrap.components.layout.ResponsiveApplicationConfiguration;
import com.ibm.xsp.theme.bootstrap.components.layout.SimpleResponsiveApplicationConfiguration;
import com.ibm.xsp.theme.bootstrap.renderkit.html.extlib.layout.tree.ApplicationLinksRenderer;
import com.ibm.xsp.theme.bootstrap.renderkit.html.extlib.layout.tree.SearchOptionsRenderer;
import com.ibm.xsp.theme.bootstrap.renderkit.html.extlib.layout.tree.UtilityLinksRenderer;
import com.ibm.xsp.theme.bootstrap.resources.Resources;
import com.ibm.xsp.util.FacesUtil;
import com.ibm.xsp.util.HtmlUtil;
import com.ibm.xsp.util.JSUtil;
import com.ibm.xsp.util.TypedUtil;
public class SimpleResponsiveLayoutRenderer extends FacesRendererEx {
public static final boolean FLUID = true;
public static final String COLUMN_TINY = "col-xs-"; // $NON-NLS-1$
public static final String COLUMN_SMALL = "col-sm-"; // $NON-NLS-1$
public static final String COLUMN_MEDIUM = "col-md-"; // $NON-NLS-1$
public static final String COLUMN_LARGE = "col-lg-"; // $NON-NLS-1$
public static final String COLUMN_SMALL_OFFSET = "col-sm-offset-"; // $NON-NLS-1$
public static final String COLUMN_MEDIUM_OFFSET = "col-md-offset-"; // $NON-NLS-1$
public static final int PROP_CONTENT_SIZE = 1;
public static final int PROP_LEFT_SIZE = 2;
public static final int PROP_SMALL_LEFT_SIZE = 3;
public static final int PROP_RIGHT_SIZE = 4;
public static final int PROP_SMALL_RIGHT_SIZE = 5;
public static final int COLLAPSE_LEFT_COLUMN_TARGET = 10;
public static final int COLLAPSE_LEFT_MENU_LABEL = 11;
public static final int PROP_BANNER_FIXEDTOP_PADDING = 20;
public static final int PROP_BANNER_FIXEDBOTTOM_PADDING = 21;
public static final int PROP_BANNER_COLLAPSE_CLASS = 22;
public static final int PROP_BANNER_COLLAPSE_BUTTON_ARIALABEL = 23;
@Override
protected Object getProperty(int prop) {
switch(prop) {
case PROP_CONTENT_SIZE: return 12;
case PROP_LEFT_SIZE: return 2;
case PROP_SMALL_LEFT_SIZE: return 3;
case PROP_RIGHT_SIZE: return 2;
case PROP_SMALL_RIGHT_SIZE: return 3;
case COLLAPSE_LEFT_COLUMN_TARGET: return ".applayout-column-left"; // $NON-NLS-1$
case COLLAPSE_LEFT_MENU_LABEL: return com.ibm.xsp.extlib.controls.ResourceHandler.getString("MenuRenderer.Menu"); // $NON-NLS-1$
//Fixed banner padding
case PROP_BANNER_FIXEDTOP_PADDING: return "body {padding-top:51px;} @media (min-width: 768px) {.applayout-main .sidebar{top:52px;bottom:0px;}}"; // $NON-NLS-1$
case PROP_BANNER_FIXEDBOTTOM_PADDING: return "body {padding-bottom:51px;} @media (min-width: 768px) {.applayout-main .sidebar{top:0px;bottom:52px;}}"; // $NON-NLS-1$
case PROP_BANNER_COLLAPSE_CLASS: return "navbar-collapse-target"; // $NON-NLS-1$
case PROP_BANNER_COLLAPSE_BUTTON_ARIALABEL: return "Toggle navigation menu"; // $NLS-NavbarRenderer.Togglenavigationmenu-1$
}
return super.getProperty(prop);
}
public SimpleResponsiveLayoutRenderer () {}
// ================================================================
// Main Frame
// ================================================================
protected void writeMainFrame(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration) throws IOException {
boolean isNavbar = false;
boolean invertedNavbar = false;
String fixedNavbar = "";
boolean collapseLeftColumn = false;
String collapseLeftTarget = "";
String collapsedLeftMenuLabel = "";
String pageWidthClass = "";
if(configuration!=null) {
isNavbar = configuration.isNavbar();
invertedNavbar = configuration.isInvertedNavbar();
collapseLeftColumn = configuration.isCollapseLeftColumn();
String target = configuration.getCollapseLeftTarget();
collapseLeftTarget = (StringUtil.isNotEmpty(target) ? target : (String)getProperty(COLLAPSE_LEFT_COLUMN_TARGET));
String menuLabel = configuration.getCollapsedLeftMenuLabel();
collapsedLeftMenuLabel = (StringUtil.isNotEmpty(menuLabel) ? menuLabel : (String)getProperty(COLLAPSE_LEFT_MENU_LABEL));
pageWidthClass = getContainerClass(configuration);
String fixed = configuration.getFixedNavbar();
fixedNavbar = (StringUtil.isNotEmpty(fixed) ? fixed : SimpleResponsiveApplicationConfiguration.NAVBAR_FIXED_TOP);
}
UIViewRoot viewRoot = context.getViewRoot();
String renderKitId = "";
if( null != viewRoot ){
renderKitId = viewRoot.getRenderKitId();
}
w.writeComment("renderKitId: " + renderKitId); // $NON-NLS-1$
newLine(w);
//CSS required for fixed Banner
if (isNavbar && !StringUtil.isEmpty(fixedNavbar)) {
String navbarPadding = "";
boolean addStyle = false;
if(fixedNavbar.equals(ResponsiveApplicationConfiguration.NAVBAR_FIXED_TOP)) {
navbarPadding = (String)getProperty(PROP_BANNER_FIXEDTOP_PADDING);
addStyle = true;
}else if(fixedNavbar.equals(ResponsiveApplicationConfiguration.NAVBAR_FIXED_BOTTOM)) {
navbarPadding = (String)getProperty(PROP_BANNER_FIXEDBOTTOM_PADDING);
addStyle = true;
}
if(addStyle) {
w.startElement("style", c); // $NON-NLS-1$
w.writeAttribute("type", "text/css", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeText(navbarPadding, null);
w.endElement("style"); // $NON-NLS-1$
}
}
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class", "applayout-main", null); // $NON-NLS-1$ $NON-NLS-2$
if (HtmlUtil.isUserId(c.getId())) {
w.writeAttribute("id", c.getClientId(context), null); // $NON-NLS-1$
}
if (configuration != null) {
// Start the navbar
if (isNavbar) {
writeNavbar(context, w, c, configuration, invertedNavbar, fixedNavbar, pageWidthClass);
}
// Start the main content
writeMainContent(context, w, c, configuration, collapseLeftColumn, pageWidthClass, collapseLeftTarget, collapsedLeftMenuLabel);
}
// Close the main frame
w.endElement("div"); // $NON-NLS-1$
newLine(w);
}
// ================================================================
// Navbar
// ================================================================
protected void writeNavbar(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration,
boolean navbarInverted, String navbarFixed, String pageWidthClass) throws IOException {
String navbarFixedClass = "";
if(StringUtil.isNotEmpty(navbarFixed)){
if(navbarFixed.equals(SimpleResponsiveApplicationConfiguration.NAVBAR_FIXED_TOP)) {
navbarFixedClass = "navbar-fixed-top"; // $NON-NLS-1$
}else if(navbarFixed.equals(SimpleResponsiveApplicationConfiguration.NAVBAR_FIXED_BOTTOM)) {
navbarFixedClass = "navbar-fixed-bottom"; // $NON-NLS-1$
}else if(navbarFixed.equals(SimpleResponsiveApplicationConfiguration.NAVBAR_UNFIXED_TOP)) {
navbarFixedClass = "navbar-static-top"; // $NON-NLS-1$
}
}
w.startElement("div", c); // $NON-NLS-1$
String navClass = "navbar applayout-banner " + // $NON-NLS-1$
(navbarInverted ? "navbar-inverse " : "navbar-default ") + navbarFixedClass; // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("class", navClass, null); // $NON-NLS-1$
w.writeAttribute("role", "banner", null); // $NON-NLS-1$ $NON-NLS-2$
//container div
w.startElement("div",c); // $NON-NLS-1$
String navbarClass = ExtLibUtil.concatStyleClasses(pageWidthClass, "applayout-banner-container"); // $NON-NLS-1$
w.writeAttribute("class", navbarClass, null); // $NON-NLS-1$
writeNavbarContent(context, w, c, configuration, navbarInverted);
w.endElement("div"); // $NON-NLS-1$
newLine(w, "container"); // $NON-NLS-1$
w.endElement("div"); // $NON-NLS-1$
newLine(w, "banner"); // $NON-NLS-1$
}
protected void writeNavbarContent(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration, boolean navbarInverted) throws IOException {
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class", "navbar-header", null); // $NON-NLS-1$ $NON-NLS-2$
//Write hidden div for attaching collapsible menus
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class", "applayout-banner-collapse", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("style", "display:none", null); // $NON-NLS-1$ $NON-NLS-2$
w.endElement("div"); // $NON-NLS-1$
writeNavbarLink(context, w, c, configuration);
newLine(w);
writeNavbarProductlogo(context, w, c, configuration);
writeNavbarText(context, w, c, configuration, navbarInverted);
w.endElement("div"); // $NON-NLS-1$
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class", ExtLibUtil.concatStyleClasses((String)getProperty(PROP_BANNER_COLLAPSE_CLASS), "navbar-collapse collapse"), null); // $NON-NLS-1$ $NON-NLS-2$
newLine(w);
writeNavbarApplicationLinks(context, w, c, configuration);
newLine(w);
writeNavbarUtilityLinks(context, w, c, configuration);
newLine(w);
writeSearchBar(context, w, c, configuration);
newLine(w);
w.endElement("div"); // $NON-NLS-1$
newLine(w, ""); // $NON-NLS-1$
}
protected void writeNavbarLink(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration) throws IOException {
List<ITreeNode> utilLinks = configuration.getNavbarUtilityLinks();
List<ITreeNode> appLinks = configuration.getNavbarAppLinks();
SearchBar searchBar = configuration.getSearchBar();
if((utilLinks != null && utilLinks.size() > 0) || (appLinks != null && appLinks.size() > 0) || (searchBar != null && searchBar.isRendered())) {
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_BANNER_COLLAPSE_BUTTON_ARIALABEL), null); // $NON-NLS-1$
w.writeAttribute("class", "navbar-toggle", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("data-toggle", "collapse", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("data-target", "." + getProperty(PROP_BANNER_COLLAPSE_CLASS), 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_BANNER_COLLAPSE_BUTTON_ARIALABEL), null); // $NON-NLS-1$
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$
}
}
protected void writeNavbarProductlogo(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration) throws IOException {
w.startElement("div",c); // $NON-NLS-1$
String style = configuration.getNavbarLogoStyle();
if(StringUtil.isNotEmpty(style)) {
w.writeAttribute("style",style,null); // $NON-NLS-1$
}
String logoImg = configuration.getNavbarLogo();
String logoAlt = configuration.getNavbarLogoAlt();
if(StringUtil.isNotEmpty(logoImg)) {
String clazz = ExtLibUtil.concatStyleClasses("navbar-brand-img", configuration.getNavbarLogoStyleClass()); // $NON-NLS-1$
w.writeAttribute("class", clazz, null); // $NON-NLS-1$
String imgSrc = HtmlRendererUtil.getImageURL(context, logoImg);
w.startElement("img",c); // $NON-NLS-1$
w.writeURIAttribute("src",imgSrc,null); // $NON-NLS-1$
if(ExtLibRenderUtil.isAltPresent(logoAlt)) {
w.writeAttribute("alt",logoAlt,null); // $NON-NLS-1$
}
w.endElement("img"); // $NON-NLS-1$
}
w.endElement("div"); // $NON-NLS-1$
}
protected void writeNavbarText(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration, boolean navbarInverted) throws IOException {
String navbarText = configuration.getNavbarText();
if(StringUtil.isNotEmpty(navbarText)) {
w.startElement("div",c); // $NON-NLS-1$
String clazz = ExtLibUtil.concatStyleClasses("navbar-brand-txt xspSimpleNavbarText", configuration.getNavbarTextStyleClass()); // $NON-NLS-1$
if(StringUtil.isNotEmpty(clazz)) {
w.writeAttribute("class", clazz, null); // $NON-NLS-1$
}
String fullStyle = navbarInverted ? "color: #DDDDDD; " : ""; // $NON-NLS-1$
String titleStyle = configuration.getNavbarTextStyle();
if (StringUtil.isNotEmpty(titleStyle)) {
fullStyle += titleStyle;
}
if(StringUtil.isNotEmpty(fullStyle)) {
w.writeAttribute("style", fullStyle, null); // $NON-NLS-1$
}
w.writeText(navbarText, null);
w.endElement("div"); // $NON-NLS-1$
}
}
protected void writeNavbarApplicationLinks(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration) throws IOException {
ITree tree = TreeImpl.get(configuration.getNavbarAppLinks());
if (tree != null) {
AbstractTreeRenderer renderer = new ApplicationLinksRenderer();
if (renderer != null) {
renderer.render(context, c, "al", tree, w); // $NON-NLS-1$
}
}
}
protected void writeNavbarUtilityLinks(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration) throws IOException {
ITree tree = TreeImpl.get(configuration.getNavbarUtilityLinks());
if (tree != null) {
AbstractTreeRenderer renderer = new UtilityLinksRenderer();
if (renderer != null) {
renderer.render(context, c, "ul", tree, w); // $NON-NLS-1$
}
}
}
// ================================================================
// Search Bar (normally part of the title bar)
// ================================================================
protected void writeSearchBar(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration) throws IOException {
UIComponent cSearchBar = c.getSearchBar();
if (!isEmptyComponent(cSearchBar)) {
if (DEBUG) {
w.writeComment("Start SearchBar Facet"); // $NON-NLS-1$
newLine(w);
}
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class","col-sm-2 col-md-2 navbar-search navbar-right applayout-searchbar",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("role", "search", null); // $NON-NLS-1$ $NON-NLS-2$
FacesUtil.renderComponent(context, cSearchBar);
w.endElement("div"); // $NON-NLS-1$
if (DEBUG) {
w.writeComment("End SearchBar Facet"); // $NON-NLS-1$
newLine(w);
}
return;
}
SearchBar searchBar = configuration.getSearchBar();
if (searchBar != null && searchBar.isRendered()) {
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class","col-sm-3 col-md-2 navbar-search navbar-right input-group applayout-searchbar",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("role", "search", null); // $NON-NLS-1$ $NON-NLS-2$
newLine(w);
boolean searchOptions = false;
ITree tree = TreeImpl.get(searchBar.getOptions());
if (tree != null) {
searchOptions = true;
}
// Write the search options
if (searchOptions) {
writeSearchOptions(context, w, c, configuration, searchBar, tree);
}
// Write the search box
writeSearchBox(context, w, c, configuration, searchBar, tree, searchOptions);
writeSearchButton(context, w, c, configuration, searchBar, tree, searchOptions);
w.endElement("div"); // $NON-NLS-1$
newLine(w);
}
}
protected void writeSearchOptions(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration, SearchBar searchBar, ITree tree) throws IOException {
AbstractTreeRenderer renderer = getSearchOptionsRenderer(context, w, c, configuration, searchBar);
if (renderer != null) {
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class","input-group-btn",null); // $NON-NLS-1$ $NON-NLS-2$
// Feels like a hack...
w.writeAttribute("style","width: 30%",null); // $NON-NLS-1$ $NON-NLS-2$
newLine(w);
renderer.render(context, c, "so", tree, w); // $NON-NLS-1$
w.endElement("div"); // $NON-NLS-1$
}
}
protected AbstractTreeRenderer getSearchOptionsRenderer(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration, SearchBar searchBar) {
String cid = c.getClientId(context) + "_searchopt"; // $NON-NLS-1$
ComboBoxRenderer renderer = new SearchOptionsRenderer();
renderer.setClientId(cid);
String scopeTitle = searchBar.getScopeTitle();
if (null == scopeTitle) {
scopeTitle = "";
}
if (StringUtil.isNotEmpty(scopeTitle)) {
renderer.setAccTitle(scopeTitle);
}
return renderer;
}
protected void writeSearchBox(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration, SearchBar searchBar, ITree tree, boolean options) throws IOException {
String cid = c.getClientId(context) + "_search"; // $NON-NLS-1$
w.startElement("input", c); // $NON-NLS-1$
w.writeAttribute("id", cid, null); // $NON-NLS-1$
w.writeAttribute("name", cid, null); // $NON-NLS-1$
w.writeAttribute("type", "text", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("class", "form-control search-query", null); // $NON-NLS-1$ $NON-NLS-2$
String inputTitle = searchBar.getInputTitle();
if (StringUtil.isNotEmpty(inputTitle)) {
w.writeAttribute("title", inputTitle, null); // $NON-NLS-1$
}
String inactiveText = searchBar.getInactiveText();
if (StringUtil.isNotEmpty(inactiveText)) {
w.writeAttribute("placeHolder", inactiveText, null); // $NON-NLS-1$
}
String submitSearch = "_xspAppSearchSubmit"; // $NON-NLS-1$
w.writeAttribute("onkeypress", "javascript:var kc=event.keyCode?event.keyCode:event.which;if(kc==13){"+submitSearch+"(); return false}",null); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
w.endElement("input"); // $NON-NLS-1$
newLine(w);
}
protected void writeSearchButton(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration, SearchBar searchBar, ITree tree, boolean searchOptions) throws IOException {
String submitSearch = "_xspAppSearchSubmit"; // $NON-NLS-1$
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class","input-group-btn",null); // $NON-NLS-1$ $NON-NLS-2$
newLine(w);
// Write the required script (done here because of Bootstrap 3 last-child selector on the input-group-btn)
writeSearchScript(context, w, c, configuration, searchBar, tree, searchOptions);
newLine(w);
w.startElement("button",c); // $NON-NLS-1$
w.writeAttribute("class","btn btn-default applayout-searchbtn",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("onclick","javascript:"+submitSearch+"(); return false;",null); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
w.startElement("span",c); // $NON-NLS-1$
w.writeAttribute("class", Resources.get().getIconClass("search"),null); // $NON-NLS-1$ $NON-NLS-2$
w.endElement("span"); // $NON-NLS-1$
w.endElement("button"); // $NON-NLS-1$
w.endElement("div"); // $NON-NLS-1$
}
protected void writeSearchScript(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration, SearchBar searchBar, ITree tree, boolean options) throws IOException {
String cid = c.getClientId(context) + "_search"; // $NON-NLS-1$
String submitSearch = "_xspAppSearchSubmit"; // $NON-NLS-1$
// "/search.xsp"
String searchPageName = searchBar.getPageName();
if( StringUtil.isEmpty(searchPageName) ){
searchPageName = "/";
}else{
// append .xsp if needed
searchPageName = ExtLibUtil.getPageXspUrl(searchPageName);
}
// "/apps/XPagesExt.nsf/search.xsp"
String path = context.getApplication().getViewHandler().getResourceURL(context, searchPageName);
path = context.getExternalContext().encodeActionURL(path);
// Compose the script function
w.startElement("script",c); // $NON-NLS-1$
if(DEBUG) { newLine(w); }
StringBuilder sb = new StringBuilder();
sb.append("function "); // $NON-NLS-1$
sb.append(submitSearch);
sb.append("(){");
if(DEBUG) { sb.append('\n'); }
//sb.append("var val=XSP.getElementById('"); sb.append(cid); sb.append("').value;");
sb.append("var val=XSP.getFieldValue(XSP.getElementById('"); sb.append(cid); sb.append("'));"); // $NON-NLS-1$
if(DEBUG) { sb.append('\n'); }
if(options) {
String oid = c.getClientId(context)+"_searchopt"; // $NON-NLS-1$
sb.append("var opt=XSP.getFieldValue(XSP.getElementById('"); sb.append(oid); sb.append("'));"); // $NON-NLS-1$
if(DEBUG) { sb.append('\n'); }
}
sb.append("if(val){var loc='"); // $NON-NLS-1$
JSUtil.appendJavaScriptString(sb, path);
sb.append("?");
String queryParam = searchBar.getQueryParam();
if(StringUtil.isEmpty(queryParam)) {
queryParam = "search"; // $NON-NLS-1$
}
JSUtil.appendJavaScriptString(sb, queryParam);
sb.append("='+encodeURIComponent(val)"); // $NON-NLS-1$
if(options) {
sb.append("+'&");
String optionsParam = searchBar.getOptionsParam();
if(StringUtil.isEmpty(optionsParam)) {
optionsParam = "option"; // $NON-NLS-1$
}
JSUtil.appendJavaScriptString(sb, optionsParam);
sb.append("='+encodeURIComponent(opt)"); // $NON-NLS-1$
}
sb.append(";");
if(DEBUG) { sb.append('\n'); }
sb.append("window.location.href=loc;}}"); // $NON-NLS-1$
w.writeText(sb.toString(),null);
if(DEBUG) { newLine(w); }
w.endElement("script"); // $NON-NLS-1$
}
// ================================================================
// Main content
// ================================================================
protected void writeMainContent(FacesContext context, ResponseWriter w, UIApplicationLayout c,
SimpleResponsiveApplicationConfiguration configuration, boolean collapseLeftColumn, String pageWidthClass, String collapseLeftTarget,
String collapseLeftColumnButtonLabel) throws IOException {
//container div
w.startElement("div",c); // $NON-NLS-1$
w.writeAttribute("role", "main", null); // $NON-NLS-1$ $NON-NLS-2$
// Empty pageWidthClass means pageWidth=none, therefore add no container class and no row
if (StringUtil.isNotEmpty(pageWidthClass)) {
w.writeAttribute("class", pageWidthClass, null); // $NON-NLS-1$
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class", "row", null); // $NON-NLS-1$ $NON-NLS-2$
}
boolean left = !isEmptyComponent(c.getLeftColumn());
boolean right = !isEmptyComponent(c.getRightColumn());
int contentSize = (Integer)getProperty(PROP_CONTENT_SIZE);
int leftSize = 0;
if(left) {
leftSize = (Integer)getProperty(PROP_LEFT_SIZE);
contentSize -= leftSize;
}
int rightSize = 0;
if(right) {
rightSize = (Integer)getProperty(PROP_LEFT_SIZE);
contentSize -= rightSize;
}
// Write the 3 columns
writeLeftColumn(context, w, c, configuration, collapseLeftColumn, collapseLeftTarget, collapseLeftColumnButtonLabel);
writeContentColumn(context, w, c, contentSize, leftSize, configuration, collapseLeftColumn);
writeRightColumn(context, w, c, rightSize, configuration, collapseLeftColumn);
// Close the main content
if (StringUtil.isNotEmpty(pageWidthClass)) {
w.endElement("div"); // $NON-NLS-1$
newLine(w, "row"); // $NON-NLS-1$
}
w.endElement("div"); // $NON-NLS-1$
newLine(w, "container"); // $NON-NLS-1$
}
protected void writeLeftColumn(FacesContext context, ResponseWriter w, UIApplicationLayout c, SimpleResponsiveApplicationConfiguration configuration,
boolean collapseLeftColumn, String collapseLeftTarget, String collapseLeftButtonLabel) throws IOException {
UIComponent left = c.getLeftColumn();
if (!isEmptyComponent(left)) {
// Write the medium/ large screen css classes
// if the collapseLeftColumn option is set, the large screen component is hidden on smaller screens
w.startElement("div", c); // $NON-NLS-1$
if (collapseLeftColumn) {
w.writeAttribute("class", getLeftColumnClasses(collapseLeftColumn) + " hidden-xs hidden-sm applayout-column-left sidebar", null); // $NON-NLS-1$ $NON-NLS-2$
} else {
w.writeAttribute("class", getLeftColumnClasses(collapseLeftColumn) + " applayout-column-left sidebar", null); // $NON-NLS-1$ $NON-NLS-2$
}
FacesUtil.renderComponent(context, left);
w.endElement("div"); // $NON-NLS-1$
newLine(w); // $NON-NLS-1$
if (collapseLeftColumn) {
// Write the small screen component (collapsed menu)
w.startElement("script", c); // $NON-NLS-1$
w.writeText("dojo.addOnLoad( function() { XTB.initCollapsibleMenu('" + collapseLeftButtonLabel + "', '" + collapseLeftTarget + "'); } );", null); // $NON-NLS-1$
w.endElement("script"); // $NON-NLS-1$
newLine(w);
}
}
}
protected void writeRightColumn(FacesContext context, ResponseWriter w, UIApplicationLayout c, int size, SimpleResponsiveApplicationConfiguration configuratio, boolean collapseLeftColumnn) throws IOException {
UIComponent right = c.getRightColumn();
UIComponent left = c.getLeftColumn();
if (!isEmptyComponent(right)) {
w.startElement("div", c); // $NON-NLS-1$
w.writeAttribute("class", getRightColumnClasses(!isEmptyComponent(left), collapseLeftColumnn) + " applayout-column-right", null); // $NON-NLS-1$ $NON-NLS-2$
FacesUtil.renderComponent(context, right);
w.endElement("div"); // $NON-NLS-1$
newLine(w);
}
}
protected void writeContentColumn(FacesContext context, ResponseWriter w, UIApplicationLayout c, int contentSize, int leftSize, SimpleResponsiveApplicationConfiguration configuration, boolean collapseLeftColumnn) throws IOException {
if (!isEmptyChildren(c)) {
w.startElement("div", c); // $NON-NLS-1$
boolean left = !isEmptyComponent(c.getLeftColumn());
boolean right = !isEmptyComponent(c.getRightColumn());
String contentClass = getContentColumnClasses(right, left, collapseLeftColumnn) + " applayout-content"; // $NON-NLS-1$
w.writeAttribute("class", contentClass, null); // $NON-NLS-1$
renderChildren(context, c);
w.endElement("div"); // $NON-NLS-1$
newLine(w);
}
}
protected String getContainerClass(SimpleResponsiveApplicationConfiguration configuration) throws IOException {
if(configuration != null) {
String pageWidth = configuration.getPageWidth();
if ( StringUtil.isNotEmpty(pageWidth)) {
if(pageWidth.equals(ResponsiveApplicationConfiguration.WIDTH_FLUID)) {
return "container-fluid"; // $NON-NLS-1$
} else if ( pageWidth.equals(ResponsiveApplicationConfiguration.WIDTH_FIXED)) {
return "container"; // $NON-NLS-1$
} else if ( pageWidth.equals(ResponsiveApplicationConfiguration.WIDTH_FULL)) {
return "container-full"; // $NON-NLS-1$
} else if ( pageWidth.equals(ResponsiveApplicationConfiguration.WIDTH_NONE)) {
return ""; // $NON-NLS-1$
}
}
}
// Fluid container by default
return "container-fluid"; // $NON-NLS-1$
}
// ==================================================================
// JSF renderer methods
@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();
UIApplicationLayout c = (UIApplicationLayout) component;
if (!c.isRendered()) {
return;
}
ApplicationConfiguration _conf = c.findConfiguration();
if (!(_conf instanceof SimpleResponsiveApplicationConfiguration)) {
return;
}
SimpleResponsiveApplicationConfiguration configuration = (SimpleResponsiveApplicationConfiguration) _conf;
writeMainFrame(context, w, c, configuration);
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
// All is done is encode begin...
}
@Override
public boolean getRendersChildren() {
return true;
}
@Override
public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
// Forget about the children, only the facets are rendered
}
// ==================================================================
// Renderer utilities
/*
* Get the CSS classes for the content of the dashboard layout.
* In Bootstrap the number of columns is 12. The content size in medium
* and large displays will be 12, unless there is a left facet and/or right
* facet, in which case the content size is reduced accordingly to 10 or 8.
* In small and extra small displays, the content size is always 12.
* When there is a left column facet, the content needs an offset
* class added as well, offset to the size of the left facet
*/
protected String getContentColumnClasses(boolean isRightCol, boolean isLeftCol, boolean isLeftColCollapsible) {
String colClasses = "";
int finalContentSize = (Integer)getProperty(PROP_CONTENT_SIZE);
int finalSmallContentSize = (Integer)getProperty(PROP_CONTENT_SIZE);
if(isLeftCol) {
finalContentSize -= (Integer)getProperty(PROP_LEFT_SIZE);
finalSmallContentSize -= isLeftColCollapsible ? 0 : (Integer)getProperty(PROP_SMALL_LEFT_SIZE);
colClasses = isLeftColCollapsible ? colClasses : ExtLibUtil.concatStyleClasses(colClasses, COLUMN_SMALL_OFFSET + (Integer)getProperty(PROP_SMALL_LEFT_SIZE));
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_MEDIUM_OFFSET + (Integer)getProperty(PROP_LEFT_SIZE));
}
if(isRightCol) {
finalContentSize -= (Integer)getProperty(PROP_RIGHT_SIZE);
}
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_SMALL + finalSmallContentSize);
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_MEDIUM + finalContentSize);
return colClasses;
}
/*
* Get the CSS classes for the left column facet of the dashboard layout.
* In Bootstrap the number of columns is 12. The content size in medium
* and large displays will be 2. In small displays, the content size is 3,
* unless the left column has been set to be collapsible, in which case
* it is collapsed into a button with dropdown menu, and needs no size.
*/
protected String getLeftColumnClasses(boolean isLeftColCollapsible) {
String colClasses = "";
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_SMALL + (Integer)getProperty(PROP_SMALL_LEFT_SIZE));
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_MEDIUM + (Integer)getProperty(PROP_LEFT_SIZE));
return colClasses;
}
/*
* Get the CSS classes for the right column facet of the dashboard layout.
* In Bootstrap the number of columns is 12. The content size in medium
* and large displays will be 2. In small displays, the content size is 3.
* When there is a left column facet, the right facet needs an offset
* class added as well, offset to the size of the left facet
*/
protected String getRightColumnClasses(boolean isLeftCol, boolean isLeftColCollapsible) {
String colClasses = "";
if(isLeftCol) {
colClasses = isLeftColCollapsible ? colClasses : ExtLibUtil.concatStyleClasses(colClasses, COLUMN_SMALL_OFFSET + (Integer)getProperty(PROP_SMALL_LEFT_SIZE));
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_SMALL + ((Integer)getProperty(PROP_CONTENT_SIZE) - (isLeftColCollapsible ? 0 : (Integer)getProperty(PROP_SMALL_LEFT_SIZE))));
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_MEDIUM_OFFSET + 0);
}else{
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_SMALL + (Integer)getProperty(PROP_SMALL_RIGHT_SIZE));
}
colClasses = ExtLibUtil.concatStyleClasses(colClasses, COLUMN_MEDIUM + (Integer)getProperty(PROP_RIGHT_SIZE));
return colClasses;
}
// we should not render the children as we don't want to render the
// event hander (we directly generate calls to the fireEvent methods
// rather than attaching event here.
protected void renderChildren(FacesContext context, UIComponent component) throws IOException {
// encode component and children
int count = component.getChildCount();
if (count > 0) {
List<?> children = component.getChildren();
for (int i = 0; i < count; i++) {
UIComponent child = (UIComponent) children.get(i);
if (isRenderChild(context, child)) {
FacesUtil.renderComponent(context, child);
}
}
}
}
protected boolean isRenderChild(FacesContext context, UIComponent child) throws IOException {
// Only render the non event handler components
if (!(child instanceof XspEventHandler)) {
return true;
}
return false;
}
protected boolean isEmptyComponent(UIComponent c) {
// If the component is null, then it is considered as empty
if (c == null) {
return true;
}
// If it is not rendered, then it is empty as well
if (!c.isRendered()) {
return true;
}
// Else, if it is a UICallback, then we should check it content
// a UICallback without anything in it should be considered as
// and empty component.
if (c instanceof UICallback) {
if (c.getChildCount() > 0) {
for (Object child : c.getChildren()) {
if (!isEmptyComponent((UIComponent) child)) {
return false;
}
}
}
if (c.getFacetCount() > 0) {
for (Object child : c.getFacets().values()) {
if (!isEmptyComponent((UIComponent) child)) {
return false;
}
}
}
return true;
}
// Ok, the component exists so it is not considered as empty
return false;
}
protected boolean isEmptyChildren(UIComponent c) {
if (c.getChildCount() > 0) {
// We should check the children one by one...
for (UIComponent child : TypedUtil.getChildren(c)) {
if (!isEmptyComponent(child)) {
return false;
}
}
}
// No children, so the list is empty
return true;
}
}