0001: /*
0002: * Copyright 2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.apache.myfaces.shared_impl.renderkit.html;
0017:
0018: import org.apache.commons.logging.Log;
0019: import org.apache.commons.logging.LogFactory;
0020: import org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable;
0021: import org.apache.myfaces.shared_impl.component.EscapeCapable;
0022: import org.apache.myfaces.shared_impl.config.MyfacesConfig;
0023: import org.apache.myfaces.shared_impl.renderkit.JSFAttr;
0024: import org.apache.myfaces.shared_impl.renderkit.RendererUtils;
0025: import org.apache.myfaces.shared_impl.renderkit.html.util.FormInfo;
0026: import org.apache.myfaces.shared_impl.renderkit.html.util.HTMLEncoder;
0027: import org.apache.myfaces.shared_impl.renderkit.html.util.JavascriptUtils;
0028:
0029: import javax.faces.FacesException;
0030: import javax.faces.component.*;
0031: import javax.faces.component.html.HtmlDataTable;
0032: import javax.faces.component.html.HtmlPanelGrid;
0033: import javax.faces.context.ExternalContext;
0034: import javax.faces.context.FacesContext;
0035: import javax.faces.context.ResponseWriter;
0036: import javax.faces.convert.Converter;
0037: import javax.faces.model.SelectItem;
0038: import javax.faces.model.SelectItemGroup;
0039: import java.io.IOException;
0040: import java.util.*;
0041:
0042: /**
0043: * @author Manfred Geiler (latest modification by $Author: matzew $)
0044: * @version $Revision: 555720 $ $Date: 2007-07-12 21:03:38 +0200 (Do, 12 Jul 2007) $
0045: */
0046: public final class HtmlRendererUtils {
0047: private static final Log log = LogFactory
0048: .getLog(HtmlRendererUtils.class);
0049:
0050: //private static final String[] EMPTY_STRING_ARRAY = new String[0];
0051: private static final String LINE_SEPARATOR = System.getProperty(
0052: "line.separator", "\r\n");
0053: private static final char TABULATOR = '\t';
0054:
0055: public static final String HIDDEN_COMMANDLINK_FIELD_NAME = "_idcl";
0056: public static final String HIDDEN_COMMANDLINK_FIELD_NAME_MYFACES_OLD = "_link_hidden_";
0057: public static final String HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD = "source";
0058:
0059: public static final String CLEAR_HIDDEN_FIELD_FN_NAME = "clearFormHiddenParams";
0060: public static final String SUBMIT_FORM_FN_NAME = "oamSubmitForm";
0061: public static final String ALLOW_CDATA_SECTION_ON = "org.apache.myfaces.ResponseWriter.CdataSectionOn";
0062:
0063: private static final String SET_HIDDEN_INPUT_FN_NAME = "oamSetHiddenInput";
0064: private static final String CLEAR_HIDDEN_INPUT_FN_NAME = "oamClearHiddenInput";
0065:
0066: private static final String AUTO_SCROLL_PARAM = "autoScroll";
0067: private static final String AUTO_SCROLL_FUNCTION = "getScrolling";
0068:
0069: private static final String FIRST_SUBMIT_SCRIPT_ON_PAGE = "org.apache.MyFaces.FIRST_SUBMIT_SCRIPT_ON_PAGE";
0070:
0071: public static final String NON_SUBMITTED_VALUE_WARNING = "There should always be a submitted value for an input if it is rendered,"
0072: + " its form is submitted, and it was not originally rendered disabled or read-only."
0073: + " You cannot submit a form after disabling an input element via javascript."
0074: + " Consider setting read-only to true instead"
0075: + " or resetting the disabled value back to false prior to form submission.";
0076:
0077: private HtmlRendererUtils() {
0078: // utility class, do not instantiate
0079: }
0080:
0081: /**
0082: * Utility to set the submitted value of the provided component from the
0083: * data in the current request object.
0084: * <p>
0085: * Param component is required to be an EditableValueHolder. On return
0086: * from this method, the component's submittedValue property will be
0087: * set if the submitted form contained that component.
0088: */
0089: public static void decodeUIInput(FacesContext facesContext,
0090: UIComponent component) {
0091: if (!(component instanceof EditableValueHolder)) {
0092: throw new IllegalArgumentException("Component "
0093: + component.getClientId(facesContext)
0094: + " is not an EditableValueHolder");
0095: }
0096: Map paramMap = facesContext.getExternalContext()
0097: .getRequestParameterMap();
0098: String clientId = component.getClientId(facesContext);
0099:
0100: if (isDisabledOrReadOnly(component))
0101: return;
0102:
0103: if (paramMap.containsKey(clientId)) {
0104: ((EditableValueHolder) component)
0105: .setSubmittedValue(paramMap.get(clientId));
0106: } else {
0107: log.warn(NON_SUBMITTED_VALUE_WARNING + " Component : "
0108: + RendererUtils.getPathToComponent(component));
0109: }
0110: }
0111:
0112: /**
0113: * X-CHECKED: tlddoc h:selectBooleanCheckbox
0114: *
0115: * @param facesContext
0116: * @param component
0117: */
0118: public static void decodeUISelectBoolean(FacesContext facesContext,
0119: UIComponent component) {
0120: if (!(component instanceof EditableValueHolder)) {
0121: throw new IllegalArgumentException("Component "
0122: + component.getClientId(facesContext)
0123: + " is not an EditableValueHolder");
0124: }
0125:
0126: if (isDisabledOrReadOnly(component))
0127: return;
0128:
0129: Map paramMap = facesContext.getExternalContext()
0130: .getRequestParameterMap();
0131: String clientId = component.getClientId(facesContext);
0132: if (paramMap.containsKey(clientId)) {
0133: String reqValue = (String) paramMap.get(clientId);
0134: if ((reqValue.equalsIgnoreCase("on")
0135: || reqValue.equalsIgnoreCase("yes") || reqValue
0136: .equalsIgnoreCase("true"))) {
0137: ((EditableValueHolder) component)
0138: .setSubmittedValue(Boolean.TRUE);
0139: } else {
0140: ((EditableValueHolder) component)
0141: .setSubmittedValue(Boolean.FALSE);
0142: }
0143: } else {
0144: ((EditableValueHolder) component)
0145: .setSubmittedValue(Boolean.FALSE);
0146: }
0147: }
0148:
0149: public static boolean isDisabledOrReadOnly(UIComponent component) {
0150: return isDisplayValueOnly(component) || isDisabled(component)
0151: || isReadOnly(component);
0152: }
0153:
0154: public static boolean isDisabled(UIComponent component) {
0155: return isTrue(component.getAttributes().get("disabled"));
0156: }
0157:
0158: public static boolean isReadOnly(UIComponent component) {
0159: return isTrue(component.getAttributes().get("readonly"));
0160: }
0161:
0162: private static boolean isTrue(Object obj) {
0163: if (!(obj instanceof Boolean))
0164: return false;
0165:
0166: return ((Boolean) obj).booleanValue();
0167: }
0168:
0169: /**
0170: * X-CHECKED: tlddoc h:selectManyListbox
0171: *
0172: * @param facesContext
0173: * @param component
0174: */
0175: public static void decodeUISelectMany(FacesContext facesContext,
0176: UIComponent component) {
0177: if (!(component instanceof EditableValueHolder)) {
0178: throw new IllegalArgumentException("Component "
0179: + component.getClientId(facesContext)
0180: + " is not an EditableValueHolder");
0181: }
0182: Map paramValuesMap = facesContext.getExternalContext()
0183: .getRequestParameterValuesMap();
0184: String clientId = component.getClientId(facesContext);
0185:
0186: if (isDisabledOrReadOnly(component))
0187: return;
0188:
0189: if (paramValuesMap.containsKey(clientId)) {
0190: String[] reqValues = (String[]) paramValuesMap
0191: .get(clientId);
0192: ((EditableValueHolder) component)
0193: .setSubmittedValue(reqValues);
0194: } else {
0195: /* request parameter not found, nothing to decode - set submitted value to an empty array
0196: as we should get here only if the component is on a submitted form, is rendered
0197: and if the component is not readonly or has not been disabled.
0198:
0199: So in fact, there must be component value at this location, but for listboxes, comboboxes etc.
0200: the submitted value is not posted if no item is selected. */
0201: ((EditableValueHolder) component)
0202: .setSubmittedValue(new String[] {});
0203: }
0204: }
0205:
0206: /**
0207: * X-CHECKED: tlddoc h:selectManyListbox
0208: *
0209: * @param facesContext
0210: * @param component
0211: */
0212: public static void decodeUISelectOne(FacesContext facesContext,
0213: UIComponent component) {
0214: if (!(component instanceof EditableValueHolder)) {
0215: throw new IllegalArgumentException("Component "
0216: + component.getClientId(facesContext)
0217: + " is not an EditableValueHolder");
0218: }
0219:
0220: if (isDisabledOrReadOnly(component))
0221: return;
0222:
0223: Map paramMap = facesContext.getExternalContext()
0224: .getRequestParameterMap();
0225: String clientId = component.getClientId(facesContext);
0226: if (paramMap.containsKey(clientId)) {
0227: //request parameter found, set submitted value
0228: ((EditableValueHolder) component)
0229: .setSubmittedValue(paramMap.get(clientId));
0230: } else {
0231: //see reason for this action at decodeUISelectMany
0232: ((EditableValueHolder) component)
0233: .setSubmittedValue(RendererUtils.NOTHING);
0234: }
0235: }
0236:
0237: /*
0238: * public static void renderCheckbox(FacesContext facesContext, UIComponent
0239: * uiComponent, String value, String label, boolean checked) throws
0240: * IOException { String clientId = uiComponent.getClientId(facesContext);
0241: *
0242: * ResponseWriter writer = facesContext.getResponseWriter();
0243: *
0244: * writer.startElement(HTML.INPUT_ELEM, uiComponent);
0245: * writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_CHECKBOX, null);
0246: * writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
0247: * writer.writeAttribute(HTML.ID_ATTR, clientId, null);
0248: *
0249: * if (checked) { writer.writeAttribute(HTML.CHECKED_ATTR,
0250: * HTML.CHECKED_ATTR, null); }
0251: *
0252: * if ((value != null) && (value.length() > 0)) {
0253: * writer.writeAttribute(HTML.VALUE_ATTR, value, null); }
0254: *
0255: * renderHTMLAttributes(writer, uiComponent,
0256: * HTML.INPUT_PASSTHROUGH_ATTRIBUTES); renderDisabledOnUserRole(writer,
0257: * uiComponent, facesContext);
0258: *
0259: * if ((label != null) && (label.length() > 0)) {
0260: * writer.write(HTML.NBSP_ENTITY); writer.writeText(label, null); }
0261: *
0262: * writer.endElement(HTML.INPUT_ELEM); }
0263: */
0264:
0265: public static void renderListbox(FacesContext facesContext,
0266: UISelectOne selectOne, boolean disabled, int size)
0267: throws IOException {
0268: internalRenderSelect(facesContext, selectOne, disabled, size,
0269: false);
0270: }
0271:
0272: public static void renderListbox(FacesContext facesContext,
0273: UISelectMany selectMany, boolean disabled, int size)
0274: throws IOException {
0275: internalRenderSelect(facesContext, selectMany, disabled, size,
0276: true);
0277: }
0278:
0279: public static void renderMenu(FacesContext facesContext,
0280: UISelectOne selectOne, boolean disabled) throws IOException {
0281: internalRenderSelect(facesContext, selectOne, disabled, 1,
0282: false);
0283: }
0284:
0285: public static void renderMenu(FacesContext facesContext,
0286: UISelectMany selectMany, boolean disabled)
0287: throws IOException {
0288: internalRenderSelect(facesContext, selectMany, disabled, 1,
0289: true);
0290: }
0291:
0292: private static void internalRenderSelect(FacesContext facesContext,
0293: UIComponent uiComponent, boolean disabled, int size,
0294: boolean selectMany) throws IOException {
0295: ResponseWriter writer = facesContext.getResponseWriter();
0296:
0297: writer.startElement(HTML.SELECT_ELEM, uiComponent);
0298: HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent,
0299: facesContext);
0300: writer.writeAttribute(HTML.NAME_ATTR, uiComponent
0301: .getClientId(facesContext), null);
0302:
0303: List selectItemList;
0304: Converter converter;
0305: if (selectMany) {
0306: writer.writeAttribute(HTML.MULTIPLE_ATTR,
0307: HTML.MULTIPLE_ATTR, null);
0308: selectItemList = org.apache.myfaces.shared_impl.renderkit.RendererUtils
0309: .getSelectItemList((UISelectMany) uiComponent);
0310: converter = findUISelectManyConverterFailsafe(facesContext,
0311: uiComponent);
0312: } else {
0313: selectItemList = RendererUtils
0314: .getSelectItemList((UISelectOne) uiComponent);
0315: converter = findUIOutputConverterFailSafe(facesContext,
0316: uiComponent);
0317: }
0318:
0319: if (size == Integer.MIN_VALUE) {
0320: //No size given (Listbox) --> size is number of select items
0321: writer.writeAttribute(HTML.SIZE_ATTR, Integer
0322: .toString(selectItemList.size()), null);
0323: } else {
0324: writer.writeAttribute(HTML.SIZE_ATTR, Integer
0325: .toString(size), null);
0326: }
0327: renderHTMLAttributes(writer, uiComponent,
0328: HTML.SELECT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
0329: if (disabled) {
0330: writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE,
0331: null);
0332: }
0333:
0334: if (isReadOnly(uiComponent)) {
0335: writer.writeAttribute(HTML.READONLY_ATTR,
0336: HTML.READONLY_ATTR, null);
0337: }
0338:
0339: Set lookupSet = getSubmittedOrSelectedValuesAsSet(selectMany,
0340: uiComponent, facesContext, converter);
0341:
0342: renderSelectOptions(facesContext, uiComponent, converter,
0343: lookupSet, selectItemList);
0344: // bug #970747: force separate end tag
0345: writer.writeText("", null);
0346: writer.endElement(HTML.SELECT_ELEM);
0347: }
0348:
0349: public static Set getSubmittedOrSelectedValuesAsSet(
0350: boolean selectMany, UIComponent uiComponent,
0351: FacesContext facesContext, Converter converter) {
0352: Set lookupSet;
0353:
0354: if (selectMany) {
0355: UISelectMany uiSelectMany = (UISelectMany) uiComponent;
0356: lookupSet = RendererUtils.getSubmittedValuesAsSet(
0357: facesContext, uiComponent, converter, uiSelectMany);
0358: if (lookupSet == null) {
0359: lookupSet = RendererUtils.getSelectedValuesAsSet(
0360: facesContext, uiComponent, converter,
0361: uiSelectMany);
0362: }
0363: } else {
0364: UISelectOne uiSelectOne = (UISelectOne) uiComponent;
0365: Object lookup = uiSelectOne.getSubmittedValue();
0366: if (lookup == null) {
0367: lookup = uiSelectOne.getValue();
0368: String lookupString = RendererUtils
0369: .getConvertedStringValue(facesContext,
0370: uiComponent, converter, lookup);
0371: lookupSet = Collections.singleton(lookupString);
0372: } else if (org.apache.myfaces.shared_impl.renderkit.RendererUtils.NOTHING
0373: .equals(lookup)) {
0374: lookupSet = Collections.EMPTY_SET;
0375: } else {
0376: lookupSet = Collections.singleton(lookup);
0377: }
0378: }
0379: return lookupSet;
0380: }
0381:
0382: public static Converter findUISelectManyConverterFailsafe(
0383: FacesContext facesContext, UIComponent uiComponent) {
0384: Converter converter;
0385: try {
0386: converter = RendererUtils.findUISelectManyConverter(
0387: facesContext, (UISelectMany) uiComponent);
0388: } catch (FacesException e) {
0389: log.error("Error finding Converter for component with id "
0390: + uiComponent.getClientId(facesContext), e);
0391: converter = null;
0392: }
0393: return converter;
0394: }
0395:
0396: public static Converter findUIOutputConverterFailSafe(
0397: FacesContext facesContext, UIComponent uiComponent) {
0398: Converter converter;
0399: try {
0400: converter = RendererUtils.findUIOutputConverter(
0401: facesContext, (UIOutput) uiComponent);
0402: } catch (FacesException e) {
0403: log.error("Error finding Converter for component with id "
0404: + uiComponent.getClientId(facesContext), e);
0405: converter = null;
0406: }
0407: return converter;
0408: }
0409:
0410: /**
0411: * Renders the select options for a <code>UIComponent</code> that is
0412: * rendered as an HTML select element.
0413: *
0414: * @param context
0415: * the current <code>FacesContext</code>.
0416: * @param component
0417: * the <code>UIComponent</code> whose options need to be
0418: * rendered.
0419: * @param converter
0420: * <code>component</code>'s converter
0421: * @param lookupSet
0422: * the <code>Set</code> to use to look up selected options
0423: * @param selectItemList
0424: * the <code>List</code> of <code>SelectItem</code> s to be
0425: * rendered as HTML option elements.
0426: * @throws IOException
0427: */
0428: public static void renderSelectOptions(FacesContext context,
0429: UIComponent component, Converter converter, Set lookupSet,
0430: List selectItemList) throws IOException {
0431: ResponseWriter writer = context.getResponseWriter();
0432:
0433: for (Iterator it = selectItemList.iterator(); it.hasNext();) {
0434: SelectItem selectItem = (SelectItem) it.next();
0435:
0436: if (selectItem instanceof SelectItemGroup) {
0437: writer.startElement(HTML.OPTGROUP_ELEM, component);
0438: writer.writeAttribute(HTML.LABEL_ATTR, selectItem
0439: .getLabel(), null);
0440: SelectItem[] selectItems = ((SelectItemGroup) selectItem)
0441: .getSelectItems();
0442: renderSelectOptions(context, component, converter,
0443: lookupSet, Arrays.asList(selectItems));
0444: writer.endElement(HTML.OPTGROUP_ELEM);
0445: } else {
0446: String itemStrValue = org.apache.myfaces.shared_impl.renderkit.RendererUtils
0447: .getConvertedStringValue(context, component,
0448: converter, selectItem);
0449:
0450: writer.write(TABULATOR);
0451: writer.startElement(HTML.OPTION_ELEM, component);
0452: if (itemStrValue != null) {
0453: writer.writeAttribute(HTML.VALUE_ATTR,
0454: itemStrValue, null);
0455: }
0456:
0457: if (lookupSet.contains(itemStrValue)) { //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings only when useSubmittedValue==true, else use the real item value Objects
0458: writer.writeAttribute(HTML.SELECTED_ATTR,
0459: HTML.SELECTED_ATTR, null);
0460: }
0461:
0462: boolean disabled = selectItem.isDisabled();
0463: if (disabled) {
0464: writer.writeAttribute(HTML.DISABLED_ATTR,
0465: HTML.DISABLED_ATTR, null);
0466: }
0467:
0468: String labelClass = null;
0469: boolean componentDisabled = isTrue(component
0470: .getAttributes().get("disabled"));
0471:
0472: if (componentDisabled || disabled) {
0473: labelClass = (String) component.getAttributes()
0474: .get(JSFAttr.DISABLED_CLASS_ATTR);
0475: } else {
0476: labelClass = (String) component.getAttributes()
0477: .get(JSFAttr.ENABLED_CLASS_ATTR);
0478: }
0479: if (labelClass != null) {
0480: writer.writeAttribute("class", labelClass,
0481: "labelClass");
0482: }
0483:
0484: boolean escape;
0485: if (component instanceof EscapeCapable) {
0486: escape = ((EscapeCapable) component).isEscape();
0487: } else {
0488: escape = RendererUtils.getBooleanAttribute(
0489: component, JSFAttr.ESCAPE_ATTR, true); //default is to escape
0490: }
0491:
0492: if (escape || selectItem.isEscape()) {
0493: writer.writeText(selectItem.getLabel(), null);
0494: } else {
0495: writer.write(selectItem.getLabel());
0496: }
0497:
0498: writer.endElement(HTML.OPTION_ELEM);
0499: }
0500: }
0501: }
0502:
0503: /*
0504: * public static void renderRadio(FacesContext facesContext, UIInput
0505: * uiComponent, String value, String label, boolean checked) throws
0506: * IOException { String clientId = uiComponent.getClientId(facesContext);
0507: *
0508: * ResponseWriter writer = facesContext.getResponseWriter();
0509: *
0510: * writer.startElement(HTML.INPUT_ELEM, uiComponent);
0511: * writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_RADIO, null);
0512: * writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
0513: * writer.writeAttribute(HTML.ID_ATTR, clientId, null);
0514: *
0515: * if (checked) { writer.writeAttribute(HTML.CHECKED_ATTR,
0516: * HTML.CHECKED_ATTR, null); }
0517: *
0518: * if ((value != null) && (value.length() > 0)) {
0519: * writer.writeAttribute(HTML.VALUE_ATTR, value, null); }
0520: *
0521: * renderHTMLAttributes(writer, uiComponent,
0522: * HTML.INPUT_PASSTHROUGH_ATTRIBUTES); renderDisabledOnUserRole(writer,
0523: * uiComponent, facesContext);
0524: *
0525: * if ((label != null) && (label.length() > 0)) {
0526: * writer.write(HTML.NBSP_ENTITY); writer.writeText(label, null); }
0527: *
0528: * writer.endElement(HTML.INPUT_ELEM); }
0529: */
0530:
0531: public static void writePrettyLineSeparator(
0532: FacesContext facesContext) throws IOException {
0533: if (org.apache.myfaces.shared_impl.config.MyfacesConfig
0534: .getCurrentInstance(facesContext.getExternalContext())
0535: .isPrettyHtml()) {
0536: facesContext.getResponseWriter().write(LINE_SEPARATOR);
0537: }
0538: }
0539:
0540: public static void writePrettyIndent(FacesContext facesContext)
0541: throws IOException {
0542: if (org.apache.myfaces.shared_impl.config.MyfacesConfig
0543: .getCurrentInstance(facesContext.getExternalContext())
0544: .isPrettyHtml()) {
0545: facesContext.getResponseWriter().write('\t');
0546: }
0547: }
0548:
0549: /**
0550: * @return true, if the attribute was written
0551: * @throws java.io.IOException
0552: */
0553: public static boolean renderHTMLAttribute(ResponseWriter writer,
0554: String componentProperty, String attrName, Object value)
0555: throws IOException {
0556: if (!RendererUtils.isDefaultAttributeValue(value)) {
0557: // render JSF "styleClass" and "itemStyleClass" attributes as "class"
0558: String htmlAttrName = attrName
0559: .equals(HTML.STYLE_CLASS_ATTR) ? HTML.CLASS_ATTR
0560: : attrName;
0561: writer.writeAttribute(htmlAttrName, value,
0562: componentProperty);
0563: return true;
0564: }
0565:
0566: return false;
0567: }
0568:
0569: /**
0570: * @return true, if the attribute was written
0571: * @throws java.io.IOException
0572: */
0573: public static boolean renderHTMLAttribute(ResponseWriter writer,
0574: UIComponent component, String componentProperty,
0575: String htmlAttrName) throws IOException {
0576: Object value = component.getAttributes().get(componentProperty);
0577: return renderHTMLAttribute(writer, componentProperty,
0578: htmlAttrName, value);
0579: }
0580:
0581: /**
0582: * @return true, if an attribute was written
0583: * @throws java.io.IOException
0584: */
0585: public static boolean renderHTMLAttributes(ResponseWriter writer,
0586: UIComponent component, String[] attributes)
0587: throws IOException {
0588: boolean somethingDone = false;
0589: for (int i = 0, len = attributes.length; i < len; i++) {
0590: String attrName = attributes[i];
0591: if (renderHTMLAttribute(writer, component, attrName,
0592: attrName)) {
0593: somethingDone = true;
0594: }
0595: }
0596: return somethingDone;
0597: }
0598:
0599: public static boolean renderHTMLAttributeWithOptionalStartElement(
0600: ResponseWriter writer, UIComponent component,
0601: String elementName, String attrName, Object value,
0602: boolean startElementWritten) throws IOException {
0603: if (!org.apache.myfaces.shared_impl.renderkit.RendererUtils
0604: .isDefaultAttributeValue(value)) {
0605: if (!startElementWritten) {
0606: writer.startElement(elementName, component);
0607: startElementWritten = true;
0608: }
0609: renderHTMLAttribute(writer, attrName, attrName, value);
0610: }
0611: return startElementWritten;
0612: }
0613:
0614: public static boolean renderHTMLAttributesWithOptionalStartElement(
0615: ResponseWriter writer, UIComponent component,
0616: String elementName, String[] attributes) throws IOException {
0617: boolean startElementWritten = false;
0618: for (int i = 0, len = attributes.length; i < len; i++) {
0619: String attrName = attributes[i];
0620: Object value = component.getAttributes().get(attrName);
0621: if (!RendererUtils.isDefaultAttributeValue(value)) {
0622: if (!startElementWritten) {
0623: writer.startElement(elementName, component);
0624: startElementWritten = true;
0625: }
0626: renderHTMLAttribute(writer, attrName, attrName, value);
0627: }
0628: }
0629: return startElementWritten;
0630: }
0631:
0632: public static boolean renderOptionalEndElement(
0633: ResponseWriter writer, UIComponent component,
0634: String elementName, String[] attributes) throws IOException {
0635: boolean endElementNeeded = false;
0636: for (int i = 0, len = attributes.length; i < len; i++) {
0637: String attrName = attributes[i];
0638: Object value = component.getAttributes().get(attrName);
0639: if (!RendererUtils.isDefaultAttributeValue(value)) {
0640: endElementNeeded = true;
0641: break;
0642: }
0643: }
0644: if (endElementNeeded) {
0645: writer.endElement(elementName);
0646: return true;
0647: }
0648:
0649: return false;
0650: }
0651:
0652: public static void writeIdIfNecessary(ResponseWriter writer,
0653: UIComponent component, FacesContext facesContext)
0654: throws IOException {
0655: if (component.getId() != null
0656: && !component.getId().startsWith(
0657: UIViewRoot.UNIQUE_ID_PREFIX)) {
0658: writer.writeAttribute(HTML.ID_ATTR, component
0659: .getClientId(facesContext), null);
0660: }
0661: }
0662:
0663: public static void writeIdAndNameIfNecessary(ResponseWriter writer,
0664: UIComponent component, FacesContext facesContext)
0665: throws IOException {
0666: if (component.getId() != null
0667: && !component.getId().startsWith(
0668: UIViewRoot.UNIQUE_ID_PREFIX)) {
0669: String clientId = component.getClientId(facesContext);
0670: writer.writeAttribute(HTML.ID_ATTR, clientId, null);
0671: writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
0672: }
0673: }
0674:
0675: public static void renderDisplayValueOnlyForSelects(
0676: FacesContext facesContext, UIComponent uiComponent)
0677: throws IOException {
0678: ResponseWriter writer = facesContext.getResponseWriter();
0679:
0680: List selectItemList = null;
0681: Converter converter = null;
0682: boolean isSelectOne = false;
0683:
0684: if (uiComponent instanceof UISelectBoolean) {
0685: converter = findUIOutputConverterFailSafe(facesContext,
0686: uiComponent);
0687:
0688: writer.startElement(HTML.SPAN_ELEM, uiComponent);
0689: writeIdIfNecessary(writer, uiComponent, facesContext);
0690: renderDisplayValueOnlyAttributes(uiComponent, writer);
0691: writer.writeText(RendererUtils.getConvertedStringValue(
0692: facesContext, uiComponent, converter,
0693: ((UISelectBoolean) uiComponent).getValue()),
0694: JSFAttr.VALUE_ATTR);
0695: writer.endElement(HTML.SPAN_ELEM);
0696:
0697: } else {
0698: if (uiComponent instanceof UISelectMany) {
0699: isSelectOne = false;
0700: selectItemList = RendererUtils
0701: .getSelectItemList((UISelectMany) uiComponent);
0702: converter = findUISelectManyConverterFailsafe(
0703: facesContext, uiComponent);
0704: } else if (uiComponent instanceof UISelectOne) {
0705: isSelectOne = true;
0706: selectItemList = RendererUtils
0707: .getSelectItemList((UISelectOne) uiComponent);
0708: converter = findUIOutputConverterFailSafe(facesContext,
0709: uiComponent);
0710: }
0711:
0712: writer.startElement(isSelectOne ? HTML.SPAN_ELEM
0713: : HTML.UL_ELEM, uiComponent);
0714: writeIdIfNecessary(writer, uiComponent, facesContext);
0715:
0716: renderDisplayValueOnlyAttributes(uiComponent, writer);
0717:
0718: Set lookupSet = getSubmittedOrSelectedValuesAsSet(
0719: uiComponent instanceof UISelectMany, uiComponent,
0720: facesContext, converter);
0721:
0722: renderSelectOptionsAsText(facesContext, uiComponent,
0723: converter, lookupSet, selectItemList, isSelectOne);
0724:
0725: // bug #970747: force separate end tag
0726: writer.writeText("", null);
0727: writer.endElement(isSelectOne ? HTML.SPAN_ELEM
0728: : HTML.UL_ELEM);
0729: }
0730:
0731: }
0732:
0733: public static void renderDisplayValueOnlyAttributes(
0734: UIComponent uiComponent, ResponseWriter writer)
0735: throws IOException {
0736: if (!(uiComponent instanceof org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable)) {
0737: log
0738: .error("Wrong type of uiComponent. needs DisplayValueOnlyCapable.");
0739: renderHTMLAttributes(writer, uiComponent,
0740: HTML.COMMON_PASSTROUGH_ATTRIBUTES);
0741:
0742: return;
0743: }
0744:
0745: if (getDisplayValueOnlyStyle(uiComponent) != null
0746: || getDisplayValueOnlyStyleClass(uiComponent) != null) {
0747: if (getDisplayValueOnlyStyle(uiComponent) != null) {
0748: writer.writeAttribute(HTML.STYLE_ATTR,
0749: getDisplayValueOnlyStyle(uiComponent), null);
0750: } else if (uiComponent.getAttributes().get("style") != null) {
0751: writer.writeAttribute(HTML.STYLE_ATTR, uiComponent
0752: .getAttributes().get("style"), null);
0753: }
0754:
0755: if (getDisplayValueOnlyStyleClass(uiComponent) != null) {
0756: writer.writeAttribute(HTML.CLASS_ATTR,
0757: getDisplayValueOnlyStyleClass(uiComponent),
0758: null);
0759: } else if (uiComponent.getAttributes().get("styleClass") != null) {
0760: writer.writeAttribute(HTML.CLASS_ATTR, uiComponent
0761: .getAttributes().get("styleClass"), null);
0762: }
0763:
0764: renderHTMLAttributes(writer, uiComponent,
0765: HTML.COMMON_PASSTROUGH_ATTRIBUTES_WITHOUT_STYLE);
0766: } else {
0767: renderHTMLAttributes(writer, uiComponent,
0768: HTML.COMMON_PASSTROUGH_ATTRIBUTES);
0769: }
0770: }
0771:
0772: private static void renderSelectOptionsAsText(FacesContext context,
0773: UIComponent component, Converter converter, Set lookupSet,
0774: List selectItemList, boolean isSelectOne)
0775: throws IOException {
0776: ResponseWriter writer = context.getResponseWriter();
0777:
0778: for (Iterator it = selectItemList.iterator(); it.hasNext();) {
0779: SelectItem selectItem = (SelectItem) it.next();
0780:
0781: if (selectItem instanceof SelectItemGroup) {
0782: SelectItem[] selectItems = ((SelectItemGroup) selectItem)
0783: .getSelectItems();
0784: renderSelectOptionsAsText(context, component,
0785: converter, lookupSet, Arrays
0786: .asList(selectItems), isSelectOne);
0787: } else {
0788: String itemStrValue = RendererUtils
0789: .getConvertedStringValue(context, component,
0790: converter, selectItem);
0791:
0792: if (lookupSet.contains(itemStrValue)) { //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings only when useSubmittedValue==true, else use the real item value Objects
0793:
0794: if (!isSelectOne)
0795: writer.startElement(HTML.LI_ELEM, component);
0796: writer.writeText(selectItem.getLabel(), null);
0797: if (!isSelectOne)
0798: writer.endElement(HTML.LI_ELEM);
0799:
0800: if (isSelectOne) {
0801: //take care of several choices with the same value; use only the first one
0802: return;
0803: }
0804: }
0805: }
0806: }
0807: }
0808:
0809: public static void renderTableCaption(FacesContext context,
0810: ResponseWriter writer, UIComponent component)
0811: throws IOException {
0812: UIComponent captionFacet = component.getFacet("caption");
0813: if (captionFacet == null)
0814: return;
0815:
0816: String captionClass;
0817: String captionStyle;
0818:
0819: if (component instanceof HtmlPanelGrid) {
0820: HtmlPanelGrid panelGrid = (HtmlPanelGrid) component;
0821: captionClass = panelGrid.getCaptionClass();
0822: captionStyle = panelGrid.getCaptionStyle();
0823: } else if (component instanceof HtmlDataTable) {
0824: HtmlDataTable dataTable = (HtmlDataTable) component;
0825: captionClass = dataTable.getCaptionClass();
0826: captionStyle = dataTable.getCaptionStyle();
0827: } else {
0828: captionClass = (String) component
0829: .getAttributes()
0830: .get(
0831: org.apache.myfaces.shared_impl.renderkit.JSFAttr.CAPTION_CLASS_ATTR);
0832: captionStyle = (String) component
0833: .getAttributes()
0834: .get(
0835: org.apache.myfaces.shared_impl.renderkit.JSFAttr.CAPTION_STYLE_ATTR);
0836: }
0837:
0838: HtmlRendererUtils.writePrettyLineSeparator(context);
0839: writer.startElement(HTML.CAPTION_ELEM, component);
0840:
0841: if (captionClass != null) {
0842: writer.writeAttribute(HTML.CLASS_ATTR, captionClass, null);
0843: }
0844:
0845: if (captionStyle != null) {
0846: writer.writeAttribute(HTML.STYLE_ATTR, captionStyle, null);
0847: }
0848:
0849: RendererUtils.renderChild(context, captionFacet);
0850:
0851: writer.endElement(HTML.CAPTION_ELEM);
0852: }
0853:
0854: public static String getDisplayValueOnlyStyleClass(
0855: UIComponent component) {
0856:
0857: if (component instanceof org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) {
0858: if (((org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) component)
0859: .getDisplayValueOnlyStyleClass() != null)
0860: return ((org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) component)
0861: .getDisplayValueOnlyStyleClass();
0862:
0863: UIComponent parent = component;
0864:
0865: while ((parent = parent.getParent()) != null) {
0866: if (parent instanceof org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable
0867: && ((org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) parent)
0868: .getDisplayValueOnlyStyleClass() != null) {
0869: return ((org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) parent)
0870: .getDisplayValueOnlyStyleClass();
0871: }
0872: }
0873: }
0874:
0875: return null;
0876: }
0877:
0878: public static String getDisplayValueOnlyStyle(UIComponent component) {
0879:
0880: if (component instanceof DisplayValueOnlyCapable) {
0881: if (((org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) component)
0882: .getDisplayValueOnlyStyle() != null)
0883: return ((org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) component)
0884: .getDisplayValueOnlyStyle();
0885:
0886: UIComponent parent = component;
0887:
0888: while ((parent = parent.getParent()) != null) {
0889: if (parent instanceof org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable
0890: && ((DisplayValueOnlyCapable) parent)
0891: .getDisplayValueOnlyStyle() != null) {
0892: return ((DisplayValueOnlyCapable) parent)
0893: .getDisplayValueOnlyStyle();
0894: }
0895: }
0896: }
0897:
0898: return null;
0899: }
0900:
0901: public static boolean isDisplayValueOnly(UIComponent component) {
0902:
0903: if (component instanceof DisplayValueOnlyCapable) {
0904: if (((DisplayValueOnlyCapable) component)
0905: .isSetDisplayValueOnly())
0906: return ((org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) component)
0907: .isDisplayValueOnly();
0908:
0909: UIComponent parent = component;
0910:
0911: while ((parent = parent.getParent()) != null) {
0912: if (parent instanceof DisplayValueOnlyCapable
0913: && ((DisplayValueOnlyCapable) parent)
0914: .isSetDisplayValueOnly()) {
0915: return ((org.apache.myfaces.shared_impl.component.DisplayValueOnlyCapable) parent)
0916: .isDisplayValueOnly();
0917: }
0918: }
0919: }
0920:
0921: return false;
0922: }
0923:
0924: public static void renderDisplayValueOnly(
0925: FacesContext facesContext, UIInput input)
0926: throws IOException {
0927: ResponseWriter writer = facesContext.getResponseWriter();
0928: writer
0929: .startElement(
0930: org.apache.myfaces.shared_impl.renderkit.html.HTML.SPAN_ELEM,
0931: input);
0932:
0933: writeIdIfNecessary(writer, input, facesContext);
0934:
0935: renderDisplayValueOnlyAttributes(input, writer);
0936:
0937: String strValue = RendererUtils.getStringValue(facesContext,
0938: input);
0939: writer.write(HTMLEncoder.encode(strValue, true, true));
0940:
0941: writer.endElement(HTML.SPAN_ELEM);
0942: }
0943:
0944: public static void appendClearHiddenCommandFormParamsFunctionCall(
0945: StringBuffer buf, String formName) {
0946: appendClearHiddenCommandFormParamsFunctionCall(
0947: new ScriptContext(buf, false), formName);
0948: }
0949:
0950: private static void appendClearHiddenCommandFormParamsFunctionCall(
0951: ScriptContext context, String formName) {
0952:
0953: String functionName = HtmlRendererUtils
0954: .getClearHiddenCommandFormParamsFunctionName(formName);
0955:
0956: if (formName == null) {
0957: context.prettyLine();
0958: context.append("var clearFn = ");
0959: context.append(functionName);
0960: context.append(";");
0961: context.prettyLine();
0962: context
0963: .append("if(typeof eval('window.'+clearFn)=='function')");
0964: context.append("{");
0965: context.append("eval('window.'+clearFn+'(formName)');");
0966: context.append("}");
0967: } else {
0968: context.prettyLine();
0969: context.append("if(typeof window.");
0970: context.append(functionName);
0971: context.append("=='function')");
0972: context.append("{");
0973: context.append(functionName).append("('").append(formName)
0974: .append("');");
0975: context.append("}");
0976: }
0977: }
0978:
0979: public static void renderFormSubmitScript(FacesContext facesContext)
0980: throws IOException {
0981:
0982: Map map = facesContext.getExternalContext().getRequestMap();
0983: Boolean firstScript = (Boolean) map
0984: .get(FIRST_SUBMIT_SCRIPT_ON_PAGE);
0985:
0986: if (firstScript == null || firstScript.equals(Boolean.TRUE)) {
0987: map.put(FIRST_SUBMIT_SCRIPT_ON_PAGE, Boolean.FALSE);
0988: HtmlRendererUtils
0989: .renderFormSubmitScriptIfNecessary(facesContext);
0990:
0991: }
0992: }
0993:
0994: private static void renderFormSubmitScriptIfNecessary(
0995: FacesContext facesContext) throws IOException {
0996: ResponseWriter writer = facesContext.getResponseWriter();
0997:
0998: writer.startElement(HTML.SCRIPT_ELEM, null);
0999: writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
1000:
1001: final ExternalContext externalContext = facesContext
1002: .getExternalContext();
1003: final MyfacesConfig currentInstance = MyfacesConfig
1004: .getCurrentInstance(externalContext);
1005: boolean autoScroll = currentInstance.isAutoScroll();
1006:
1007: ScriptContext context = new ScriptContext(currentInstance
1008: .isPrettyHtml());
1009: context.prettyLine();
1010: context.increaseIndent();
1011:
1012: prepareScript(context, autoScroll);
1013:
1014: writer.writeText(context.toString(), null);
1015:
1016: writer.endElement(HTML.SCRIPT_ELEM);
1017: }
1018:
1019: private static void prepareScript(ScriptContext context,
1020: boolean autoScroll) {
1021:
1022: context.prettyLine();
1023:
1024: //render a function to create a hidden input, if it doesn't exist
1025: context.append("function ");
1026: context.append(SET_HIDDEN_INPUT_FN_NAME).append(
1027: "(formname, name, value)");
1028: context.append("{");
1029: context.append("var form = document.forms[formname];");
1030: context.prettyLine();
1031: context.append("if(typeof form.elements[name]=='undefined')");
1032: context.append("{");
1033: context
1034: .append("var newInput = document.createElement('input');");
1035: context.prettyLine();
1036: context.append("newInput.setAttribute('type','hidden');");
1037: context.prettyLine();
1038: context.append("newInput.setAttribute('name',name);");
1039: context.prettyLine();
1040: context.append("newInput.setAttribute('value',value);");
1041: context.prettyLine();
1042: context.append("form.appendChild(newInput);");
1043: context.append("}");
1044: context.append("else");
1045: context.append("{");
1046: context.append("form.elements[name].value=value;");
1047: context.append("}");
1048:
1049: context.append("}");
1050:
1051: context.prettyLine();
1052:
1053: context.prettyLine();
1054:
1055: //render a function to clear a hidden input, if it exists
1056: context.append("function ");
1057: context.append(CLEAR_HIDDEN_INPUT_FN_NAME).append(
1058: "(formname, name, value)");
1059: context.append("{");
1060: context.append("var form = document.forms[formname];");
1061: context.prettyLine();
1062: context.append("if(typeof form.elements[name]!='undefined')");
1063: context.append("{");
1064: context.append("form.elements[name].value=null;");
1065: context.append("}");
1066:
1067: context.append("}");
1068:
1069: context.prettyLine();
1070:
1071: context.append("function ");
1072: context.append(SUBMIT_FORM_FN_NAME).append(
1073: "(formName, linkId, target, params)");
1074: context.append("{");
1075:
1076: //call the script to clear the form (clearFormHiddenParams_<formName>) method - optionally, only necessary for IE5.5.
1077: //todo: if IE5.5. is ever desupported, we can get rid of this and instead rely on the last part of this script to
1078: //clear the parameters
1079: HtmlRendererUtils
1080: .appendClearHiddenCommandFormParamsFunctionCall(
1081: context, null);
1082:
1083: if (autoScroll) {
1084: appendAutoScrollAssignment(context, null);
1085: }
1086:
1087: context.prettyLine();
1088:
1089: //set the target (and save it)
1090: context.append("var oldTarget = '';");
1091: context.prettyLine();
1092: context
1093: .append("if((typeof target=='function') && target != null)");
1094: context.append("{");
1095: context.append("oldTarget=document.forms[formName].target;");
1096: context.prettyLine();
1097: context.append("document.forms[formName].target=target;");
1098: context.append("}");
1099:
1100: //set the submit parameters
1101:
1102: context
1103: .append("if((typeof params!='undefined') && params != null)");
1104: context.append("{");
1105: context.append("for(var i=0; i<params.length; i++)");
1106: context.append("{");
1107: context.append(SET_HIDDEN_INPUT_FN_NAME).append(
1108: "(formName,params[i][0], params[i][1]);");
1109: context.append("}");
1110: context.append("}");
1111:
1112: context.prettyLine();
1113:
1114: context.append(SET_HIDDEN_INPUT_FN_NAME);
1115: context.append("(formName,formName +'"
1116: + NamingContainer.SEPARATOR_CHAR + "'+'"
1117: + HtmlRendererUtils.HIDDEN_COMMANDLINK_FIELD_NAME
1118: + "',linkId);");
1119:
1120: context.prettyLine();
1121: context.prettyLine();
1122:
1123: //do the actual submit calls
1124:
1125: context.append("if(document.forms[formName].onsubmit)");
1126: context.append("{");
1127: context
1128: .append("var result=document.forms[formName].onsubmit();");
1129: context.prettyLine();
1130: context.append("if((typeof result=='undefined')||result)");
1131: context.append("{");
1132: context.append("document.forms[formName].submit();");
1133: context.append("}");
1134: context.append("}");
1135: context.append("else ");
1136: context.append("{");
1137: context.append("document.forms[formName].submit();");
1138: context.append("}");
1139:
1140: //reset the target
1141: context.append("if(oldTarget==null) oldTarget='';");
1142: context.prettyLine();
1143: context.append("document.forms[formName].target=oldTarget;");
1144: context.prettyLine();
1145:
1146: //clear the individual parameters - to make sure that even if the clear-function isn't called,
1147: // the back button/resubmit functionality will still work in all browsers except IE 5.5.
1148:
1149: context
1150: .append("if((typeof params!='undefined') && params != null)");
1151: context.append("{");
1152: context.append("for(var i=0; i<params.length; i++)");
1153: context.append("{");
1154: context.append(CLEAR_HIDDEN_INPUT_FN_NAME).append(
1155: "(formName,params[i][0], params[i][1]);");
1156: context.append("}");
1157: context.append("}");
1158:
1159: context.prettyLine();
1160:
1161: context.append(CLEAR_HIDDEN_INPUT_FN_NAME);
1162: context.append("(formName,formName +'"
1163: + NamingContainer.SEPARATOR_CHAR + "'+'"
1164: + HtmlRendererUtils.HIDDEN_COMMANDLINK_FIELD_NAME
1165: + "',linkId);");
1166:
1167: //return false, so that browser does not handle the click
1168: context.append("return false;");
1169: context.append("}");
1170:
1171: context.prettyLineDecreaseIndent();
1172: }
1173:
1174: /**
1175: * Adds the hidden form input value assignment that is necessary for the autoscroll
1176: * feature to an html link or button onclick attribute.
1177: */
1178: public static void appendAutoScrollAssignment(
1179: StringBuffer onClickValue, String formName) {
1180: appendAutoScrollAssignment(new ScriptContext(onClickValue,
1181: false), formName);
1182: }
1183:
1184: private static void appendAutoScrollAssignment(
1185: ScriptContext scriptContext, String formName) {
1186: String formNameStr = formName == null ? "formName"
1187: : (new StringBuffer("'").append(formName).append("'")
1188: .toString());
1189: String paramName = new StringBuffer().append("'").append(
1190: AUTO_SCROLL_PARAM).append("'").toString();
1191: String value = new StringBuffer().append(AUTO_SCROLL_FUNCTION)
1192: .append("()").toString();
1193:
1194: scriptContext.prettyLine();
1195: scriptContext.append("if(typeof window." + AUTO_SCROLL_FUNCTION
1196: + "!='undefined')");
1197: scriptContext.append("{");
1198: scriptContext.append(SET_HIDDEN_INPUT_FN_NAME);
1199: scriptContext.append("(").append(formNameStr).append(",")
1200: .append(paramName).append(",").append(value).append(
1201: ");");
1202: scriptContext.append("}");
1203:
1204: }
1205:
1206: /**
1207: * Renders the hidden form input that is necessary for the autoscroll feature.
1208: */
1209: public static void renderAutoScrollHiddenInput(
1210: FacesContext facesContext, ResponseWriter writer)
1211: throws IOException {
1212: writePrettyLineSeparator(facesContext);
1213: writer.startElement(HTML.INPUT_ELEM, null);
1214: writer.writeAttribute(HTML.TYPE_ATTR, "hidden", null);
1215: writer.writeAttribute(HTML.NAME_ATTR, AUTO_SCROLL_PARAM, null);
1216: writer.endElement(HTML.INPUT_ELEM);
1217: writePrettyLineSeparator(facesContext);
1218: }
1219:
1220: /**
1221: * Renders the autoscroll javascript function.
1222: */
1223: public static void renderAutoScrollFunction(
1224: FacesContext facesContext, ResponseWriter writer)
1225: throws IOException {
1226: writePrettyLineSeparator(facesContext);
1227: writer.startElement(HTML.SCRIPT_ELEM, null);
1228: writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,
1229: HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
1230:
1231: ScriptContext script = new ScriptContext(MyfacesConfig
1232: .getCurrentInstance(facesContext.getExternalContext())
1233: .isPrettyHtml());
1234:
1235: script.prettyLineIncreaseIndent();
1236:
1237: script.append("function ");
1238: script.append(AUTO_SCROLL_FUNCTION);
1239: script.append("()");
1240: script.append("{");
1241: script.append("var x = 0; var y = 0;");
1242: script.append("if (self.pageXOffset || self.pageYOffset)");
1243: script.append("{");
1244: script.append("x = self.pageXOffset;");
1245: script.prettyLine();
1246: script.append("y = self.pageYOffset;");
1247: script.append("}");
1248: script
1249: .append(" else if ((document.documentElement && document.documentElement.scrollLeft)||(document.documentElement && document.documentElement.scrollTop))");
1250: script.append("{");
1251: script.append("x = document.documentElement.scrollLeft;");
1252: script.prettyLine();
1253: script.append("y = document.documentElement.scrollTop;");
1254: script.append("}");
1255: script.append(" else if (document.body) ");
1256: script.append("{");
1257: script.append("x = document.body.scrollLeft;");
1258: script.prettyLine();
1259: script.append("y = document.body.scrollTop;");
1260: script.append("}");
1261: script.append("return x + \",\" + y;");
1262: script.append("}");
1263:
1264: ExternalContext externalContext = facesContext
1265: .getExternalContext();
1266: String oldViewId = JavascriptUtils
1267: .getOldViewId(externalContext);
1268: if (oldViewId != null
1269: && oldViewId.equals(facesContext.getViewRoot()
1270: .getViewId())) {
1271: //ok, we stayed on the same page, so let's scroll it to the former place
1272: String scrolling = (String) externalContext
1273: .getRequestParameterMap().get(AUTO_SCROLL_PARAM);
1274: if (scrolling != null && scrolling.length() > 0) {
1275: int x = 0;
1276: int y = 0;
1277: int comma = scrolling.indexOf(',');
1278: if (comma == -1) {
1279: log.warn("Illegal autoscroll request parameter: "
1280: + scrolling);
1281: } else {
1282: try {
1283: //we convert to int against XSS vulnerability
1284: x = Integer.parseInt(scrolling.substring(0,
1285: comma));
1286: } catch (NumberFormatException e) {
1287: log
1288: .warn("Error getting x offset for autoscroll feature. Bad param value: "
1289: + scrolling);
1290: x = 0; //ignore false numbers
1291: }
1292:
1293: try {
1294: //we convert to int against XSS vulnerability
1295: y = Integer.parseInt(scrolling
1296: .substring(comma + 1));
1297: } catch (NumberFormatException e) {
1298: log
1299: .warn("Error getting y offset for autoscroll feature. Bad param value: "
1300: + scrolling);
1301: y = 0; //ignore false numbers
1302: }
1303: }
1304: script.append("window.scrollTo(").append(x).append(",")
1305: .append(y).append(");\n");
1306: }
1307: }
1308:
1309: writer.writeText(script.toString(), null);
1310:
1311: writer.endElement(HTML.SCRIPT_ELEM);
1312: writePrettyLineSeparator(facesContext);
1313: }
1314:
1315: public static boolean isAllowedCdataSection(FacesContext fc) {
1316: Boolean value = null;
1317:
1318: if (fc != null) {
1319: value = (Boolean) fc.getExternalContext().getRequestMap()
1320: .get(ALLOW_CDATA_SECTION_ON);
1321: }
1322:
1323: return value != null && ((Boolean) value).booleanValue();
1324: }
1325:
1326: public static void allowCdataSection(FacesContext fc,
1327: boolean cdataSectionAllowed) {
1328: fc.getExternalContext().getRequestMap().put(
1329: ALLOW_CDATA_SECTION_ON,
1330: Boolean.valueOf(cdataSectionAllowed));
1331: }
1332:
1333: public static class LinkParameter {
1334: private String _name;
1335:
1336: private Object _value;
1337:
1338: public String getName() {
1339: return _name;
1340: }
1341:
1342: public void setName(String name) {
1343: _name = name;
1344: }
1345:
1346: public Object getValue() {
1347: return _value;
1348: }
1349:
1350: public void setValue(Object value) {
1351: _value = value;
1352: }
1353:
1354: }
1355:
1356: public static void renderHiddenCommandFormParams(
1357: ResponseWriter writer, Set dummyFormParams)
1358: throws IOException {
1359: for (Iterator it = dummyFormParams.iterator(); it.hasNext();) {
1360: Object name = it.next();
1361: renderHiddenInputField(writer, name, null);
1362: }
1363: }
1364:
1365: public static void renderHiddenInputField(ResponseWriter writer,
1366: Object name, Object value) throws IOException {
1367: writer.startElement(HTML.INPUT_ELEM, null);
1368: writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN,
1369: null);
1370: writer.writeAttribute(HTML.NAME_ATTR, name, null);
1371: if (value != null) {
1372: writer.writeAttribute(HTML.VALUE_ATTR, value, null);
1373: }
1374: writer.endElement(HTML.INPUT_ELEM);
1375: }
1376:
1377: /**
1378: * Renders a label HTML element
1379: */
1380: public static void renderLabel(ResponseWriter writer,
1381: UIComponent component, String forClientId,
1382: String labelValue, boolean disabled) throws IOException {
1383: writer.startElement(HTML.LABEL_ELEM, component);
1384: writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1385:
1386: String labelClass = null;
1387:
1388: if (disabled) {
1389: labelClass = (String) component.getAttributes().get(
1390: JSFAttr.DISABLED_CLASS_ATTR);
1391: } else {
1392: labelClass = (String) component
1393: .getAttributes()
1394: .get(
1395: org.apache.myfaces.shared_impl.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1396: }
1397: if (labelClass != null) {
1398: writer.writeAttribute("class", labelClass, "labelClass");
1399: }
1400:
1401: if ((labelValue != null) && (labelValue.length() > 0)) {
1402: writer.write(HTML.NBSP_ENTITY);
1403: writer.writeText(labelValue, null);
1404: }
1405:
1406: writer.endElement(HTML.LABEL_ELEM);
1407: }
1408:
1409: /**
1410: * Render the javascript function that is called on a click on a commandLink
1411: * to clear the hidden inputs. This is necessary because on a browser back,
1412: * each hidden input still has it's old value (browser cache!) and therefore
1413: * a new submit would cause the according action once more!
1414: *
1415: * @param writer
1416: * @param formName
1417: * @param dummyFormParams
1418: * @param formTarget
1419: * @throws IOException
1420: */
1421: public static void renderClearHiddenCommandFormParamsFunction(
1422: ResponseWriter writer, String formName,
1423: Set dummyFormParams, String formTarget) throws IOException {
1424: //render the clear hidden inputs javascript function
1425: String functionName = getClearHiddenCommandFormParamsFunctionName(formName);
1426: writer.startElement(HTML.SCRIPT_ELEM, null);
1427: writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
1428:
1429: // Using writeComment instead of write with <!-- tag
1430: StringBuffer script = new StringBuffer();
1431: script.append("function ");
1432: script.append(functionName);
1433: script.append("() {");
1434: if (dummyFormParams != null) {
1435: script.append("\n var f = document.forms['");
1436: script.append(formName);
1437: script.append("'];");
1438: int i = 0;
1439: for (Iterator it = dummyFormParams.iterator(); it.hasNext();) {
1440: String elemVarName = "elem" + i;
1441: script.append("\n var ").append(elemVarName).append(
1442: " = ");
1443: script.append("f.elements['")
1444: .append((String) it.next()).append("'];");
1445: script.append("\n if (").append(elemVarName).append(
1446: ".value != '') {");
1447: script.append("\n " + elemVarName + ".value='';");
1448: script.append("\n }");
1449: i++;
1450: }
1451: }
1452: // clear form target
1453: script.append("\n f.target=");
1454: if (formTarget == null || formTarget.length() == 0) {
1455: //Normally one would think that setting target to null has the
1456: //desired effect, but once again IE is different...
1457: //Setting target to null causes IE to open a new window!
1458: script.append("'';");
1459: } else {
1460: script.append("'");
1461: script.append(formTarget);
1462: script.append("';");
1463: }
1464: script.append("\n}");
1465:
1466: //Just to be sure we call this clear method on each load.
1467: //Otherwise in the case, that someone submits a form by pressing Enter
1468: //within a text input, the hidden inputs won't be cleared!
1469: script.append("\n");
1470: script.append(functionName);
1471: script.append("();");
1472:
1473: writer.writeText(script.toString(), null);
1474: writer.endElement(HTML.SCRIPT_ELEM);
1475: }
1476:
1477: /**
1478: * Prefixes the given String with "clear_" and removes special characters
1479: *
1480: * @param formName
1481: * @return String
1482: */
1483: public static String getClearHiddenCommandFormParamsFunctionName(
1484: String formName) {
1485: if (formName == null) {
1486: return "'" + CLEAR_HIDDEN_FIELD_FN_NAME
1487: + "_'+formName.replace(/-/g, '\\$"
1488: + NamingContainer.SEPARATOR_CHAR + "').replace(/"
1489: + NamingContainer.SEPARATOR_CHAR + "/g,'_')";
1490: }
1491:
1492: return JavascriptUtils
1493: .getValidJavascriptNameAsInRI(CLEAR_HIDDEN_FIELD_FN_NAME
1494: + "_"
1495: + formName.replace(
1496: NamingContainer.SEPARATOR_CHAR, '_'));
1497: }
1498:
1499: public static String getClearHiddenCommandFormParamsFunctionNameMyfacesLegacy(
1500: String formName) {
1501: return "clear_"
1502: + JavascriptUtils.getValidJavascriptName(formName,
1503: false);
1504: }
1505:
1506: /**
1507: * Get the name of the request parameter that holds the id of the
1508: * link-type component that caused the form to be submitted.
1509: * <p/>
1510: * Within each page there may be multiple "link" type components that
1511: * cause page submission. On the server it is necessary to know which
1512: * of these actually caused the submit, in order to invoke the correct
1513: * listeners. Such components therefore store their id into the
1514: * "hidden command link field" in their associated form before
1515: * submitting it.
1516: * <p/>
1517: * The field is always a direct child of each form, and has the same
1518: * <i>name</i> in each form. The id of the form component is therefore
1519: * both necessary and sufficient to determine the full name of the
1520: * field.
1521: */
1522: public static String getHiddenCommandLinkFieldName(FormInfo formInfo) {
1523: if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm())) {
1524: return HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD;
1525: }
1526: return formInfo.getFormName() + NamingContainer.SEPARATOR_CHAR
1527: + HIDDEN_COMMANDLINK_FIELD_NAME;
1528: }
1529:
1530: public static String getHiddenCommandLinkFieldNameMyfacesOld(
1531: FormInfo formInfo) {
1532: return formInfo.getFormName() + NamingContainer.SEPARATOR_CHAR
1533: + HIDDEN_COMMANDLINK_FIELD_NAME_MYFACES_OLD;
1534: }
1535:
1536: private static String HTML_CONTENT_TYPE = "text/html";
1537: private static String TEXT_ANY_CONTENT_TYPE = "text/*";
1538: private static String ANY_CONTENT_TYPE = "*/*";
1539:
1540: public static String DEFAULT_CHAR_ENCODING = "ISO-8859-1";
1541: private static String XHTML_CONTENT_TYPE = "application/xhtml+xml";
1542: private static String APPLICATION_XML_CONTENT_TYPE = "application/xml";
1543: private static String TEXT_XML_CONTENT_TYPE = "text/xml";
1544:
1545: public static String selectContentType(String contentTypeListString) {
1546: if (contentTypeListString == null) {
1547: FacesContext context = FacesContext.getCurrentInstance();
1548: if (context != null) {
1549: contentTypeListString = (String) context
1550: .getExternalContext().getRequestHeaderMap()
1551: .get("Accept");
1552: }
1553:
1554: if (contentTypeListString == null) {
1555: if (log.isDebugEnabled())
1556: log
1557: .debug("No content type list given, creating HtmlResponseWriterImpl with default content type.");
1558:
1559: contentTypeListString = HTML_CONTENT_TYPE;
1560: }
1561: }
1562:
1563: List contentTypeList = splitContentTypeListString(contentTypeListString);
1564: String[] supportedContentTypeArray = getSupportedContentTypes();
1565:
1566: String selectedContentType = null;
1567:
1568: for (int i = 0; i < supportedContentTypeArray.length; i++) {
1569: String supportedContentType = supportedContentTypeArray[i]
1570: .trim();
1571:
1572: for (int j = 0; j < contentTypeList.size(); j++) {
1573: String contentType = (String) contentTypeList.get(j);
1574:
1575: if (contentType.indexOf(supportedContentType) != -1) {
1576: if (isHTMLContentType(contentType)) {
1577: selectedContentType = HTML_CONTENT_TYPE;
1578: }
1579:
1580: else if (isXHTMLContentType(contentType)) {
1581: selectedContentType = XHTML_CONTENT_TYPE;
1582: }
1583: break;
1584: }
1585: }
1586: if (selectedContentType != null) {
1587: break;
1588: }
1589: }
1590:
1591: if (selectedContentType == null) {
1592: throw new IllegalArgumentException(
1593: "ContentTypeList does not contain a supported content type: "
1594: + contentTypeListString);
1595: }
1596: return selectedContentType;
1597: }
1598:
1599: public static String[] getSupportedContentTypes() {
1600: String[] supportedContentTypeArray = new String[] {
1601: HTML_CONTENT_TYPE, ANY_CONTENT_TYPE,
1602: XHTML_CONTENT_TYPE, APPLICATION_XML_CONTENT_TYPE,
1603: TEXT_XML_CONTENT_TYPE };
1604: return supportedContentTypeArray;
1605: }
1606:
1607: private static boolean isHTMLContentType(String contentType) {
1608: return contentType.indexOf(HTML_CONTENT_TYPE) != -1
1609: || contentType.indexOf(ANY_CONTENT_TYPE) != -1
1610: || contentType.indexOf(TEXT_ANY_CONTENT_TYPE) != -1;
1611: }
1612:
1613: public static boolean isXHTMLContentType(String contentType) {
1614: return contentType.indexOf(XHTML_CONTENT_TYPE) != -1
1615: || contentType.indexOf(APPLICATION_XML_CONTENT_TYPE) != -1
1616: || contentType.indexOf(TEXT_XML_CONTENT_TYPE) != -1;
1617: }
1618:
1619: private static List splitContentTypeListString(
1620: String contentTypeListString) {
1621: List contentTypeList = new ArrayList();
1622:
1623: StringTokenizer st = new StringTokenizer(contentTypeListString,
1624: ",");
1625: while (st.hasMoreTokens()) {
1626: String contentType = st.nextToken().trim();
1627:
1628: int semicolonIndex = contentType.indexOf(";");
1629:
1630: if (semicolonIndex != -1) {
1631: contentType = contentType.substring(0, semicolonIndex);
1632: }
1633:
1634: contentTypeList.add(contentType);
1635: }
1636:
1637: return contentTypeList;
1638: }
1639:
1640: public static String getJavascriptLocation(UIComponent component) {
1641: if (component == null)
1642: return null;
1643:
1644: return (String) component.getAttributes().get(
1645: JSFAttr.JAVASCRIPT_LOCATION);
1646: }
1647:
1648: public static String getImageLocation(UIComponent component) {
1649: if (component == null)
1650: return null;
1651:
1652: return (String) component.getAttributes().get(
1653: JSFAttr.IMAGE_LOCATION);
1654: }
1655:
1656: public static String getStyleLocation(UIComponent component) {
1657: if (component == null)
1658: return null;
1659:
1660: return (String) component.getAttributes().get(
1661: JSFAttr.STYLE_LOCATION);
1662: }
1663:
1664: /**
1665: * The ScriptContext offers methods and fields
1666: * to help with rendering out a script and keeping a
1667: * proper formatting.
1668: */
1669: public static class ScriptContext {
1670: private long currentIndentationLevel;
1671: private StringBuffer buffer = new StringBuffer();
1672: private boolean prettyPrint = false;
1673: /**
1674: * automatic formatting will render
1675: * new-lines and indents if blocks are opened
1676: * and closed - attention: you need to append
1677: * opening and closing brackets of blocks separately in this case!
1678: */
1679: private boolean automaticFormatting = true;
1680:
1681: public ScriptContext() {
1682:
1683: }
1684:
1685: public ScriptContext(boolean prettyPrint) {
1686: this .prettyPrint = prettyPrint;
1687: }
1688:
1689: public ScriptContext(StringBuffer buf, boolean prettyPrint) {
1690: this .prettyPrint = prettyPrint;
1691: this .buffer = buf;
1692: }
1693:
1694: public void increaseIndent() {
1695: currentIndentationLevel++;
1696: }
1697:
1698: public void decreaseIndent() {
1699: currentIndentationLevel--;
1700:
1701: if (currentIndentationLevel < 0)
1702: currentIndentationLevel = 0;
1703: }
1704:
1705: public void prettyLine() {
1706: if (prettyPrint) {
1707: append(LINE_SEPARATOR);
1708:
1709: for (int i = 0; i < getCurrentIndentationLevel(); i++)
1710: append(TABULATOR);
1711: }
1712: }
1713:
1714: public void prettyLineIncreaseIndent() {
1715: increaseIndent();
1716: prettyLine();
1717: }
1718:
1719: public void prettyLineDecreaseIndent() {
1720: decreaseIndent();
1721: prettyLine();
1722: }
1723:
1724: public long getCurrentIndentationLevel() {
1725: return currentIndentationLevel;
1726: }
1727:
1728: public void setCurrentIndentationLevel(
1729: long currentIndentationLevel) {
1730: this .currentIndentationLevel = currentIndentationLevel;
1731: }
1732:
1733: public ScriptContext append(String str) {
1734:
1735: if (automaticFormatting && str.length() == 1) {
1736: boolean openBlock = str.equals("{");
1737: boolean closeBlock = str.equals("}");
1738:
1739: if (openBlock) {
1740: prettyLine();
1741: } else if (closeBlock) {
1742: prettyLineDecreaseIndent();
1743: }
1744:
1745: buffer.append(str);
1746:
1747: if (openBlock) {
1748: prettyLineIncreaseIndent();
1749: } else if (closeBlock) {
1750: prettyLine();
1751: }
1752: } else {
1753: buffer.append(str);
1754: }
1755: return this ;
1756: }
1757:
1758: public ScriptContext append(char c) {
1759: buffer.append(c);
1760: return this ;
1761: }
1762:
1763: public ScriptContext append(int i) {
1764: buffer.append(i);
1765: return this ;
1766: }
1767:
1768: public String toString() {
1769: return buffer.toString();
1770: }
1771: }
1772: }
|