/* * � Copyright IBM Corp. 2010, 2013, 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.extlib.renderkit.html_extended.outline.tree; import java.io.IOException; import java.util.Map; 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.outline.UIOutlineNavigator; import com.ibm.xsp.extlib.tree.ITreeNode; import com.ibm.xsp.extlib.util.ExtLibRenderUtil; import com.ibm.xsp.extlib.util.ExtLibUtil; import com.ibm.xsp.extlib.util.ThemeUtil; import com.ibm.xsp.util.JSUtil; import com.ibm.xsp.util.TypedUtil; public class MenuRenderer extends HtmlListRenderer { protected static final int PROP_MENU_SECTION = 0; protected static final int PROP_MENU_SUBSECTION = 1; protected static final int PROP_MENU_MENU = 2; protected static final int PROP_MENU_BOTTOMCORNER = 3; protected static final int PROP_MENU_INNER = 4; protected static final int PROP_MENU_HEADER = 5; protected static final int PROP_MENU_SELECTED = 6; protected static final int PROP_MENU_EXPANDED = 7; protected static final int PROP_MENU_COLLAPSED = 8; protected static final int PROP_MENU_SECTION_HEADING = 9; protected static final int PROP_MENU_SECTION_LINK_TITLE = 10; private static final long serialVersionUID = 1L; private boolean expandable; private String expandEffect; private int expandLevel; public MenuRenderer() { } public MenuRenderer(UIComponent component) { super(component); } @Override protected boolean renderCollapsedChildren() throws IOException { // We render the children if the menu is exandable return isExpandable(); } @Override protected Object getProperty(int prop) { { // translating some extra strings that are unused here in the extlib.control plugin, // but are used in the other themes - e.g. the bootstrap MenuRenderer. String str = ""; str = "Navigation menu"; // $NLS-MenuRenderer.Navigationmenu-1$ str = "Collapsed section"; // $NLS-MenuRenderer.Collapsedsection-1$ str = "Expanded section"; // $NLS-MenuRenderer.Expandedsection-1$ str = "Menu"; // $NLS-MenuRenderer.Menu-1$ // end xe:navigator strings str.getClass(); // prevent unused variable warning }// end translating extra string switch(prop) { case PROP_MENU_SECTION: return "lotusMenuSection"; // $NON-NLS-1$ case PROP_MENU_SUBSECTION: return "lotusMenuSubsection"; // $NON-NLS-1$ case PROP_MENU_MENU: return "lotusMenu"; // $NON-NLS-1$ case PROP_MENU_BOTTOMCORNER: return "lotusBottomCorner"; // $NON-NLS-1$ case PROP_MENU_INNER: return "lotusInner"; // $NON-NLS-1$ case PROP_MENU_HEADER: return "lotusMenuHeader"; // $NON-NLS-1$ case PROP_MENU_SELECTED: return "lotusSelected"; // $NON-NLS-1$ case PROP_MENU_EXPANDED: return "lotusSprite lotusArrow lotusTwistyOpenMenu"; // $NON-NLS-1$ case PROP_MENU_COLLAPSED: return "lotusSprite lotusArrow lotusTwistyClosedMenu"; // $NON-NLS-1$ case PROP_MENU_SECTION_HEADING: return "lotusHeading"; // $NON-NLS-1$ case PROP_MENU_SECTION_LINK_TITLE: return "Click to expand or collapse this section"; // $NLS-MenuRenderer.Clicktoexpandorcollapsethissectio-1$ } return super.getProperty(prop); } public boolean isExpandable() { return expandable; } public void setExpandable(boolean expandable) { this.expandable = expandable; } public String getExpandEffect() { return expandEffect; } public void setExpandEffect(String expandEffect) { this.expandEffect = expandEffect; } public int getExpandLevel() { return expandLevel; } public void setExpandLevel(int expandLevel) { this.expandLevel = expandLevel; } @Override protected void startRenderContainer(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException { if(tree.getDepth()>1) { writer.startElement("div", null); // $NON-NLS-1$ writer.writeAttribute("class", (String)getProperty(PROP_MENU_SUBSECTION), null); // $NON-NLS-1$ writer.writeAttribute("style", "margin-top: 0",null); // $NON-NLS-1$ $NON-NLS-2$ } super.startRenderContainer(context, writer, tree); } @Override protected void endRenderContainer(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException { super.endRenderContainer(context, writer, tree); if(tree.getDepth()>1) { writer.endElement("div"); // $NON-NLS-1$ } } @Override protected void renderEntrySeparator(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException { boolean enabled = tree.getNode().isEnabled(); boolean selected = tree.getNode().isSelected(); writer.startElement("li", null); // $NON-NLS-1$ writer.startElement("div", null); // $NON-NLS-1$ String style = getItemStyle(tree,enabled,selected); if(StringUtil.isNotEmpty(style)) { writer.writeAttribute("style",style,null); // $NON-NLS-1$ } String styleClass = ExtLibUtil.concatStyleClasses((String)getProperty(PROP_MENU_SECTION),getItemStyleClass(tree,enabled,selected)); if(StringUtil.isNotEmpty(styleClass)) { writer.writeAttribute("class",styleClass,null); // $NON-NLS-1$ } writer.endElement("div"); // $NON-NLS-1$ writer.endElement("li"); // $NON-NLS-1$ } @Override protected void preRenderTree(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException { // Add the JS support if necessary // if(isExpandable()) { // UIViewRootEx rootEx = (UIViewRootEx) context.getViewRoot(); // rootEx.setDojoTheme(true); // ExtLibResources.addEncodeResource(rootEx, OneUIResources.oneUINavigator); // // Specific dojo effects // String effect = getExpandEffect(); // if(StringUtil.isNotEmpty(effect)) { // rootEx.addEncodeResource(ExtLibResources.dojoFx); // ExtLibResources.addEncodeResource(rootEx, ExtLibResources.dojoFx); // } // } writer.startElement("div", null); // $NON-NLS-1$ writer.writeAttribute("class", (String)getProperty(PROP_MENU_MENU),null); // $NON-NLS-1$ // Accessibility writer.writeAttribute("role", "tree",null); // $NON-NLS-1$ $NON-NLS-2$ UIComponent component = tree.getComponent(); UIOutlineNavigator tcomponent = component instanceof UIOutlineNavigator ? (UIOutlineNavigator)component : null; // aria-label String ariaLabel = ""; if (tcomponent != null) { ariaLabel = tcomponent.getAriaLabel(); } if (StringUtil.isNotEmpty(ariaLabel)) { writer.writeAttribute("aria-label", ariaLabel, null); // $NON-NLS-1$ } writeClientIdIfNecessary(context, writer, tree); writer.startElement("div", null); // $NON-NLS-1$ writer.writeAttribute("class", (String)getProperty(PROP_MENU_BOTTOMCORNER),null); // $NON-NLS-1$ writer.startElement("div", null); // $NON-NLS-1$ writer.writeAttribute("class", (String)getProperty(PROP_MENU_INNER),null); // $NON-NLS-1$ $NON-NLS-2$ if(ThemeUtil.isOneUIVersionAtLeast(context, 2, 1)) { // Should actually be for OneUI 3.0 writer.startElement("header", null); // $NON-NLS-1$ writer.writeAttribute("class", (String)getProperty(PROP_MENU_HEADER),null); // $NON-NLS-1$ $NON-NLS-2$ } } @Override protected void postRenderTree(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException { if(ThemeUtil.isOneUIVersionAtLeast(context, 2, 1)) { writer.endElement("header"); // $NON-NLS-1$ } writer.endElement("div"); // $NON-NLS-1$ writer.endElement("div"); // $NON-NLS-1$ writer.endElement("div"); // $NON-NLS-1$ } @Override protected void renderEntryItemContent(FacesContext context, ResponseWriter writer, TreeContextImpl tree, boolean enabled, boolean selected) throws IOException { boolean section = tree.getNode().getType()!=ITreeNode.NODE_LEAF; if(section && isExpandable()) { if(selected) { writer.writeAttribute("class", "selected",null); // $NON-NLS-1$ $NON-NLS-2$ } writer.startElement("a", null); int depth = tree.getDepth()-2; UIComponent uiTree = tree.getComponent(); boolean keepState = false; if (uiTree instanceof UIOutlineNavigator) { keepState = ((UIOutlineNavigator)uiTree).isKeepState(); } String nodeId = tree.getClientId(context, "node", tree.getDepth());//$NON-NLS-1$ boolean userExpanded = false; boolean userCollapsed = false; if (keepState) { Map<String, String> params = TypedUtil.getRequestParameterMap(context.getExternalContext()); String value = params.get(nodeId); if (!StringUtil.isEmpty(value)) { if (value.equals("1")) { // $NON-NLS-1$ userExpanded = true; } else if (value.equals("0")) { // $NON-NLS-1$ userCollapsed = true; } } } if (userExpanded || userCollapsed) { if (userExpanded) { writer.writeAttribute("class", (String)getProperty(PROP_MENU_EXPANDED),null); // $NON-NLS-1$ } else { writer.writeAttribute("class", (String)getProperty(PROP_MENU_COLLAPSED),null); // $NON-NLS-1$ tree.getNodeContext().setHidden(true); } } else { boolean expanded = depth<expandLevel && tree.getNode().isExpanded(); if(expanded) { writer.writeAttribute("class", (String)getProperty(PROP_MENU_EXPANDED),null); // $NON-NLS-1$ } else { writer.writeAttribute("class", (String)getProperty(PROP_MENU_COLLAPSED),null); // $NON-NLS-1$ tree.getNodeContext().setHidden(true); } } // OneUI v2.1 looks better with this... if(ThemeUtil.isOneUIVersion(context, 2, 1)) { writer.writeAttribute("style", "padding: 0px; margin-top: 4px;",null); // $NON-NLS-1$ $NON-NLS-2$ } else { writer.writeAttribute("style", "padding: 0px",null); // $NON-NLS-1$ $NON-NLS-2$ } writer.writeAttribute("role", "button",null); // $NON-NLS-1$ $NON-NLS-2$ writer.writeAttribute("href", "javascript:",null); // $NON-NLS-1$ $NON-NLS-2$ StringBuilder b = new StringBuilder(); b.append("javascript:XSP.oneUIMenuSwap(event,"); //$NON-NLS-1$ JSUtil.addSingleQuoteString(b, getExpandEffect()); b.append(","); //$NON-NLS-1$ JSUtil.addSingleQuoteString(b, nodeId); b.append(")"); //$NON-NLS-1$ writer.writeAttribute("onclick", b.toString(),null); // $NON-NLS-1$ String menuSectionLinkTitle = (String)getProperty(PROP_MENU_SECTION_LINK_TITLE); if( ExtLibRenderUtil.isAltPresent(menuSectionLinkTitle) ){ writer.writeAttribute("title", menuSectionLinkTitle,null); // $NON-NLS-1$ } writer.startElement("span", null); //$NON-NLS-1$ writer.writeAttribute("class", "lotusAltText",null); //$NON-NLS-1$ //$NON-NLS-2$ if(userCollapsed || userExpanded){ if(userExpanded){ // down arrow writer.writeText("\u25BC", null); //$NON-NLS-1$ }else{ // right arrow writer.writeText("\u25BA", null); //$NON-NLS-1$ } }else{ boolean isExpanded = depth < expandLevel && tree.getNode().isExpanded(); if(isExpanded){ // down arrow writer.writeText("\u25BC", null); //$NON-NLS-1$ }else{ // right arrow writer.writeText("\u25BA", null); //$NON-NLS-1$ } } writer.endElement("span"); //$NON-NLS-1$ writer.endElement("a"); //$NON-NLS-1$ // Preserve user's Expanded/Collapsed state if (keepState) { writer.startElement("input", uiTree); // $NON-NLS-1$ writer.writeAttribute("type", "hidden", null); // $NON-NLS-1$ $NON-NLS-2$ writer.writeAttribute("id", nodeId, "id"); //$NON-NLS-1$ $NON-NLS-2$ writer.writeAttribute("name", nodeId, "name"); // $NON-NLS-1$ $NON-NLS-2$ if (userExpanded) { writer.writeAttribute("value", "1", "value"); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ } else if (userCollapsed) { writer.writeAttribute("value", "0", "value"); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ } else { writer.writeAttribute("value", "", "value"); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ } writer.endElement("input"); //$NON-NLS-1$ } } if(section) { writer.startElement("h3", null); // $NON-NLS-1$ writer.writeAttribute("class", (String)getProperty(PROP_MENU_SECTION_HEADING),null); // $NON-NLS-1$ // Accessibility writer.writeAttribute("role", "treeitem",null); // $NON-NLS-1$ $NON-NLS-2$ if(ThemeUtil.isOneUIVersionAtLeast(context, 2, 1)) { writer.writeAttribute("style","padding-left: 0px;",null); // $NON-NLS-1$ $NON-NLS-2$ } super.renderEntryItemContent(context, writer, tree, enabled, selected); writer.endElement("h3"); // $NON-NLS-1$ } else { super.renderEntryItemContent(context, writer, tree, enabled, selected); } } @Override protected void renderEntryItemLinkAttributes(FacesContext context, ResponseWriter writer, TreeContextImpl tree, boolean enabled, boolean selected) throws IOException { boolean section = tree.getNode().getType()!=ITreeNode.NODE_LEAF; if(section) { if(ThemeUtil.isOneUIVersionAtLeast(context, 2, 1)) { writer.writeAttribute("style","text-decoration:none; position: static; padding-bottom: 0px; padding-top: 0px;",null); // $NON-NLS-1$ $NON-NLS-2$ } else { writer.writeAttribute("style","text-decoration:none; position: static; padding-left: 0px",null); // $NON-NLS-1$ $NON-NLS-2$ } } else { super.renderEntryItemLinkAttributes(context, writer, tree, enabled, selected); } } @Override protected boolean isChildrenSeparate() { // We need the children to be generated in a separate <li>, else the lotusSelected class // applies to the entire hierarchy return true; } @Override protected boolean alwaysRenderItemLink(TreeContextImpl tree, boolean enabled, boolean selected) { // Always render a link tag for the item, even when the text is empty. // Else the menu is not rendered properly // There is currently an issue with OneUI 3.0 where the dojo style reset the h3 style for .oneui30 // if(false) { // boolean section = tree.getNode().getType()!=ITreeNode.NODE_LEAF; // if(section) { // return false; // } // } return true; } @Override protected void renderEntryNodeChildAttributes(FacesContext context, ResponseWriter writer, TreeContextImpl tree, boolean enabled, boolean selected) throws IOException { if(tree.getNodeContext().isHidden()) { writer.writeAttribute("style","display:none",null); // $NON-NLS-1$ $NON-NLS-2$ } } @Override protected String getItemStyleClass(TreeContextImpl tree, boolean enabled, boolean selected) { String s = super.getItemStyleClass(tree, enabled, selected); if(selected) { return ExtLibUtil.concatStyleClasses(s,(String)getProperty(PROP_MENU_SELECTED)); } return s; } }