All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.custom.navmenu.jscookmenu.HtmlJSCookMenuRenderer Maven / Gradle / Ivy

Go to download

JSF components and utilities that can be used with any JSF implementation. This library is based on the JSF1.1 version of Tomahawk, but with minor source code and build changes to take advantage of JSF1.2 features. A JSF1.2 implementation is required to use this version of the Tomahawk library.

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.myfaces.custom.navmenu.jscookmenu;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.custom.navmenu.NavigationMenuItem;
import org.apache.myfaces.custom.navmenu.NavigationMenuUtils;
import org.apache.myfaces.custom.navmenu.UINavigationMenuItem;
import org.apache.myfaces.renderkit.html.util.AddResource;
import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
import org.apache.myfaces.shared_tomahawk.el.SimpleActionMethodBinding;
import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlFormRendererBase;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRenderer;
import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;
import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;

import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.el.MethodBinding;
import javax.faces.el.ValueBinding;
import javax.faces.event.ActionEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * @JSFRenderer
 *   renderKitId = "HTML_BASIC" 
 *   family = "javax.faces.Command"
 *   type = "org.apache.myfaces.JSCookMenu"
 * 
 * @author Thomas Spiegl
 * @version $Revision: 698742 $ $Date: 2008-09-24 16:25:46 -0500 (Wed, 24 Sep 2008) $
 */
