001/* 002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Thomas Roger 016 */ 017 018package org.nuxeo.ecm.platform.ui.web.renderer; 019 020import java.io.IOException; 021import java.util.Map; 022 023import javax.faces.component.UIComponent; 024import javax.faces.component.UIInput; 025import javax.faces.component.UIOutput; 026import javax.faces.component.html.HtmlInputFile; 027import javax.faces.context.FacesContext; 028import javax.faces.context.ResponseWriter; 029 030import com.sun.faces.renderkit.Attribute; 031import com.sun.faces.renderkit.AttributeManager; 032import com.sun.faces.renderkit.RenderKitUtils; 033import com.sun.faces.renderkit.html_basic.TextRenderer; 034 035/** 036 * Override default {@code TextRenderer} to handle HTML5 attributes. 037 * 038 * @since 5.7 039 */ 040public class NXTextRenderer extends TextRenderer { 041 042 private static final Attribute[] INPUT_ATTRIBUTES = AttributeManager.getAttributes(AttributeManager.Key.INPUTTEXT); 043 044 private static final Attribute[] OUTPUT_ATTRIBUTES = AttributeManager.getAttributes( 045 AttributeManager.Key.OUTPUTTEXT); 046 047 @Override 048 protected void getEndTextToRender(FacesContext context, UIComponent component, String currentValue) 049 throws IOException { 050 ResponseWriter writer = context.getResponseWriter(); 051 assert (writer != null); 052 boolean shouldWriteIdAttribute = false; 053 boolean isOutput = false; 054 055 String style = (String) component.getAttributes().get("style"); 056 String styleClass = (String) component.getAttributes().get("styleClass"); 057 String dir = (String) component.getAttributes().get("dir"); 058 String lang = (String) component.getAttributes().get("lang"); 059 String title = (String) component.getAttributes().get("title"); 060 String placeholder = (String) component.getAttributes().get("placeholder"); 061 Map<String, Object> passthroughAttributes = component.getPassThroughAttributes(false); 062 boolean hasPassthroughAttributes = null != passthroughAttributes && !passthroughAttributes.isEmpty(); 063 if (component instanceof UIInput) { 064 writer.startElement("input", component); 065 writeIdAttributeIfNecessary(context, writer, component); 066 067 if (component instanceof HtmlInputFile) { 068 writer.writeAttribute("type", "file", null); 069 } else { 070 writer.writeAttribute("type", "text", null); 071 } 072 writer.writeAttribute("name", (component.getClientId(context)), "clientId"); 073 074 // only output the autocomplete attribute if the value 075 // is 'off' since its lack of presence will be interpreted 076 // as 'on' by the browser 077 if ("off".equals(component.getAttributes().get("autocomplete"))) { 078 writer.writeAttribute("autocomplete", "off", "autocomplete"); 079 } 080 081 // render default text specified 082 if (currentValue != null) { 083 writer.writeAttribute("value", currentValue, "value"); 084 } 085 if (null != styleClass) { 086 writer.writeAttribute("class", styleClass, "styleClass"); 087 } 088 089 // HTML5 attributes not handled by default TextRenderer 090 if (null != placeholder) { 091 writer.writeAttribute("placeholder", placeholder, "placeholder"); 092 } 093 094 // style is rendered as a passthur attribute 095 RenderKitUtils.renderPassThruAttributes(context, writer, component, INPUT_ATTRIBUTES, 096 getNonOnChangeBehaviors(component)); 097 RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component); 098 099 RenderKitUtils.renderOnchange(context, component, false); 100 101 writer.endElement("input"); 102 103 } else if (isOutput = (component instanceof UIOutput)) { 104 if (styleClass != null || style != null || dir != null || lang != null || title != null 105 || hasPassthroughAttributes || (shouldWriteIdAttribute = shouldWriteIdAttribute(component))) { 106 writer.startElement("span", component); 107 writeIdAttributeIfNecessary(context, writer, component); 108 if (null != styleClass) { 109 writer.writeAttribute("class", styleClass, "styleClass"); 110 } 111 // style is rendered as a passthru attribute 112 RenderKitUtils.renderPassThruAttributes(context, writer, component, OUTPUT_ATTRIBUTES); 113 114 } 115 if (currentValue != null) { 116 Object val = component.getAttributes().get("escape"); 117 if ((val != null) && Boolean.valueOf(val.toString())) { 118 writer.writeText(currentValue, component, "value"); 119 } else { 120 writer.write(currentValue); 121 } 122 } 123 } 124 if (isOutput && (styleClass != null || style != null || dir != null || lang != null || title != null 125 || hasPassthroughAttributes || (shouldWriteIdAttribute))) { 126 writer.endElement("span"); 127 } 128 } 129 130}