View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.custom.ajax.util;
21  
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.util.Iterator;
25  import java.util.Map;
26  
27  import javax.faces.application.FacesMessage;
28  import javax.faces.application.ViewHandler;
29  import javax.faces.component.UIComponent;
30  import javax.faces.component.UIOutput;
31  import javax.faces.context.FacesContext;
32  import javax.faces.context.ResponseWriter;
33  import javax.servlet.http.HttpServletResponse;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.myfaces.component.html.ext.HtmlMessages;
38  import org.apache.myfaces.custom.ajax.AjaxCallbacks;
39  import org.apache.myfaces.custom.inputAjax.HtmlInputTextAjax;
40  import org.apache.myfaces.custom.prototype.PrototypeResourceLoader;
41  import org.apache.myfaces.custom.util.ComponentUtils;
42  import org.apache.myfaces.renderkit.html.util.AddResource;
43  import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
44  import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
45  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
46  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlMessageRendererBase;
47  
48  /**
49   * @author Travis Reeder (latest modification by $Author: mmarinschek $)
50   * @version $Revision: 290397 $ $Date: 2005-09-20 10:35:09 +0200 (Di, 20 Sep 2005) $
51   */
52  public final class AjaxRendererUtils
53  {
54      public static final String STYLECLASS_LOADER = "myFacesInputSuggestAjax";
55  
56      /**
57       * util class.
58       */
59      private AjaxRendererUtils()
60      {
61          //util clazz
62      }
63  
64      private static final Log log = LogFactory.getLog(AjaxRendererUtils.class);
65      public static final String JAVASCRIPT_ENCODED = "org.apache.myfaces.custom.inputAjax.JAVASCRIPT_ENCODED";
66      public static final String JS_MYFACES_NAMESPACE = "_MyFaces_inputAjax_";
67  
68      public static void addPrototypeScript(FacesContext context, UIComponent component, AddResource addResource)
69      {
70          // todo: replace with DOJO
71          // todo: this doesn't need to be called from every component, move it into encodeAjax or something
72          String javascriptLocation = (String) component.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
73          if (javascriptLocation != null)
74          {
75              addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/prototype.js");
76              addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/effects.js");
77          }
78          else
79          {
80              addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, PrototypeResourceLoader.class, "prototype.js");
81              addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, PrototypeResourceLoader.class, "effects.js");
82          }
83          addResource.addStyleSheet(context, AddResource.HEADER_BEGIN, AjaxRendererUtils.class, "myFaces_Ajax.css");
84      }
85  
86      public static void writeAjaxScript(FacesContext context, ResponseWriter out, AjaxCallbacks component)
87              throws IOException
88      {
89          writeAjaxScript(context, out, component, null);
90      }
91  
92      /**
93       * Not really liking having the extraParams thing, seems to inflexible for altering other things
94       *
95       * @param context
96       * @param out
97       * @param component
98       * @param extraParams
99       * @throws IOException
100      */
101     public static void writeAjaxScript(FacesContext context, ResponseWriter out, AjaxCallbacks component, String extraParams) throws IOException
102     {
103         UIComponent uiComponent = (UIComponent) component;
104         String clientId = uiComponent.getClientId(context);
105         String viewId = context.getViewRoot().getViewId();
106         ViewHandler viewHandler = context.getApplication().getViewHandler();
107         String ajaxURL = viewHandler.getActionURL(context, viewId);
108 
109         String ajaxMessagesId = null;
110 
111         if (uiComponent instanceof HtmlInputTextAjax)
112         {
113             //finding the corresponding messages component to display an ajaxMessage
114             UIComponent ajaxMessages = (UIComponent) ComponentUtils
115                     .findFirstMessagesComponent(context, context.getViewRoot());
116 
117             if (ajaxMessages != null && ((HtmlMessages) ajaxMessages).getForceSpan())
118             {
119                 ajaxMessagesId = ajaxMessages.getClientId(context);
120             }
121         }
122 
123         String jsNameSpace = //uiComponent.getId() +
124                 JS_MYFACES_NAMESPACE;
125         String AJAX_RESPONSE_MAP = JS_MYFACES_NAMESPACE + "ajaxResponseMap";
126 
127         // todo: only namespace the things that are specific to the component and only output those a second time, use comment below to limit
128         // // check to see if javascript has already been written
129 
130         if (!context.getExternalContext().getRequestMap().containsKey(JAVASCRIPT_ENCODED))
131         {
132             out.startElement(HTML.SCRIPT_ELEM, null);
133             out.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
134 
135             out.writeText("var " + jsNameSpace + "ajaxUrl = '" + ajaxURL + "';\n", null);
136             out.writeText("var " + jsNameSpace + "globalErrorsId = '" + ajaxMessagesId + "';\n", null);
137             // for component specific mappings
138             out.writeText("var " + AJAX_RESPONSE_MAP + " = new Object();\n", null);
139 
140             out.endElement(HTML.SCRIPT_ELEM);
141 
142             AddResource addResource = AddResourceFactory.getInstance(context);
143             addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN,
144                     AjaxRendererUtils.class, "inputAjax.js");
145                     //"/js/inputAjax.js");
146 
147             context.getExternalContext().getRequestMap().put(JAVASCRIPT_ENCODED, Boolean.TRUE);
148         }
149 
150         // component specific mappings, one per component
151         out.startElement(HTML.SCRIPT_ELEM, null);
152         out.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
153         out.writeText(AJAX_RESPONSE_MAP + "['" + clientId + "'] = new Object();\n", null);
154         if (component.getOnSuccess() != null)
155             out.writeText(AJAX_RESPONSE_MAP + "['" + clientId + "']['onSuccessFunction'] = " + component.getOnSuccess() + ";\n", null);
156         if (component.getOnFailure() != null)
157             out.writeText(AJAX_RESPONSE_MAP + "['" + clientId + "']['onFailureFunction'] = " + component.getOnFailure() + ";\n", null);
158         if (component.getOnStart() != null)
159             out.writeText(AJAX_RESPONSE_MAP + "['" + clientId + "']['onStartFunction'] = " + component.getOnStart() + ";\n", null);
160         out.endElement(HTML.SCRIPT_ELEM);
161 
162     }
163 
164 
165     public static void encodeAjax(FacesContext context, UIComponent component)
166             throws IOException
167     {
168         encodeAjax(context, component, null);
169     }
170 
171     /**
172      * Outputs elementUpdate elements with the client id and value.
173      * Also outputs error elements.
174      *
175      * @param context
176      * @param component
177      * @param extraReturnAttributes
178      * @throws IOException
179      */
180     public static void encodeAjax(FacesContext context, UIComponent component, Map extraReturnAttributes) throws IOException
181     {
182         String clientId = component.getClientId(context);
183         Object responseOb = context.getExternalContext().getResponse();
184         if (responseOb instanceof HttpServletResponse)
185         {
186             HttpServletResponse response = (HttpServletResponse) responseOb;
187             //HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
188             StringBuffer buff = new StringBuffer();
189 
190             // send error messages
191             boolean hasErrorMessages = false;
192             Iterator iter = context.getMessages(clientId);
193             while (iter.hasNext())
194             {
195                 FacesMessage msg = (FacesMessage) iter.next();
196                 String style = "";
197                 String styleClass = "";
198 
199                 String msgForId = clientId + "_msgFor";
200                 //System.out.println("Looking for component: " + msgForId);
201                 UIComponent msgComponent = context.getViewRoot().findComponent(msgForId);
202                 String msgId = null;
203                 if (msgComponent != null)
204                 {
205                     //System.out.println("Component found");
206                     // then send to update single component
207                     // get styleclass
208                     String[] styleAndClass = HtmlMessageRendererBase.getStyleAndStyleClass(msgComponent, msg.getSeverity());
209                     style = styleAndClass[0];
210                     styleClass = styleAndClass[1];
211                     msgId = msgComponent.getClientId(context);
212                     //System.out.println("style: " + style);
213                 }
214                 else
215                 {
216                     // send to update global messages, maybe this could happen on the client side though ?
217                 }
218                 buff.append("<error elname=\"").append(clientId)
219                         .append("\" severity=\"").append(msg.getSeverity().toString());
220                 if (styleClass != null) buff.append("\" styleClass=\"").append(styleClass);
221                 if (style != null) buff.append("\" style=\"").append(style);
222                 buff.append("\" summary=\"").append(msg.getSummary())
223                         .append("\" ");
224                         if(msgId != null) buff.append(" msgId=\"").append(msgId).append("\"");
225                         buff.append(">\n");
226                 String detail = msg.getDetail();
227                 if (detail != null)
228                 {
229                     buff.append("<detail>");
230                     buff.append(msg.getDetail());
231                     buff.append("</detail>\n");
232                 }
233                 buff.append("</error>\n");
234                 hasErrorMessages = true;
235             }
236             if (!hasErrorMessages)
237             {
238                 // send elementUpdated messages
239                 buff.append("<elementUpdated elname=\"").append(clientId).append("\"");
240                 if(component instanceof UIOutput){
241                     UIOutput uiOutput = (UIOutput) component;
242                     // todo: might have to make sure this value can be serialized like this
243                     // todo: and should probably be in text block, rather than an attribute
244                     buff.append(" elvalue=\"").append(uiOutput.getValue()).append("\"");
245                     // this is needed to know how to update the html element, javascript doesn't give the correct info!
246                     //buff.append(" eltype=\"output\"");
247                 }
248 
249                 if (extraReturnAttributes != null)
250                 {
251                     Iterator iter2 = extraReturnAttributes.keySet().iterator();
252                     while (iter2.hasNext())
253                     {
254                         String key = (String) iter2.next();
255                         buff.append(" ").append(key).append("=\"").append(extraReturnAttributes.get(key).toString()).append("\"");
256                     }
257                 }
258                 buff.append(" />");
259                 buff.append("\n");
260             }
261 
262             String output = buff.toString();
263             log.debug(output);
264             PrintWriter out = response.getWriter();
265             out.print(output);
266 
267         }
268     }
269 
270     /**
271      * Writes an animated image to show that something is happening
272      * @param context
273      * @param comp
274      */
275     public static void writeLoadingImage(FacesContext context, UIComponent comp) throws IOException
276     {
277         ResponseWriter writer = context.getResponseWriter();
278 
279         writer.startElement(HTML.SPAN_ELEM, null);
280         writer.writeAttribute(HTML.ID_ATTR, comp.getClientId(context) + "_loaderImg", null);
281         writer.writeAttribute(HTML.CLASS_ATTR, AjaxRendererUtils.STYLECLASS_LOADER, null);
282         // could alternatively use a
283         // spacer.gif to stretch the span, but didn't want to have another resource dependency.  It would be nice to
284         // have a "common" resource repository with spacer.gif and other common things
285         writer.write("<spacer type=\"block\" width=\"15\" height=\"15\"/>");
286         writer.endElement(HTML.SPAN_ELEM);
287 
288     }
289 }