public class HtmlJSCookMenuRenderer
    extends HtmlRenderer {
    private static final String MYFACES_HACK_SCRIPT = "MyFacesHack.js";

    private static final String JSCOOK_MENU_SCRIPT = "JSCookMenu.js";
    
    private static final String JSCOOK_EFFECT_SCRIPT = "effect.js";    

    private static final Log log = LogFactory.getLog(HtmlJSCookMenuRenderer.class);

    private static final String JSCOOK_ACTION_PARAM = "jscook_action";
    private static final Class[] ACTION_LISTENER_ARGS = {ActionEvent.class};

    private static final Map builtInThemes = new java.util.HashMap();

    static {
        builtInThemes.put("ThemeOffice", "ThemeOffice/");
        builtInThemes.put("ThemeMiniBlack", "ThemeMiniBlack/");
        builtInThemes.put("ThemeIE", "ThemeIE/");
        builtInThemes.put("ThemePanel", "ThemePanel/");
        builtInThemes.put("ThemeGray", "ThemeGray/");        
    }

    public void decode(FacesContext context, UIComponent component) {
        RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);

        Map parameter = context.getExternalContext().getRequestParameterMap();
        String actionParam = (String) parameter.get(JSCOOK_ACTION_PARAM);
        if (actionParam != null && !actionParam.trim().equals("") &&
            !actionParam.trim().equals("null")) {
            String compId = getMenuId(context, component);
            StringTokenizer tokenizer = new StringTokenizer(actionParam, ":");
            if (tokenizer.countTokens() > 1) {
                String actionId = tokenizer.nextToken();
                if (! compId.equals(actionId)) {
                    return;
                }
                while (tokenizer.hasMoreTokens()) {
                    String action = tokenizer.nextToken();
                    if (action.startsWith("A]")) {
                        action = action.substring(2, action.length());
                        action = decodeValueBinding(action, context);
                        MethodBinding mb;
                        if (NavigationMenuUtils.isValueReference(action)) {
                            mb = context.getApplication().createMethodBinding(action, null);
                        }
                        else {
                            mb = new SimpleActionMethodBinding(action);
                        }
                        ((HtmlCommandJSCookMenu) component).setAction(mb);
                    }
                    else if (action.startsWith("L]")) {
                        action = action.substring(2, action.length());
                        String value = null;
                        int idx = action.indexOf(";");
                        if (idx > 0 && idx < action.length() - 1) {
                            value = action.substring(idx + 1, action.length());
                            action = action.substring(0, idx);
                            ((HtmlCommandJSCookMenu) component).setValue(value);
                        }
                        else if (idx == action.length() - 1)
                        {
                            value = null; //No Value found, so set it to null as expected
                            action = action.substring(0, idx);
                        }
                        MethodBinding mb;
                        if (NavigationMenuUtils.isValueReference(action)) {
                            mb = context.getApplication().createMethodBinding(action, ACTION_LISTENER_ARGS);
                            ((HtmlCommandJSCookMenu) component).setActionListener(mb);
                            if (value != null)
                                ((HtmlCommandJSCookMenu) component).setValue(value);
                        }
                    }
                }
            }
            component.queueEvent(new ActionEvent(component));
        }
    }

    private String decodeValueBinding(String actionParam, FacesContext context) {
        int idx = actionParam.indexOf(";#{");
        if (idx == -1) {
            return actionParam;
        }

        String newActionParam = actionParam.substring(0, idx);
        String vbParam = actionParam.substring(idx + 1);

        idx = vbParam.indexOf('=');
        if (idx == -1) {
            return newActionParam;
        }
        String vbExpressionString = vbParam.substring(0, idx);
        String vbValue = vbParam.substring(idx + 1);

        ValueBinding vb =
            context.getApplication().createValueBinding(vbExpressionString);
        vb.setValue(context, vbValue);

        return newActionParam;
    }

    public boolean getRendersChildren() {
        return true;
    }

    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);

        List list = NavigationMenuUtils.getNavigationMenuItemList(component);
        if (list.size() > 0) {
            FormInfo parentFormInfo = RendererUtils.findNestingForm(component, context);
            ResponseWriter writer = context.getResponseWriter();

            if (parentFormInfo == null)
                throw new FacesException("jscook menu is not embedded in a form.");
            String formName = parentFormInfo.getFormName();
            List uiNavMenuItemList = component.getChildren();
            /* todo: disabled for now. Check if dummy form stuff is still needed/desired
                if( formName == null ) {
                DummyFormUtils.setWriteDummyForm(context,true);
                DummyFormUtils.addDummyFormParameter(context,JSCOOK_ACTION_PARAM);

                formName = DummyFormUtils.getDummyFormName();
            }
            else {*/
            if (RendererUtils.isAdfOrTrinidadForm(parentFormInfo.getForm())) {
                // need to add hidden input, cause MyFaces form is missing hence will not render hidden inputs
                writer.write("");
            }
            else {
                HtmlFormRendererBase.addHiddenCommandParameter(context, parentFormInfo.getForm(), JSCOOK_ACTION_PARAM);
            }

            //}

            String myId = getMenuId(context, component);

            writer.startElement(HTML.SCRIPT_ELEM, component);
            writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
            StringBuffer script = new StringBuffer();
            script.append("var ").append(getMenuId(context, component)).append(" =\n[");
            encodeNavigationMenuItems(context, script,
                                      (NavigationMenuItem[]) list.toArray(new NavigationMenuItem[list.size()]),
                                      uiNavMenuItemList,
                                      myId, formName);

            script.append("];");
            writer.writeText(script.toString(), null);
            writer.endElement(HTML.SCRIPT_ELEM);
        }
    }

    private void encodeNavigationMenuItems(FacesContext context,
                                           StringBuffer writer,
                                           NavigationMenuItem[] items,
                                           List uiNavMenuItemList,
                                           String menuId, String formName)
        throws IOException {
        for (int i = 0; i < items.length; i++) {
            NavigationMenuItem item = items[i];
            Object tempObj = null;
            UINavigationMenuItem uiNavMenuItem = null;
            if (i < uiNavMenuItemList.size()) {
                tempObj = uiNavMenuItemList.get(i);
            }
            if (tempObj != null) {
                if (tempObj instanceof UINavigationMenuItem) {
                    uiNavMenuItem = (UINavigationMenuItem) tempObj;
                }
            }

            if (! item.isRendered()) {
                continue;
            }

            if (i > 0) {
                writer.append(",\n");
            }

            if (item.isSplit()) {
                writer.append("_cmSplit,");

                if (item.getLabel().equals("0")) {
                    continue;
                }
            }

            writer.append("[");
            if (item.getIcon() != null) {
                String iconSrc = context.getApplication().getViewHandler().getResourceURL(context, item.getIcon());
                writer.append("''");
            }
            else {
                writer.append("null");
            }
            writer.append(", '");
            if (item.getLabel() != null) {
                writer.append(getString(context, item.getLabel()));
            }
            writer.append("', ");
            StringBuffer actionStr = new StringBuffer();
            if ((item.getAction() != null || item.getActionListener() != null) && ! item.isDisabled()) {
                actionStr.append("'");
                actionStr.append(menuId);
                if (item.getActionListener() != null) {
                    actionStr.append(":L]");
                    actionStr.append(item.getActionListener());
                    if (uiNavMenuItem != null && uiNavMenuItem.getItemValue() != null) {
                        actionStr.append(';');
                        actionStr.append(getString(context, uiNavMenuItem.getItemValue()));
                    }
                    else if (item.getValue() != null) {
                        actionStr.append(';');
                        actionStr.append(getString(context, item.getValue()));
                    }
                }
                if (item.getAction() != null) {
                    actionStr.append(":A]");
                    actionStr.append(item.getAction());
                    if (uiNavMenuItem != null) {
                        encodeValueBinding(actionStr, uiNavMenuItem, item);
                    }
                }
                actionStr.append("'");
                writer.append(actionStr.toString());
            }
            else {
                writer.append("null");
            }
            writer.append(", '");
            // Change here to allow the use of non dummy form.
            writer.append(formName);
            writer.append("', null");

            if (item.isRendered() && ! item.isDisabled()) {
                // render children only if parent is visible/enabled
                NavigationMenuItem[] menuItems = item.getNavigationMenuItems();
                if (menuItems != null && menuItems.length > 0) {
                    writer.append(",");
                    if (uiNavMenuItem != null) {
                        encodeNavigationMenuItems(context, writer, menuItems,
                                                  uiNavMenuItem.getChildren(), menuId, formName);
                    }
                    else {
                        encodeNavigationMenuItems(context, writer, menuItems,
                                                  new ArrayList(1), menuId, formName);
                    }
                }
            }
            writer.append("]");
        }
    }

    private String getString(FacesContext facesContext, Object value) {
        String str = "";

        if (value != null) {
            str = value.toString();
        }

        if (NavigationMenuUtils.isValueReference(str)) {
            value = facesContext.getApplication().createValueBinding(str).getValue(facesContext);

            if (value != null) {
                str = value.toString();
            }
            else {
                str = "";
            }
        }

        return JavascriptUtils.encodeString(str);
    }

    private void encodeValueBinding(StringBuffer writer, UINavigationMenuItem uiNavMenuItem,
                                    NavigationMenuItem item) {
        ValueBinding vb = uiNavMenuItem.getValueBinding("NavMenuItemValue");
        if (vb == null) {
            return;
        }
        String vbExpression = vb.getExpressionString();
        if (vbExpression == null) {
            return;
        }
        Object tempObj = item.getValue();
        if (tempObj == null) {
            return;
        }

        writer.append(";");
        writer.append(vbExpression);
        writer.append("=");
        writer.append(tempObj.toString());
    }

    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
        HtmlCommandJSCookMenu menu = (HtmlCommandJSCookMenu) component;
        String theme = menu.getTheme();
        if (theme == null) {
            // should never happen; theme is a required attribute in the jsp tag definition
            throw new IllegalArgumentException("theme name is mandatory for a jscookmenu.");
        }

        addResourcesToHeader(theme, menu, context);
    }

    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
        RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);
        HtmlCommandJSCookMenu menu = (HtmlCommandJSCookMenu) component;
        String theme = menu.getTheme();


        ResponseWriter writer = context.getResponseWriter();

        String menuId = getMenuId(context, component);

        writer.write("
\n"); writer.startElement(HTML.SCRIPT_ELEM, menu); writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null); StringBuffer buf = new StringBuffer(); buf.append("\tif(window.cmDraw!=undefined) { cmDraw ('"). append(menuId). append("', "). append(menuId). append(", '"). append(menu.getLayout()). append("', cm"). append(theme). append(", '"). append(theme). append("');}"); writer.writeText(buf.toString(), null); writer.endElement(HTML.SCRIPT_ELEM); } private void addResourcesToHeader(String themeName, HtmlCommandJSCookMenu menu, FacesContext context) { String javascriptLocation = (String) menu.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION); String imageLocation = (String) menu.getAttributes().get(JSFAttr.IMAGE_LOCATION); String styleLocation = (String) menu.getAttributes().get(JSFAttr.STYLE_LOCATION); AddResource addResource = AddResourceFactory.getInstance(context); if (javascriptLocation != null) { addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + JSCOOK_MENU_SCRIPT); addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + JSCOOK_EFFECT_SCRIPT); addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + MYFACES_HACK_SCRIPT); } else { addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, HtmlJSCookMenuRenderer.class, JSCOOK_MENU_SCRIPT); addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, HtmlJSCookMenuRenderer.class, JSCOOK_EFFECT_SCRIPT); addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, HtmlJSCookMenuRenderer.class, MYFACES_HACK_SCRIPT); } addThemeSpecificResources(themeName, styleLocation, javascriptLocation, imageLocation, context); } /** * A theme for a menu requires a number of external files; this method * outputs those into the page head section. * * @param themeName is the name of the theme for this menu. It is never * null. It may match one of the built-in theme names or may be a custom * theme defined by the application. * @param styleLocation is the URL of a directory containing a * "theme.css" file. A stylesheet link tag will be inserted into * the page header referencing that file. If null then if the * themeName is a built-in one then a reference to the appropriate * built-in stylesheet is generated (requires the ExtensionsFilter). * If null and a custom theme is used then no stylesheet link will be * generated here. * @param javascriptLocation is the URL of a directory containing a * "theme.js" file. A script tag will be inserted into the page header * referencing that file. If null then if the themeName is a built-in * one then a reference to the built-in stylesheet is generated (requires * the ExtensionsFilter). If null and a custom theme is used then no * stylesheet link will be generated here. * @param imageLocation is the URL of a directory containing files * (esp. image files) used by the theme.js file to define the menu * theme. A javascript variable of name "my{themeName}Base" is * generated in the page header containing this URL, so that the * theme.js script can locate the files. If null then if the themeName * is a built-in one then the URL to the appropriate resource directory * is generated (requires the ExtensionsFilter). If null and a custom * theme is used then no javascript variable will be generated here. * @param context is the current faces context. */ private void addThemeSpecificResources(String themeName, String styleLocation, String javascriptLocation, String imageLocation, FacesContext context) { String themeLocation = (String) builtInThemes.get(themeName); if (themeLocation == null) { log.debug("Unknown theme name '" + themeName + "' specified."); } AddResource addResource = AddResourceFactory.getInstance(context); if ((imageLocation != null) || (themeLocation != null)) { // Generate a javascript variable containing a reference to the // directory containing theme image files, for use by the theme // javascript file. If neither of these is defined (ie a custom // theme was specified but no imageLocation) then presumably the // theme.js file uses some other mechanism to determine where // its image files are. StringBuffer buf = new StringBuffer(); buf.append("var my"); buf.append(themeName); buf.append("Base='"); ExternalContext externalContext = context.getExternalContext(); if (imageLocation != null) { buf.append(externalContext.encodeResourceURL(addResource.getResourceUri(context, imageLocation + "/" + themeName))); } else { buf.append(externalContext.encodeResourceURL(addResource.getResourceUri(context, HtmlJSCookMenuRenderer.class, themeLocation))); } buf.append("';"); addResource.addInlineScriptAtPosition(context, AddResource.HEADER_BEGIN, buf.toString()); } if ((javascriptLocation != null) || (themeLocation != null)) { // Generate a