1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.renderkit.html;
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.EnumSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.RandomAccess;
29 import java.util.Set;
30 import javax.faces.component.UIComponent;
31 import javax.faces.component.UIParameter;
32 import javax.faces.component.behavior.AjaxBehavior;
33 import javax.faces.component.behavior.ClientBehaviorContext;
34 import javax.faces.component.behavior.ClientBehaviorHolder;
35 import javax.faces.component.html.HtmlCommandScript;
36 import javax.faces.component.search.SearchExpressionContext;
37 import javax.faces.component.search.SearchExpressionHandler;
38 import javax.faces.component.search.SearchExpressionHint;
39 import javax.faces.context.FacesContext;
40 import javax.faces.context.ResponseWriter;
41 import javax.faces.event.ActionEvent;
42 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
43 import org.apache.myfaces.shared.renderkit.RendererUtils;
44 import org.apache.myfaces.shared.renderkit.html.HTML;
45 import org.apache.myfaces.shared.renderkit.html.HtmlRenderer;
46 import org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils;
47 import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
48 import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
49 import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
50 import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
51 import org.apache.myfaces.shared.util.StringUtils;
52
53 @JSFRenderer(
54 renderKitId="HTML_BASIC",
55 family="javax.faces.Command",
56 type="javax.faces.Script")
57 public class HtmlCommandScriptRenderer extends HtmlRenderer
58 {
59 private static final Set<SearchExpressionHint> EXPRESSION_HINTS =
60 EnumSet.of(SearchExpressionHint.RESOLVE_CLIENT_SIDE, SearchExpressionHint.RESOLVE_SINGLE_COMPONENT);
61
62 private static final String AJAX_KEY_ONERROR = "onerror";
63 private static final String AJAX_KEY_ONEVENT = "onevent";
64 private static final String AJAX_KEY_EXECUTE = "execute";
65 private static final String AJAX_KEY_RENDER = "render";
66 private static final String AJAX_KEY_DELAY = "delay";
67 private static final String AJAX_KEY_RESETVALUES = "resetValues";
68
69 private static final String AJAX_VAL_THIS = "this";
70 private static final String JS_AJAX_REQUEST = "jsf.ajax.request";
71
72 private static final String AJAX_SB = "oam.renderkit.AJAX_SB";
73 private static final String AJAX_PARAM_SB = "oam.renderkit.AJAX_PARAM_SB";
74
75 @Override
76 public void encodeBegin(FacesContext context, UIComponent component) throws IOException
77 {
78 super.encodeBegin(context, component);
79
80 HtmlCommandScript commandScript = (HtmlCommandScript) component;
81 ResponseWriter writer = context.getResponseWriter();
82
83 ResourceUtils.renderDefaultJsfJsInlineIfNecessary(context, writer);
84
85 writer.startElement(HTML.SPAN_ELEM, component);
86 writer.writeAttribute(HTML.ID_ATTR, component.getClientId(context), null);
87 writer.startElement(HTML.SCRIPT_ELEM, component);
88 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
89
90 HtmlRendererUtils.ScriptContext script = new HtmlRendererUtils.ScriptContext();
91
92
93 String cmdName = commandScript.getName();
94 String name;
95 if (cmdName != null && cmdName.length() > 0)
96 {
97 name = JavascriptUtils.getValidJavascriptName(cmdName, true);
98 }
99 else
100 {
101 name = JavascriptUtils.getValidJavascriptName(component.getClientId(context), true);
102 }
103
104 script.prettyLine();
105 script.increaseIndent();
106 script.append("var "+name+" = function(o){var o=(typeof o==='object')&&o?o:{};");
107 script.prettyLine();
108
109
110 AjaxBehavior ajaxBehavior = new AjaxBehavior();
111 Boolean resetValues = commandScript.getResetValues();
112 if (resetValues != null)
113 {
114 ajaxBehavior.setResetValues(resetValues);
115 }
116 ajaxBehavior.setOnerror(commandScript.getOnerror());
117 ajaxBehavior.setOnevent(commandScript.getOnevent());
118
119 Collection<ClientBehaviorContext.Parameter> eventParameters = null;
120
121 ClientBehaviorContext ccc = ClientBehaviorContext.createClientBehaviorContext(
122 context, component, "action",
123 commandScript.getClientId(context), eventParameters);
124
125 script.append(makeAjax(context, ccc, ajaxBehavior, commandScript).toString());
126 script.decreaseIndent();
127 script.append("}");
128
129
130 if (commandScript.isAutorun())
131 {
132 script.append(";");
133 script.append("myfaces._impl.core._Runtime.addOnLoad(window,");
134 script.append(name);
135 script.append(");");
136 }
137
138 writer.writeText(script.toString(), null);
139 }
140
141 @Override
142 public void encodeEnd(FacesContext context, UIComponent component) throws IOException
143 {
144 super.encodeEnd(context, component);
145 ResponseWriter writer = context.getResponseWriter();
146 writer.endElement(HTML.SCRIPT_ELEM);
147 writer.endElement(HTML.SPAN_ELEM);
148 }
149
150 @Override
151 public void decode(FacesContext facesContext, UIComponent component)
152 {
153 super.decode(facesContext, component);
154
155 HtmlCommandScript commandScript = (HtmlCommandScript) component;
156
157 if (HtmlRendererUtils.isDisabled(component) || !commandScript.isRendered())
158 {
159 return;
160 }
161
162 Map<String, String> paramMap = facesContext.getExternalContext().getRequestParameterMap();
163 String behaviorEventName = paramMap.get(ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME);
164 if (behaviorEventName != null)
165 {
166 String sourceId = paramMap.get(ClientBehaviorContext.BEHAVIOR_SOURCE_PARAM_NAME);
167 String componentClientId = component.getClientId(facesContext);
168 String clientId = sourceId;
169 if (sourceId.startsWith(componentClientId) && sourceId.length() > componentClientId.length())
170 {
171 String item = sourceId.substring(componentClientId.length()+1);
172
173
174 if (StringUtils.isInteger(item))
175 {
176 clientId = componentClientId;
177 }
178 }
179 if (component.getClientId(facesContext).equals(clientId))
180 {
181 boolean disabled = HtmlRendererUtils.isDisabled(component);
182 FormInfo formInfo = RendererUtils.findNestingForm(component, facesContext);
183 boolean activateActionEvent = false;
184 if (formInfo != null && !disabled)
185 {
186 String reqValue = (String) facesContext.getExternalContext().getRequestParameterMap().get(
187 HtmlRendererUtils.getHiddenCommandLinkFieldName(formInfo, facesContext));
188 activateActionEvent = reqValue != null && reqValue.equals(clientId)
189 || HtmlRendererUtils.isPartialOrBehaviorSubmit(facesContext, clientId);
190 if (activateActionEvent)
191 {
192 RendererUtils.initPartialValidationAndModelUpdate(component, facesContext);
193 }
194 }
195 if (activateActionEvent)
196 {
197 component.queueEvent(new ActionEvent(component));
198 }
199 }
200 }
201 if (component instanceof ClientBehaviorHolder && !HtmlRendererUtils.isDisabled(component))
202 {
203 HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
204 }
205 }
206
207
208
209
210
211
212
213
214
215
216 private StringBuilder makeAjax(FacesContext facesContext, ClientBehaviorContext context, AjaxBehavior behavior,
217 HtmlCommandScript commandScript)
218 {
219 StringBuilder retVal = SharedStringBuilder.get(context.getFacesContext(), AJAX_SB, 60);
220 StringBuilder paramBuffer = SharedStringBuilder.get(context.getFacesContext(), AJAX_PARAM_SB, 20);
221
222 SearchExpressionContext searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
223 context.getFacesContext(), context.getComponent(), EXPRESSION_HINTS, null);
224
225 String executes = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_EXECUTE, commandScript.getExecute(),
226 searchExpressionContext);
227 String render = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_RENDER, commandScript.getRender(),
228 searchExpressionContext);
229
230 String onError = behavior.getOnerror();
231 if (onError != null && !onError.trim().isEmpty())
232 {
233
234 paramBuffer.setLength(0);
235 paramBuffer.append(AJAX_KEY_ONERROR);
236 paramBuffer.append(':');
237 paramBuffer.append(onError);
238 onError = paramBuffer.toString();
239 }
240 else
241 {
242 onError = null;
243 }
244 String onEvent = behavior.getOnevent();
245 if (onEvent != null && !onEvent.trim().isEmpty())
246 {
247 paramBuffer.setLength(0);
248 paramBuffer.append(AJAX_KEY_ONEVENT);
249 paramBuffer.append(':');
250 paramBuffer.append(onEvent);
251 onEvent = paramBuffer.toString();
252 }
253 else
254 {
255 onEvent = null;
256 }
257
258 String delay = behavior.getDelay();
259 if (delay != null && !delay.trim().isEmpty())
260 {
261 paramBuffer.setLength(0);
262 paramBuffer.append(AJAX_KEY_DELAY);
263 paramBuffer.append(':');
264 if ("none".equals(delay))
265 {
266 paramBuffer.append('\'');
267 paramBuffer.append(delay);
268 paramBuffer.append('\'');
269 }
270 else
271 {
272 paramBuffer.append(delay);
273 }
274 delay = paramBuffer.toString();
275 }
276 else
277 {
278 delay = null;
279 }
280
281 String resetValues = Boolean.toString(behavior.isResetValues());
282 if (resetValues.equals("true"))
283 {
284 paramBuffer.setLength(0);
285 paramBuffer.append(AJAX_KEY_RESETVALUES);
286 paramBuffer.append(':');
287 paramBuffer.append(resetValues);
288 resetValues = paramBuffer.toString();
289 }
290 else
291 {
292 resetValues = null;
293 }
294
295 String sourceId = null;
296 if (context.getSourceId() == null)
297 {
298 sourceId = AJAX_VAL_THIS;
299 }
300 else
301 {
302 paramBuffer.setLength(0);
303 paramBuffer.append('\'');
304 paramBuffer.append(context.getSourceId());
305 paramBuffer.append('\'');
306 sourceId = paramBuffer.toString();
307
308 if (!context.getSourceId().trim().equals(
309 context.getComponent().getClientId(context.getFacesContext())))
310 {
311
312 UIComponent ref = context.getComponent();
313 ref = (ref.getParent() == null) ? ref : ref.getParent();
314 UIComponent instance = null;
315 try
316 {
317 instance = ref.findComponent(context.getSourceId());
318 }
319 catch (IllegalArgumentException e)
320 {
321
322 }
323 if (instance == null && executes == null)
324 {
325
326
327 executes = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_EXECUTE,
328 context.getComponent().getClientId(context.getFacesContext()), searchExpressionContext);
329 }
330 }
331 }
332
333 String event = context.getEventName();
334
335 retVal.append(JS_AJAX_REQUEST);
336 retVal.append('(');
337 retVal.append(sourceId);
338 retVal.append(",window.event,myfaces._impl._util._Lang.mixMaps(");
339
340 Collection<ClientBehaviorContext.Parameter> params = context.getParameters();
341 int paramSize = (params != null) ? params.size() : 0;
342
343 List<String> parameterList = new ArrayList<>(paramSize + 2);
344 if (executes != null)
345 {
346 parameterList.add(executes);
347 }
348 if (render != null)
349 {
350 parameterList.add(render);
351 }
352 if (onError != null)
353 {
354 parameterList.add(onError);
355 }
356 if (onEvent != null)
357 {
358 parameterList.add(onEvent);
359 }
360 if (delay != null)
361 {
362 parameterList.add(delay);
363 }
364 if (resetValues != null)
365 {
366 parameterList.add(resetValues);
367 }
368 if (paramSize > 0)
369 {
370
371
372
373
374
375
376 if (params instanceof RandomAccess)
377 {
378 List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;
379 for (int i = 0, size = list.size(); i < size; i++)
380 {
381 ClientBehaviorContext.Parameter param = list.get(i);
382 append(paramBuffer, parameterList, param.getName(), param.getValue());
383 }
384 }
385 else
386 {
387 for (ClientBehaviorContext.Parameter param : params)
388 {
389 append(paramBuffer, parameterList, param.getName(), param.getValue());
390 }
391 }
392 }
393
394 List<UIParameter> uiParams = HtmlRendererUtils.getValidUIParameterChildren(
395 facesContext, commandScript.getChildren(), false, false);
396 if (uiParams != null && uiParams.size() > 0)
397 {
398 for (int i = 0, size = uiParams.size(); i < size; i++)
399 {
400 UIParameter param = uiParams.get(i);
401 append(paramBuffer, parameterList, param.getName(), param.getValue());
402 }
403 }
404
405 paramBuffer.setLength(0);
406 paramBuffer.append('\'');
407 paramBuffer.append(ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME);
408 paramBuffer.append("\':\'");
409 paramBuffer.append(event);
410 paramBuffer.append('\'');
411 parameterList.add(paramBuffer.toString());
412
413
414
415
416
417
418 retVal.append(buildOptions(paramBuffer, parameterList));
419
420
421 retVal.append(",o,false))");
422
423 return retVal;
424 }
425
426 private void append(StringBuilder paramBuffer, List<String> parameterList, String paramName, Object paramValue)
427 {
428
429
430
431
432
433 paramBuffer.setLength(0);
434 paramBuffer.append('\'');
435 paramBuffer.append(paramName);
436 paramBuffer.append("\':\'");
437 if (paramValue != null)
438 {
439 paramBuffer.append(paramValue.toString());
440 }
441 paramBuffer.append('\'');
442 parameterList.add(paramBuffer.toString());
443 }
444
445 private StringBuilder buildOptions(StringBuilder retVal, List<String> options)
446 {
447 retVal.setLength(0);
448
449 retVal.append("{");
450
451 boolean first = true;
452
453 for (int i = 0, size = options.size(); i < size; i++)
454 {
455 String option = options.get(i);
456 if (option != null && !option.trim().isEmpty())
457 {
458 if (!first)
459 {
460 retVal.append(',');
461 }
462 else
463 {
464 first = false;
465 }
466 retVal.append(option);
467 }
468 }
469 retVal.append("}");
470 return retVal;
471 }
472
473 private String resolveExpressionsAsParameter(StringBuilder retVal, String target, String expressions,
474 SearchExpressionContext searchExpressionContext)
475 {
476 if (expressions != null && !expressions.trim().isEmpty())
477 {
478 retVal.setLength(0);
479 retVal.append(target);
480 retVal.append(':');
481 retVal.append('\'');
482
483 SearchExpressionHandler handler =
484 searchExpressionContext.getFacesContext().getApplication().getSearchExpressionHandler();
485 List<String> clientIds =
486 handler.resolveClientIds(searchExpressionContext, expressions);
487
488 if (clientIds != null && !clientIds.isEmpty())
489 {
490 for (int i = 0; i < clientIds.size(); i++)
491 {
492 if (i > 0)
493 {
494 retVal.append(' ');
495 }
496 retVal.append(clientIds.get(i));
497 }
498 }
499
500 retVal.append('\'');
501 return retVal.toString();
502 }
503
504 return null;
505 }
506 }