/*
* � Copyright IBM Corp. 2010, 2011
*
* 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.extlib.renderkit.html_extended.containers;
import java.io.IOException;
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.component.containers.UIWidgetContainer;
import com.ibm.xsp.extlib.component.util.EventHandlerUtil;
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.ITreeNode;
import com.ibm.xsp.extlib.tree.impl.RootContainerTreeNode;
import com.ibm.xsp.extlib.tree.impl.TreeImpl;
import com.ibm.xsp.extlib.util.ExtLibUtil;
import com.ibm.xsp.renderkit.html_basic.HtmlRendererUtil;
import com.ibm.xsp.renderkit.html_extended.RenderUtil;
import com.ibm.xsp.util.FacesUtil;
import com.ibm.xsp.util.JSUtil;
import com.ibm.xsp.util.JavaScriptUtil;
/**
* Widget container renderer.
*/
public class WidgetContainerRenderer extends FacesRendererEx {
// Compatible with UISection for reusing the same script
final protected String _INITCLOSED = "_closed"; // $NON-NLS-1$
final protected String _CONTENTS = "_contents"; // $NON-NLS-1$
final protected String _OPENED = "_open"; // $NON-NLS-1$
final protected String _CLOSED = "_close"; // $NON-NLS-1$
final protected String _LKOPENED = "_lk_open"; // $NON-NLS-1$
final protected String _LKCLOSED = "_lk_close"; // $NON-NLS-1$
final protected String _TITLE = "_title"; // $NON-NLS-1$
// default style class
final protected String _DISPLAY_NONE = "display: none;"; // $NON-NLS-1$
final protected String _DISPLAY_BLOCK = "display: block;"; // $NON-NLS-1$
// ==========================================================================
// Rendering Properties
// ==========================================================================
// Main widget
protected static final int PROP_CSSWIDGETBASIC = 1;
protected static final int PROP_CSSWIDGETSIDEBAR = 2;
protected static final int PROP_CSSWIDGETPLAIN = 3;
protected static final int PROP_CONTAINER_STYLE_DEFAULT = 4;
protected static final int PROP_BLANKIMG = 10;
// Title bar
protected static final int PROP_TAGTITLE = 11;
protected static final int PROP_CSSTITLEBAR = 12;
protected static final int PROP_STYLETITLEBAR = 13;
protected static final int PROP_TAGTITLETEXT = 14;
protected static final int PROP_CSSTITLETEXT = 15;
protected static final int PROP_CSSTITLEIMG = 16;
protected static final int PROP_TREEDROPDOWN = 17;
protected static final int PROP_TITLE_PREVENT_BLANK = 18;
protected static final int PROP_TWISTYCLASSLINK = 19;
protected static final int PROP_TWISTYCLASSIMGOPEN = 20;
protected static final int PROP_TWISTYCLASSIMGCLOSE = 21;
// Header
protected static final int PROP_TAGHEADER = 30;
protected static final int PROP_CSSHEADER = 41;
// Body
protected static final int PROP_CSSBODY = 40;
protected static final int PROP_CSSSCROLLUP = 41;
protected static final int PROP_CSSSCROLLUPLINK = 42;
protected static final int PROP_CSSSCROLLUPALTTEXT = 43;
protected static final int PROP_CSSSCROLLDOWN = 44;
protected static final int PROP_CSSSCROLLDOWNLINK = 45;
protected static final int PROP_CSSSCROLLDOWNALTTEXT = 46;
protected static final int PROP_BODY_PREVENT_BLANK = 47;
// Footer
protected static final int PROP_TAGFOOTER = 60;
protected static final int PROP_CSSFOOTER = 61;
@Override
protected Object getProperty(int prop) {
switch(prop) {
// TODO talk to Tony about the non-OneUI widget container styling.
// TODO dropdown menu not displayed for non-OneUI widget container
// Container div
case PROP_CONTAINER_STYLE_DEFAULT: return "border: solid thin black; padding:0px; margin: 2px;"; //$NON-NLS-1$
// Twisty
// Title Bar
case PROP_TAGTITLE: return "div"; // $NON-NLS-1$
case PROP_TAGTITLETEXT: return "span"; // $NON-NLS-1$
case PROP_TITLE_PREVENT_BLANK: return true;
case PROP_STYLETITLEBAR: return "border-bottom: solid thin black; background-color:#DDDDDD"; //$NON-NLS-1$
// Header
case PROP_TAGHEADER: return "h3"; // $NON-NLS-1$
// Body
// TODO scrollUp and scrollDown buttons not appearing for non-OneUI widget container styling
case PROP_BODY_PREVENT_BLANK: return true;
// Footer
case PROP_TAGFOOTER: return "div"; // $NON-NLS-1$
}
return super.getProperty(prop);
}
@Override
public void decode(FacesContext context, UIComponent component) {
if(component instanceof UIWidgetContainer) {
UIWidgetContainer wc = (UIWidgetContainer)component;
// Get current state from request map
String fieldId = component.getClientId(context) + _INITCLOSED;
String it_closed = (String)context.getExternalContext().getRequestParameterMap().get(fieldId);
if(StringUtil.isNotEmpty(it_closed)) {
if(Boolean.valueOf(it_closed)){
wc.setClosed(true);
} else {
wc.setClosed(false);
}
}
}
}
@Override
public boolean getRendersChildren() {
return true;
}
@Override
public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
}
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
UIWidgetContainer container = (UIWidgetContainer)component;
if(!container.isRendered()) {
return;
}
ResponseWriter w = context.getResponseWriter();
writeMainFrame(context, w, container);
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
}
// ================================================================
// Main Frame
// ================================================================
protected void writeMainFrame(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
// Start the main frame
w.startElement("div",c); // $NON-NLS-1$
String id = c.getClientId(context);
w.writeAttribute("id",id,null); // $NON-NLS-1$
w.writeAttribute("role", "region", null); // $NON-NLS-1$ $NON-NLS-2$
if (c.isTitleBar()) {
w.writeAttribute("aria-labelledby", id+_TITLE, null); // $NON-NLS-1$
}
boolean closed = c.isClosed();
w.writeAttribute("aria-expanded", Boolean.toString(!closed), null); // $NON-NLS-1$
String type = c.getType();
String cls = c.getStyleClass();
if(StringUtil.equals(type, UIWidgetContainer.TYPE_SIDEBAR)) {
cls = ExtLibUtil.concatStyleClasses(cls, (String)getProperty(PROP_CSSWIDGETSIDEBAR));
} else if(StringUtil.equals(type, UIWidgetContainer.TYPE_PLAIN)) {
cls = ExtLibUtil.concatStyleClasses(cls, (String)getProperty(PROP_CSSWIDGETPLAIN));
} else {
cls = ExtLibUtil.concatStyleClasses(cls, (String)getProperty(PROP_CSSWIDGETBASIC));
}
if(StringUtil.isNotEmpty(cls)) {
w.writeAttribute("class",cls,null); // $NON-NLS-1$
}
String style = c.getStyle();
if( StringUtil.isEmpty(style) ){
// note, this default style is not concat'd
style = (String) getProperty(PROP_CONTAINER_STYLE_DEFAULT);
}
if(StringUtil.isNotEmpty(style)) {
w.writeAttribute("style",style,null); // $NON-NLS-1$
}
newLine(w);
// Write the title bar
if(c.isTitleBar()) {
writeTitleBar(context, w, c);
}
// Write the widget content (the children)
writeContent(context, w, c);
// Write the footer, if any
writeFooter(context, w, c);
// Close the main frame
w.endElement("div"); // $NON-NLS-1$
newLine(w);
}
// ================================================================
// TitleBar
// ================================================================
protected void writeTitleBar(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
String tag = (String)getProperty(PROP_TAGTITLE);
w.startElement(tag,c);
String id = c.getClientId(context);
w.writeAttribute("id",id+_TITLE,null); // $NON-NLS-1$
String cls = (String)getProperty(PROP_CSSTITLEBAR);
if(StringUtil.isNotEmpty(cls)) {
w.writeAttribute("class",cls,null); // $NON-NLS-1$
}
String style = (String)getProperty(PROP_STYLETITLEBAR);
if(StringUtil.isNotEmpty(style)) {
w.writeAttribute("style",style,null); // $NON-NLS-1$
}
// Write the collapsible arrow
writeCollapsible(context, w, c);
// Write the title
writeTitle(context, w, c);
// Write the dropdown menu
if(c.isDropDownRendered()) {
writeDropDown(context, w, c);
}
w.endElement(tag);
writeCollapsibleInput(context, w, c);
}
protected void writeCollapsibleInput(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
if(c.isCollapsible()) {
// Add a hidden field to cary out the section state
String id = c.getClientId(context);
boolean closed = c.isClosed();
w.startElement("input",c); // this is for the uistate $NON-NLS-1$
w.writeAttribute("id",id + _INITCLOSED,null); // $NON-NLS-1$
w.writeAttribute("name",id + _INITCLOSED,null); // $NON-NLS-1$
w.writeAttribute("type","hidden",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("value",Boolean.toString(closed),null); // closed = true means section will be closed $NON-NLS-1$
w.endElement("input"); // $NON-NLS-1$
// Initialize the script for the links
StringBuilder buff = new StringBuilder();
JavaScriptUtil.appendInitSectionScript(
context,
buff,
id+_LKCLOSED,
id,
true);
JavaScriptUtil.appendInitSectionScript(
context,
buff,
id+_LKOPENED,
id,
false);
JavaScriptUtil.addScriptOnLoad(buff.toString());
}
}
protected void writeCollapsible(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
if(c.isCollapsible()) {
boolean closed = c.isClosed();
String id = c.getClientId(context);
//Open twisty
w.startElement("span",c); // $NON-NLS-1$
w.writeAttribute("id",id+_OPENED,null); // $NON-NLS-1$
if(closed){
w.writeAttribute("style", _DISPLAY_NONE, null); // $NON-NLS-1$
} else {
w.writeAttribute("style", _DISPLAY_BLOCK, null); // $NON-NLS-1$
}
w.startElement("a",c);
w.writeAttribute("id",id+_LKOPENED,null); // $NON-NLS-1$
String clsLk = (String)getProperty(PROP_TWISTYCLASSLINK);
if(StringUtil.isNotEmpty(clsLk)) {
w.writeAttribute("class",clsLk,null); // $NON-NLS-1$
}
w.writeAttribute("role","button",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("href","javascript:;",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("title","click to collapse the section",null); // $NON-NLS-1$ $NLS-WidgetContainerRenderer.clicktocollapsethesection-2$
if (closed) {
// accesskey
String accesskey = c.getAccesskey();
if (StringUtil.isNotEmpty(accesskey)) {
w.writeAttribute("accesskey", accesskey, null); // tabindex $NON-NLS-1$
}
// tabindex
String tabindex = c.getTabindex();
if (StringUtil.isNotEmpty(tabindex)) {
w.writeAttribute("tabindex", tabindex, null); // tabindex $NON-NLS-1$
} else {
w.writeAttribute("tabindex", "0", null); // tabindex $NON-NLS-1$
}
}
w.startElement("span",c); // $NON-NLS-1$
w.writeAttribute("class","lotusAltText", null); // $NON-NLS-1$ $NON-NLS-2$
// up arrow
w.writeText("\u25B2", null); // //$NON-NLS-1$
//<span class="lotusAltText">▼</span></a>
w.endElement("span"); //$NON-NLS-1$
w.startElement("img",c); // $NON-NLS-1$
String collapseStr = "Collapse section"; // $NLS-WidgetContainerRenderer.Collapsesection-1$
w.writeAttribute("aria-label",collapseStr,null); // $NON-NLS-1$
w.writeAttribute("alt",collapseStr,null); // $NON-NLS-1$
String bgif = (String)getProperty(PROP_BLANKIMG);
if(StringUtil.isNotEmpty(bgif)) {
w.writeURIAttribute("src",HtmlRendererUtil.getImageURL(context,bgif),null); // $NON-NLS-1$
}
String clsOpen = (String)getProperty(PROP_TWISTYCLASSIMGOPEN);
if(StringUtil.isNotEmpty(clsOpen)) {
w.writeAttribute("class",clsOpen,null); // $NON-NLS-1$
}
w.endElement("a"); //$NON-NLS-1$
w.endElement("span"); //$NON-NLS-1$
//Close twisty
w.startElement("span",c); // $NON-NLS-1$
w.writeAttribute("id",id+_CLOSED,null); // $NON-NLS-1$
if(closed){
w.writeAttribute("style", _DISPLAY_BLOCK, null); // $NON-NLS-1$
} else {
w.writeAttribute("style", _DISPLAY_NONE, null); // $NON-NLS-1$
}
w.startElement("a",c);
w.writeAttribute("id",id+_LKCLOSED,null); // $NON-NLS-1$
if(StringUtil.isNotEmpty(clsLk)) {
w.writeAttribute("class",clsLk,null); // $NON-NLS-1$
}
w.writeAttribute("role","button",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("href","javascript:;",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("title","click to expand the section",null); // $NON-NLS-1$ $NLS-WidgetContainerRenderer.clicktoexpandthesection-2$
w.startElement("span",c); // $NON-NLS-1$
w.writeAttribute("class","lotusAltText", null); // $NON-NLS-1$ $NON-NLS-2$
// down arrow
w.writeText("\u25BC", null); // //$NON-NLS-1$
//<span class="lotusAltText">▼</span></a>
w.endElement("span"); //$NON-NLS-1$
w.startElement("img",c); // $NON-NLS-1$
String expandStr = "Expand section"; //$NLS-WidgetContainerRenderer.Expandsection-1$
w.writeAttribute("aria-label",expandStr,null); // $NON-NLS-1$
w.writeAttribute("alt",expandStr,null); // $NON-NLS-1$
if(StringUtil.isNotEmpty(bgif)) {
w.writeURIAttribute("src",HtmlRendererUtil.getImageURL(context,bgif),null); // $NON-NLS-1$
}
String clsClose = (String)getProperty(PROP_TWISTYCLASSIMGCLOSE);
if(StringUtil.isNotEmpty(clsClose)) {
w.writeAttribute("class",clsClose,null); // $NON-NLS-1$
}
w.endElement("a"); //$NON-NLS-1$
w.endElement("span"); //$NON-NLS-1$
}
}
protected void writeTitle(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
String title = c.getTitleBarText();
if(StringUtil.isNotEmpty(title)) {
String href = c.getTitleBarHref();
boolean hasLink = StringUtil.isNotEmpty(href);
String tagText = (String)getProperty(PROP_TAGTITLETEXT);
w.startElement(tagText,c);
String cls = (String)getProperty(PROP_CSSTITLETEXT);
if(StringUtil.isNotEmpty(cls)) {
w.writeAttribute("class",cls,null); // $NON-NLS-1$
}
if(hasLink) {
w.startElement("a",c); //$NON-NLS-1$
RenderUtil.writeLinkAttribute(context, w, href);
}
w.writeText(title,null);
if(hasLink) {
w.endElement("a"); //$NON-NLS-1$
}
w.endElement(tagText);
}else{
boolean isTitlePreventBlank = (Boolean) getProperty(PROP_TITLE_PREVENT_BLANK);
if( isTitlePreventBlank ){
JSUtil.writeTextBlank(w); //
}
}
}
protected void writeDropDown(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
List<ITreeNode> nodes = c.getDropDownNodes();
if(nodes!=null && !nodes.isEmpty()) {
ITree tree = TreeImpl.get(new RootContainerTreeNode(c.getDropDownNodes()));
if(tree!=null) {
// TODO the non-OneUI WidgetContainerRenderer will not output dropDownNodes,
// need to update this so its possible.
AbstractTreeRenderer renderer = (AbstractTreeRenderer)getProperty(PROP_TREEDROPDOWN);
if(renderer!=null) {
renderer.render(context, c, tree, w);
}
}
}
}
// ================================================================
// Main content
// ================================================================
protected void writeContent(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
boolean scrollable = c.isScrollable();
w.startElement("div", c); // $NON-NLS-1$
String cls = (String)getProperty(PROP_CSSBODY);
if(StringUtil.isNotEmpty(cls)) {
w.writeAttribute("class", cls, null); // $NON-NLS-1$
}
String id = c.getClientId(context);
w.writeAttribute("id",id+_CONTENTS,null); // $NON-NLS-1$
// Manage the section state
boolean closed = c.isClosed();
if(closed){
w.writeAttribute("style", _DISPLAY_NONE, null); // $NON-NLS-1$
} else {
w.writeAttribute("style", _DISPLAY_BLOCK, null); // $NON-NLS-1$
}
// Write the header, if any
writeHeader(context, w, c);
if(scrollable) {
writeBodyScrollUp(context, w, c);
}
writeBodyContent(context, w, c);
if(scrollable) {
writeBodyScrollDown(context, w, c);
}
w.endElement("div"); // $NON-NLS-1$
}
protected void writeBodyContent(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
FacesUtil.renderChildren(context, c);
if( c.getChildCount() == 0 ){
boolean isBodyPreventBlank = (Boolean) getProperty(PROP_BODY_PREVENT_BLANK);
if( isBodyPreventBlank ){
JSUtil.writeTextBlank(w); //
}
}
}
protected void writeBodyScrollUp(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
if(!c.isDisableScrollUp()) {
w.startElement("div", c); // $NON-NLS-1$
String clsDiv = (String)getProperty(PROP_CSSSCROLLUP);
if(StringUtil.isNotEmpty(clsDiv)) {
w.writeAttribute("class", clsDiv, null); // $NON-NLS-1$
}
w.startElement("a", c);
String clsA = (String)getProperty(PROP_CSSSCROLLUPLINK);
if(StringUtil.isNotEmpty(clsA)) {
w.writeAttribute("class", clsA, null); // $NON-NLS-1$
}
w.writeAttribute("role","button",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("title","Scroll up",null); // $NON-NLS-1$ $NLS-WidgetContainerRenderer.Scrollup-2$
w.writeAttribute("href","javascript:;",null); // $NON-NLS-1$ $NON-NLS-2$
String script = EventHandlerUtil.getEventScript(context, c, "onScrollUp"); // $NON-NLS-1$
if(StringUtil.isNotEmpty(script)) {
w.writeAttribute("onClick", script, null); // $NON-NLS-1$
}
writeBodyScrollUpAltText(context, w, c);
w.endElement("a");
w.endElement("div"); // $NON-NLS-1$
}
}
protected void writeBodyScrollUpAltText(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
String alt = (String)getProperty(PROP_CSSSCROLLUPALTTEXT);
if(StringUtil.isNotEmpty(alt)) {
w.startElement("span", c); // $NON-NLS-1$
w.writeAttribute("class", "lotusAltText", null); //$NON-NLS-1$ //$NON-NLS-2$
w.writeText(alt, "\u25B2"); //$NON-NLS-1$
w.endElement("span"); // $NON-NLS-1$
}
}
protected void writeBodyScrollDown(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
if(!c.isDisableScrollDown()) {
w.startElement("div", c); // $NON-NLS-1$
String clsDiv = (String)getProperty(PROP_CSSSCROLLDOWN);
if(StringUtil.isNotEmpty(clsDiv)) {
w.writeAttribute("class", clsDiv, null); // $NON-NLS-1$
}
w.startElement("a", c);
String clsA = (String)getProperty(PROP_CSSSCROLLDOWNLINK);
if(StringUtil.isNotEmpty(clsA)) {
w.writeAttribute("class", clsA, null); // $NON-NLS-1$
}
w.writeAttribute("role","button",null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("title","Scroll down",null); // $NON-NLS-1$ $NLS-WidgetContainerRenderer.Scrolldown-2$
w.writeAttribute("href","javascript:;",null); // $NON-NLS-1$ $NON-NLS-2$
String script = EventHandlerUtil.getEventScript(context, c, "onScrollDown"); // $NON-NLS-1$
if(StringUtil.isNotEmpty(script)) {
w.writeAttribute("onClick", script, null); // $NON-NLS-1$
}
writeBodyScrollDownAltText(context, w, c);
w.endElement("a");
w.endElement("div"); // $NON-NLS-1$
}
}
protected void writeBodyScrollDownAltText(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
String alt = (String)getProperty(PROP_CSSSCROLLDOWNALTTEXT);
if(StringUtil.isNotEmpty(alt)) {
w.startElement("span", c); // $NON-NLS-1$
w.writeAttribute("class", "lotusAltText", null); //$NON-NLS-1$ //$NON-NLS-2$
w.writeText(alt, "\u25BC"); //$NON-NLS-1$
w.endElement("span"); // $NON-NLS-1$
}
}
// ================================================================
// Header
// ================================================================
protected void writeHeader(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
UIComponent header = c.getFacet(UIWidgetContainer.FACET_HEADER);
if(header!=null) {
String tag = (String)getProperty(PROP_TAGHEADER);
w.startElement(tag, c);
String cls = (String)getProperty(PROP_CSSHEADER);
if(StringUtil.isNotEmpty(cls)) {
w.writeAttribute("class", cls, null); // $NON-NLS-1$
}
FacesUtil.renderChildren(context, header);
w.endElement(tag);
}
}
// ================================================================
// Footer
// ================================================================
protected void writeFooter(FacesContext context, ResponseWriter w, UIWidgetContainer c) throws IOException {
UIComponent footer = c.getFacet(UIWidgetContainer.FACET_FOOTER);
if(footer!=null) {
String tag = (String)getProperty(PROP_TAGFOOTER);
w.startElement(tag, null);
String cls = (String)getProperty(PROP_CSSFOOTER);
if(StringUtil.isNotEmpty(cls)) {
w.writeAttribute("class", cls, null); // $NON-NLS-1$
}
FacesUtil.renderChildren(context, footer);
w.endElement(tag);
}
}
}