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.tobago.renderkit.html.util;
21  
22  import org.apache.myfaces.tobago.component.Attributes;
23  import org.apache.myfaces.tobago.component.SupportsMarkup;
24  import org.apache.myfaces.tobago.component.SupportsRenderedPartially;
25  import org.apache.myfaces.tobago.component.UIColumnEvent;
26  import org.apache.myfaces.tobago.component.UICommand;
27  import org.apache.myfaces.tobago.component.UIForm;
28  import org.apache.myfaces.tobago.component.UIPage;
29  import org.apache.myfaces.tobago.component.UISheet;
30  import org.apache.myfaces.tobago.context.Markup;
31  import org.apache.myfaces.tobago.context.ResourceManagerUtils;
32  import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
33  import org.apache.myfaces.tobago.internal.util.Deprecation;
34  import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
35  import org.apache.myfaces.tobago.internal.util.StringUtils;
36  import org.apache.myfaces.tobago.internal.webapp.TobagoResponseWriterWrapper;
37  import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
38  import org.apache.myfaces.tobago.renderkit.css.Classes;
39  import org.apache.myfaces.tobago.renderkit.css.Style;
40  import org.apache.myfaces.tobago.renderkit.html.Command;
41  import org.apache.myfaces.tobago.renderkit.html.CommandMap;
42  import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
43  import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
44  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
45  import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
46  import org.apache.myfaces.tobago.renderkit.util.RenderUtils;
47  import org.apache.myfaces.tobago.util.ComponentUtils;
48  import org.apache.myfaces.tobago.util.FacetUtils;
49  import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
50  import org.slf4j.Logger;
51  import org.slf4j.LoggerFactory;
52  
53  import javax.el.ELContext;
54  import javax.el.ValueExpression;
55  import javax.faces.component.UIComponent;
56  import javax.faces.component.UIInput;
57  import javax.faces.component.UINamingContainer;
58  import javax.faces.context.FacesContext;
59  import javax.faces.context.ResponseWriter;
60  import javax.faces.model.SelectItem;
61  import javax.faces.model.SelectItemGroup;
62  import java.io.IOException;
63  import java.util.ArrayList;
64  import java.util.Arrays;
65  import java.util.List;
66  import java.util.Locale;
67  import java.util.Map;
68  
69  public final class HtmlRendererUtils {
70  
71    private static final Logger LOG = LoggerFactory.getLogger(HtmlRendererUtils.class);
72    private static final String ERROR_FOCUS_KEY = HtmlRendererUtils.class.getName() + ".ErrorFocusId";
73    private static final String FOCUS_KEY = HtmlRendererUtils.class.getName() + ".FocusId";
74  
75    private HtmlRendererUtils() {
76      // to prevent instantiation
77    }
78  
79    private static boolean renderErrorFocusId(final FacesContext facesContext, final UIInput input) throws IOException {
80      if (ComponentUtils.isError(input)) {
81        if (!FacesContext.getCurrentInstance().getExternalContext().getRequestMap().containsKey(ERROR_FOCUS_KEY)) {
82          FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put(ERROR_FOCUS_KEY, Boolean.TRUE);
83          final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
84          final String id = input.getClientId(facesContext);
85          writer.writeJavascript("Tobago.errorFocusId = '" + id + "';");
86          return true;
87        } else {
88          return true;
89        }
90      }
91      return FacesContext.getCurrentInstance().getExternalContext().getRequestMap().containsKey(ERROR_FOCUS_KEY);
92    }
93  
94    public static void renderFocus(
95        final String clientId, final boolean focus, final boolean error, final FacesContext facesContext,
96        final TobagoResponseWriter writer) throws IOException {
97      final Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
98      if (!requestMap.containsKey(FOCUS_KEY)
99          && (clientId.equals(FacesContextUtils.getFocusId(facesContext)) || focus || error)) {
100       requestMap.put(FOCUS_KEY, Boolean.TRUE);
101       writer.writeAttribute(HtmlAttributes.AUTOFOCUS, true);
102     }
103   }
104 
105   /** @deprecated since 2.0.0, because of CSP */
106   public static void renderFocusId(final FacesContext facesContext, final UIComponent component)
107       throws IOException {
108     if (component instanceof UIInput) {
109       renderFocusId(facesContext, (UIInput) component);
110     }
111   }
112 
113   /** @deprecated since 2.0.0, because of CSP */
114   public static void renderFocusId(final FacesContext facesContext, final UIInput component)
115       throws IOException {
116     if (renderErrorFocusId(facesContext, component)) {
117       return;
118     }
119     if (ComponentUtils.getBooleanAttribute(component, Attributes.FOCUS)) {
120       final UIPage page = (UIPage) ComponentUtils.findPage(facesContext, component);
121       final String id = component.getClientId(facesContext);
122       if (!StringUtils.isBlank(page.getFocusId()) && !page.getFocusId().equals(id)) {
123         LOG.warn("page focusId='" + page.getFocusId() + "' ignoring new value '" + id + "'");
124       } else {
125         final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
126         writer.writeJavascript("Tobago.focusId='" + id + "';");
127       }
128     }
129   }
130 
131   /**
132    * @deprecated Since Tobago 2.0.0
133    */
134   @Deprecated
135   public static void createCssClass(final FacesContext facesContext, final UIComponent component) {
136     final String rendererName = getRendererName(facesContext, component);
137     Deprecation.LOG.error("Can't render style class for renderer " + rendererName);
138   }
139 
140   public static String getRendererName(final FacesContext facesContext, final UIComponent component) {
141     final String rendererType = component.getRendererType();
142     return rendererType.substring(0, 1).toLowerCase(Locale.ENGLISH) + rendererType.substring(1);
143   }
144 
145   public static void writeLabelWithAccessKey(final TobagoResponseWriter writer, final LabelWithAccessKey label)
146       throws IOException {
147     final int pos = label.getPos();
148     final String text = label.getLabel();
149     if (text != null) {
150       if (pos == -1) {
151         writer.writeText(text);
152       } else {
153         writer.writeText(text.substring(0, pos));
154         writer.startElement(HtmlElements.SPAN, null);
155         writer.writeClassAttribute("tobago-x-accessKey");
156         writer.writeText(Character.toString(text.charAt(pos)));
157         writer.endElement(HtmlElements.SPAN);
158         writer.writeText(text.substring(pos + 1));
159       }
160     }
161   }
162 
163   /** @deprecated since 1.5.7 and 2.0.0 */
164   @Deprecated
165   public static void setDefaultTransition(final FacesContext facesContext, final boolean transition)
166       throws IOException {
167     writeScriptLoader(facesContext, null, new String[]{"Tobago.transition = " + transition + ";"});
168   }
169 
170   /** @deprecated since 2.0.0 */
171   @Deprecated
172   public static void addClickAcceleratorKey(
173       final FacesContext facesContext, final String clientId, final char key)
174       throws IOException {
175     //addClickAcceleratorKey(facesContext, clientId, key, null);
176   }
177 
178   /** @deprecated since 2.0.0 */
179   @Deprecated
180   public static void addClickAcceleratorKey(
181       final FacesContext facesContext, final String clientId, final char key, final String modifier)
182       throws IOException {
183     //String str
184     //    = createOnclickAcceleratorKeyJsStatement(clientId, key, modifier);
185     //writeScriptLoader(facesContext, null, new String[]{str});
186   }
187 
188   /** @deprecated since 2.0.0 */
189   @Deprecated
190   public static void addAcceleratorKey(
191       final FacesContext facesContext, final String func, final char key) throws IOException {
192     //addAcceleratorKey(facesContext, func, key, null);
193   }
194 
195   /** @deprecated since 2.0.0 */
196   @Deprecated
197   public static void addAcceleratorKey(
198       final FacesContext facesContext, final String func, final char key, final String modifier)
199       throws IOException {
200     final String str = createAcceleratorKeyJsStatement(func, key, modifier);
201     writeScriptLoader(facesContext, null, new String[]{str});
202   }
203 
204   /** @deprecated since 2.0.0 */
205   @Deprecated
206   public static String createOnclickAcceleratorKeyJsStatement(
207       final String clientId, final char key, final String modifier) {
208     final String func = "Tobago.clickOnElement('" + clientId + "');";
209     return createAcceleratorKeyJsStatement(func, key, modifier);
210   }
211 
212   /** @deprecated since 2.0.0 */
213   @Deprecated
214   public static String createAcceleratorKeyJsStatement(
215       final String func, final char key, final String modifier) {
216     final StringBuilder buffer = new StringBuilder("new Tobago.AcceleratorKey(function() {");
217     buffer.append(func);
218     if (!func.endsWith(";")) {
219       buffer.append(';');
220     }
221     buffer.append("}, \"");
222     buffer.append(key);
223     if (modifier != null) {
224       buffer.append("\", \"");
225       buffer.append(modifier);
226     }
227     buffer.append("\");");
228     return buffer.toString();
229   }
230 
231   /**
232    * @deprecated since 2.0.0. Please use setter.
233    */
234   @Deprecated
235   public static void removeStyleAttribute(final UIComponent component, final String name) {
236     Deprecation.LOG.error("HtmlRendererUtils.removeStyleAttribute() no longer supported. Use setter.");
237   }
238 
239   /** @deprecated since 2.0.0 */
240   @Deprecated
241   public static void createHeaderAndBodyStyles(final FacesContext facesContext, final UIComponent component) {
242     Deprecation.LOG.error("HtmlRendererUtils.createHeaderAndBodyStyles() no longer supported");
243   }
244 
245   /** @deprecated since 2.0.0 */
246   @Deprecated
247   public static void createHeaderAndBodyStyles(
248       final FacesContext facesContext, final UIComponent component, final boolean width) {
249     Deprecation.LOG.error("HtmlRendererUtils.createHeaderAndBodyStyles() no longer supported");
250   }
251 
252   public static String createSrc(final String src, final String ext) {
253     final int dot = src.lastIndexOf('.');
254     if (dot == -1) {
255       LOG.warn("Image src without extension: '" + src + "'");
256       return src;
257     } else {
258       return src.substring(0, dot) + ext + src.substring(dot);
259     }
260   }
261 
262   public static TobagoResponseWriter getTobagoResponseWriter(final FacesContext facesContext) {
263 
264     final ResponseWriter writer = facesContext.getResponseWriter();
265     if (writer instanceof TobagoResponseWriter) {
266       return (TobagoResponseWriter) writer;
267     } else {
268       return new TobagoResponseWriterWrapper(writer);
269     }
270   }
271 
272   /**
273    * @deprecated Since Tobago 2.0.0. Because of CSP.
274    */
275   @Deprecated
276   public static void writeScriptLoader(final FacesContext facesContext, final String script)
277       throws IOException {
278     writeScriptLoader(facesContext, new String[]{script}, null);
279   }
280 
281   /**
282    * @deprecated Since Tobago 2.0.0. Because of CSP.
283    */
284   @Deprecated
285   public static void writeScriptLoader(
286       final FacesContext facesContext, final String[] scripts, final String[] afterLoadCmds)
287       throws IOException {
288     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
289     if (scripts != null) {
290       LOG.error("Scripts argument for writeScriptLoader not supported anymore!");
291     }
292     String allScripts = "[]";
293     if (scripts != null) {
294       allScripts = ResourceManagerUtils.getScriptsAsJSArray(facesContext, scripts);
295     }
296     final boolean ajax = FacesContextUtils.isAjax(facesContext);
297     writer.startJavascript();
298     // XXX fix me if scripts != null
299     if (ajax || scripts != null) {
300       writer.write("new Tobago.ScriptLoader(");
301       if (!ajax) {
302         writer.write("\n    ");
303       }
304       writer.write(allScripts);
305 
306       if (afterLoadCmds != null && afterLoadCmds.length > 0) {
307         writer.write(", ");
308         if (!ajax) {
309           writer.write("\n");
310         }
311         boolean first = true;
312         for (final String afterLoadCmd : afterLoadCmds) {
313           final String[] splittedStrings = StringUtils.split(afterLoadCmd, '\n'); // split on <CR> to have nicer JS
314           for (final String splitted : splittedStrings) {
315             writer.write(first ? "          " : "        + ");
316             writer.write("\"");
317             String cmd = StringUtils.replace(splitted, "\\", "\\\\");
318             cmd = StringUtils.replace(cmd, "\"", "\\\"");
319             writer.write(cmd);
320             writer.write("\"");
321             if (!ajax) {
322               writer.write("\n");
323             }
324             first = false;
325           }
326         }
327       }
328       writer.write(");");
329     } else {
330     for (final String afterLoadCmd : afterLoadCmds) {
331       writer.write(afterLoadCmd);
332     }
333     }
334     writer.endJavascript();
335   }
336 
337   /**
338    * @deprecated Since Tobago 2.0.0. Because of CSP.
339    */
340   @Deprecated
341   public static void writeStyleLoader(
342       final FacesContext facesContext, final String[] styles) throws IOException {
343     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
344 
345     writer.startJavascript();
346     writer.write("Tobago.ensureStyleFiles(");
347     writer.write(ResourceManagerUtils.getStylesAsJSArray(facesContext, styles));
348     writer.write(");");
349     writer.endJavascript();
350   }
351 
352   public static String getTitleFromTipAndMessages(final FacesContext facesContext, final UIComponent component) {
353     final String messages = ComponentUtils.getFacesMessageAsString(facesContext, component);
354     return HtmlRendererUtils.addTip(messages, component.getAttributes().get(Attributes.TIP));
355   }
356 
357   public static String addTip(String title, final Object tip) {
358     if (tip != null) {
359       if (title != null && title.length() > 0) {
360         title += " :: ";
361       } else {
362         title = "";
363       }
364       title += tip;
365     }
366     return title;
367   }
368 
369   public static void renderSelectItems(final UIInput component, final Iterable<SelectItem> items, final Object[] values,
370       final TobagoResponseWriter writer, final FacesContext facesContext) throws IOException {
371     renderSelectItems(component, items, values, null, writer, facesContext);
372   }
373 
374   public static void renderSelectItems(
375       final UIInput component, final Iterable<SelectItem> items, final Object[] values, final Boolean onlySelected,
376       final TobagoResponseWriter writer, final FacesContext facesContext) throws IOException {
377 
378     if (LOG.isDebugEnabled()) {
379       LOG.debug("value = '" + Arrays.toString(values) + "'");
380     }
381     for (final SelectItem item : items) {
382       if (item instanceof SelectItemGroup) {
383         writer.startElement(HtmlElements.OPTGROUP, null);
384         writer.writeAttribute(HtmlAttributes.LABEL, item.getLabel(), true);
385         if (item.isDisabled()) {
386           writer.writeAttribute(HtmlAttributes.DISABLED, true);
387         }
388         final SelectItem[] selectItems = ((SelectItemGroup) item).getSelectItems();
389         renderSelectItems(component, Arrays.asList(selectItems), values, onlySelected, writer, facesContext);
390         writer.endElement(HtmlElements.OPTGROUP);
391       } else {
392 
393         Object itemValue = item.getValue();
394         // when using selectItem tag with a literal value: use the converted value
395         if (itemValue instanceof String && values != null && values.length > 0 && !(values[0] instanceof String)) {
396           itemValue = ComponentUtils.getConvertedValue(facesContext, component, (String) itemValue);
397         }
398         final boolean contains = RenderUtils.contains(values, itemValue);
399         if (onlySelected != null) {
400           if (onlySelected) {
401             if (!contains) {
402               continue;
403             }
404           } else {
405             if (contains) {
406               continue;
407             }
408           }
409         }
410         writer.startElement(HtmlElements.OPTION, null);
411         final String formattedValue = RenderUtils.getFormattedValue(facesContext, component, itemValue);
412         writer.writeAttribute(HtmlAttributes.VALUE, formattedValue, true);
413         if (item instanceof org.apache.myfaces.tobago.model.SelectItem) {
414           final String image = ((org.apache.myfaces.tobago.model.SelectItem) item).getImage();
415           if (image != null) {
416             final Style style = new Style();
417             style.setBackgroundImage("url('"
418                 + ResourceManagerUtils.getImageOrDisabledImageWithPath(facesContext, image, item.isDisabled())
419                 + "')");
420             writer.writeStyleAttribute(style);
421           }
422         }
423         Markup markup = item instanceof SupportsMarkup ? ((SupportsMarkup) item).getMarkup() : Markup.NULL;
424         if (onlySelected == null && contains) {
425           writer.writeAttribute(HtmlAttributes.SELECTED, true);
426           markup = Markup.SELECTED.add(markup);
427         }
428         if (item.isDisabled()) {
429           writer.writeAttribute(HtmlAttributes.DISABLED, true);
430           markup = Markup.DISABLED.add(markup);
431         }
432         writer.writeClassAttribute(Classes.create(component, "option", markup));
433 
434         writer.writeText(item.getLabel());
435         writer.endElement(HtmlElements.OPTION);
436       }
437     }
438   }
439 
440   public static String getComponentIds(
441       final FacesContext context, final UIComponent component, final String[] componentId) {
442     final StringBuilder sb = new StringBuilder();
443     for (final String id : componentId) {
444       if (!StringUtils.isBlank(id)) {
445         if (sb.length() > 0) {
446           sb.append(",");
447         }
448         final String clientId = getComponentId(context, component, id);
449         if (clientId != null) {
450           sb.append(clientId);
451         }
452       }
453     }
454     return sb.toString();
455   }
456 
457   public static String[] getComponentIdsAsList(
458       final FacesContext context, final UIComponent component, final String[] componentId) {
459     final List<String> result = new ArrayList<String>(componentId.length);
460     for (final String id : componentId) {
461       if (!StringUtils.isBlank(id)) {
462         final String clientId = getComponentId(context, component, id);
463         if (clientId != null) {
464           result.add(clientId);
465         }
466       }
467     }
468     return (String[]) result.toArray(new String[result.size()]);
469   }
470 
471   public static String getComponentId(
472       final FacesContext context, final UIComponent component, final String componentId) {
473     final UIComponent partiallyComponent = ComponentUtils.findComponent(component, componentId);
474     if (partiallyComponent != null) {
475       final String clientId = partiallyComponent.getClientId(context);
476       if (partiallyComponent instanceof UISheet) {
477         final int rowIndex = ((UISheet) partiallyComponent).getRowIndex();
478         if (rowIndex >= 0 && clientId.endsWith(Integer.toString(rowIndex))) {
479           return clientId.substring(0, clientId.lastIndexOf(UINamingContainer.getSeparatorChar(context)));
480         }
481       }
482       return clientId;
483     }
484     LOG.error("No component found for id='" + componentId + "', "
485         + "search base component is '" + component.getClientId(context) + "'");
486     return null;
487   }
488 
489   /**
490    * @deprecated since Tobago 1.5.0.
491    */
492   @Deprecated
493   public static String toStyleString(final String key, final Integer value) {
494     final StringBuilder buf = new StringBuilder();
495     buf.append(key);
496     buf.append(":");
497     buf.append(value);
498     buf.append("px; ");
499     return buf.toString();
500   }
501 
502   /**
503    * @deprecated since Tobago 1.5.0.
504    */
505   @Deprecated
506   public static String toStyleString(final String key, final String value) {
507     final StringBuilder buf = new StringBuilder();
508     buf.append(key);
509     buf.append(":");
510     buf.append(value);
511     buf.append("; ");
512     return buf.toString();
513   }
514 
515   /**
516    * @deprecated since Tobago 1.5.0. Please use getTitleFromTipAndMessages and write it out.
517    */
518   @Deprecated
519   public static void renderTip(final UIComponent component, final TobagoResponseWriter writer) throws IOException {
520     final Object objTip = component.getAttributes().get(Attributes.TIP);
521     if (objTip != null) {
522       final String tip = String.valueOf(objTip);
523       writer.writeAttribute(HtmlAttributes.TITLE, tip, true);
524     }
525   }
526 
527   /**
528    * @deprecated since Tobago 1.5.0. Please use getTitleFromTipAndMessages and write it out.
529    */
530   public static void renderImageTip(final UIComponent component, final TobagoResponseWriter writer) throws IOException {
531     final Object objTip = component.getAttributes().get(Attributes.TIP);
532     if (objTip != null) {
533       final String tip = String.valueOf(objTip);
534       writer.writeAttribute(HtmlAttributes.ALT, tip, true);
535     } else {
536       writer.writeAttribute(HtmlAttributes.ALT, "", false);
537     }
538   }
539 
540   public static String getJavascriptString(final String str) {
541     if (str != null) {
542       return "\"" + str + "\"";
543     }
544     return null;
545   }
546 
547   public static String getRenderedPartiallyJavascriptArray(final FacesContext facesContext, final UICommand command) {
548     if (command == null) {
549       return null;
550     }
551     return getRenderedPartiallyJavascriptArray(facesContext, command, command);
552   }
553 
554   public static String getRenderedPartiallyJavascriptArray(
555       final FacesContext facesContext, final UIComponent searchBase,
556       final SupportsRenderedPartially supportsRenderedPartially) {
557     final String[] list = supportsRenderedPartially.getRenderedPartially();
558     if (list == null || list.length == 0) {
559       return null;
560     }
561     final StringBuilder strBuilder = new StringBuilder();
562     strBuilder.append("[");
563     for (int i = 0; i < list.length; i++) {
564       if (i != 0) {
565         strBuilder.append(",");
566       }
567       strBuilder.append("\"");
568       strBuilder.append(HtmlRendererUtils.getComponentId(facesContext, searchBase, list[i]));
569       strBuilder.append("\"");
570     }
571     strBuilder.append("]");
572     return strBuilder.toString();
573   }
574 
575   public static String getJavascriptArray(final String[] list) {
576     final StringBuilder strBuilder = new StringBuilder();
577     strBuilder.append("[");
578     for (int i = 0; i < list.length; i++) {
579       if (i != 0) {
580         strBuilder.append(",");
581       }
582       strBuilder.append("\"");
583       strBuilder.append(list[i]);
584       strBuilder.append("\"");
585     }
586     strBuilder.append("]");
587     return strBuilder.toString();
588   }
589 
590   /**
591    * will be removed in later versions
592    * @deprecated since 2.0.0
593    */
594   @Deprecated
595   public static void renderDojoDndSource(final FacesContext context, final UIComponent component)
596       throws IOException {
597     final Object objDojoType = component.getAttributes().get("dojoType");
598     if (null != objDojoType && (objDojoType.equals("dojo.dnd.Source") || objDojoType.equals("dojo.dnd.Target"))) {
599       FacesContextUtils.addOnloadScript(context, createDojoDndType(component,
600           component.getClientId(context), String.valueOf(objDojoType)));
601     }
602   }
603 
604   /**
605    * will be removed in later versions
606    * @deprecated since 2.0.0
607    */
608   @Deprecated
609   public static void renderDojoDndItem(
610       final UIComponent component, final TobagoResponseWriter writer, final boolean addStyle)
611       throws IOException {
612     final Object objDndType = component.getAttributes().get("dndType");
613     if (objDndType != null) {
614       writer.writeAttribute("dndType", String.valueOf(objDndType), false);
615     }
616     final Object objDndData = component.getAttributes().get("dndData");
617     if (objDndData != null) {
618       writer.writeAttribute("dndData", String.valueOf(objDndData), false);
619     }
620   }
621 
622   /**
623    * will be removed in later versions
624    * @deprecated since 2.0.0
625    */
626   @Deprecated
627   public static String createDojoDndType(final UIComponent component, final String clientId, final String dojoType) {
628     final StringBuilder strBuilder = new StringBuilder();
629     strBuilder.append("new ").append(dojoType).append("('").append(clientId).append("'");
630     final StringBuilder parameter = new StringBuilder();
631 
632     final Object objHorizontal = component.getAttributes().get("horizontal");
633     if (objHorizontal != null) {
634       parameter.append("horizontal: ").append(String.valueOf(objHorizontal)).append(",");
635     }
636     final Object objCopyOnly = component.getAttributes().get("copyOnly");
637     if (objCopyOnly != null) {
638       parameter.append("copyOnly: ").append(String.valueOf(objCopyOnly)).append(",");
639     }
640     final Object objSkipForm = component.getAttributes().get("skipForm");
641     if (objSkipForm != null) {
642       parameter.append("skipForm: ").append(String.valueOf(objSkipForm)).append(",");
643     }
644     final Object objWithHandles = component.getAttributes().get("withHandles");
645     if (objWithHandles != null) {
646       parameter.append("withHandles: ").append(String.valueOf(objWithHandles)).append(",");
647     }
648     final Object objAccept = component.getAttributes().get("accept");
649     if (objAccept != null) {
650       String accept = null;
651       if (objAccept instanceof String[]) {
652         final String[] allowed = (String[]) objAccept;
653         if (allowed.length > 1) {
654           // TODO replace this
655           accept = "'" + allowed[0] + "'";
656           for (int i = 1; i < allowed.length; i++) {
657             accept += ",'" + allowed[i] + "'";
658           }
659         }
660       } else {
661         accept = (String) objAccept;
662       }
663       parameter.append("accept: [").append(accept).append("],");
664     }
665     final Object objSingular = component.getAttributes().get("singular");
666     if (objSingular != null) {
667       parameter.append("singular: ").append(String.valueOf(objSingular)).append(",");
668     }
669     final Object objCreator = component.getAttributes().get("creator");
670     if (objCreator != null) {
671       parameter.append("creator: ").append(String.valueOf(objCreator)).append(",");
672     }
673     if (parameter.length() > 0) {
674       parameter.deleteCharAt(parameter.lastIndexOf(","));
675       strBuilder.append(",{").append(parameter).append("}");
676     }
677     strBuilder.append(");");
678     return strBuilder.toString();
679   }
680 
681   public static void renderCommandFacet(final UIComponent component, final FacesContext facesContext,
682       final TobagoResponseWriter writer) throws IOException {
683     renderCommandFacet(component, component.getClientId(facesContext), facesContext, writer);
684   }
685 
686   public static void renderCommandFacet(
687       final UIComponent component, final String id, final FacesContext facesContext, final TobagoResponseWriter writer)
688       throws IOException {
689     if (ComponentUtils.getBooleanAttribute(component, Attributes.READONLY)
690         || ComponentUtils.getBooleanAttribute(component, Attributes.DISABLED)) {
691       return;
692     }
693     CommandMap commandMap = null;
694     final Map<String, UIComponent> facets = component.getFacets();
695     for (final Map.Entry<String, UIComponent> entry : facets.entrySet()) {
696       final UIComponent facetComponent = entry.getValue();
697       if (facetComponent.isRendered()
698           && (facetComponent instanceof AbstractUICommand || facetComponent instanceof UIForm)) {
699         if (commandMap == null) {
700           commandMap = new CommandMap();
701         }
702         final String key = entry.getKey();
703         commandMap.addCommand(key, new Command(facesContext, entry.getValue(), id));
704       }
705     }
706     if (commandMap != null) {
707       writer.writeAttribute(DataAttributes.COMMANDS, JsonUtils.encode(commandMap), true);
708     }
709   }
710 
711   public static boolean renderSheetCommands(final UISheet sheet, final FacesContext facesContext,
712                                          final TobagoResponseWriter writer) throws IOException {
713     CommandMap commandMap = null;
714     for (final UIComponent child : sheet.getChildren()) {
715       if (child instanceof UIColumnEvent) {
716         final UIColumnEvent columnEvent = (UIColumnEvent) child;
717         if (columnEvent.isRendered()) {
718           final UIComponent selectionChild = child.getChildren().get(0);
719           if (selectionChild != null && selectionChild instanceof AbstractUICommand && selectionChild.isRendered()) {
720             final UICommand action = (UICommand) selectionChild;
721             if (commandMap == null) {
722               commandMap = new CommandMap();
723             }
724             commandMap.addCommand(columnEvent.getEvent(), new Command(facesContext, action, null));
725           }
726         }
727       }
728     }
729     if (commandMap != null) {
730       writer.writeAttribute(DataAttributes.ROW_ACTION, JsonUtils.encode(commandMap), true);
731       return true;
732     }
733     return false;
734   }
735 
736 
737   public static void checkForCommandFacet(
738       final UIComponent component, final FacesContext facesContext, final TobagoResponseWriter writer)
739       throws IOException {
740     checkForCommandFacet(component, Arrays.asList(component.getClientId(facesContext)), facesContext, writer);
741   }
742 
743   public static void checkForCommandFacet(
744       final UIComponent component, final List<String> clientIds, final FacesContext facesContext,
745       final TobagoResponseWriter writer) throws IOException {
746     if (ComponentUtils.getBooleanAttribute(component, Attributes.READONLY)
747         || ComponentUtils.getBooleanAttribute(component, Attributes.DISABLED)) {
748       return;
749     }
750     final Map<String, UIComponent> facets = component.getFacets();
751     for (final Map.Entry<String, UIComponent> entry : facets.entrySet()) {
752       if (entry.getValue() instanceof UICommand) {
753         addCommandFacet(clientIds, entry, facesContext, writer);
754       }
755     }
756   }
757 
758   private static void addCommandFacet(
759       final List<String> clientIds, final Map.Entry<String, UIComponent> facetEntry,
760       final FacesContext facesContext, final TobagoResponseWriter writer)
761       throws IOException {
762     for (final String clientId : clientIds) {
763       writeScriptForClientId(clientId, facetEntry, facesContext, writer);
764     }
765   }
766 
767   /**
768    * @deprecated Since Tobago 2.0.0. Because of CSP.
769    */
770   @Deprecated
771   private static void writeScriptForClientId(
772       final String clientId, final Map.Entry<String, UIComponent> facetEntry,
773       final FacesContext facesContext, final TobagoResponseWriter writer) throws IOException {
774     if (facetEntry.getValue() instanceof UICommand
775         && ((UICommand) facetEntry.getValue()).getRenderedPartially().length > 0) {
776       writer.startJavascript();
777       writer.write("var element = Tobago.element(\"");
778       writer.write(clientId);
779       writer.write("\");\n");
780       writer.write("if (element) {\n");
781       writer.write("   Tobago.addEventListener(element, \"");
782       writer.write(facetEntry.getKey());
783       writer.write("\", function(){Tobago.reloadComponent(this, '");
784       writer.write(HtmlRendererUtils.getComponentIds(facesContext, facetEntry.getValue(),
785               ((UICommand) facetEntry.getValue()).getRenderedPartially()));
786       writer.write("','");
787       writer.write(facetEntry.getValue().getClientId(facesContext)); 
788       writer.write("', {})});\n");
789       writer.write("};");
790       writer.endJavascript();
791     } else {
792       final UIComponent facetComponent = facetEntry.getValue();
793 
794       writer.startJavascript();
795       writer.write("var element = Tobago.element(\"");
796       writer.write(clientId + "\");\n");
797       writer.write("if (element) {\n");
798       writer.write("   Tobago.addEventListener(element, \"");
799       writer.write(facetEntry.getKey());
800       writer.write("\", function(){");
801       String facetAction = (String) facetComponent.getAttributes().get(Attributes.ONCLICK);
802       if (facetAction != null) {
803          // Replace @autoId
804         facetAction = StringUtils.replace(facetAction, "@autoId", facetComponent.getClientId(facesContext));
805         writer.write(facetAction);
806       } else {
807         writer.write(createSubmitAction(
808             facetComponent.getClientId(facesContext),
809             ComponentUtils.getBooleanAttribute(facetComponent, Attributes.TRANSITION),
810             null,
811             clientId));
812       }
813       writer.write("});\n};");
814       writer.endJavascript();
815     }
816   }
817 
818   /**
819    * @deprecated since 2.0.0. JavaScript should not be rendered in the page. See CSP.
820    */
821   @Deprecated
822   public static String createSubmitAction(
823       final String clientId, final boolean transition, final String target, final String focus) {
824     final StringBuilder builder = new StringBuilder();
825     builder.append("Tobago.submitAction(this,'");
826     builder.append(clientId);
827     builder.append("',{");
828     if (!transition) { // transition == true is the default
829       builder.append("transition:false");
830       if (target != null || focus != null) {
831         builder.append(',');
832       }
833     }
834     if (target != null) {
835       builder.append("target:'");
836       builder.append(target);
837       builder.append('\'');
838       if (focus != null) {
839         builder.append(',');
840       }
841     }
842     if (focus != null) {
843       builder.append("focus:'");
844       builder.append(focus);
845       builder.append('\'');
846     }
847     builder.append("});");
848     return builder.toString();
849   }
850 
851   /**
852    * @deprecated since Tobago 1.5.0. Please use {@link org.apache.myfaces.tobago.renderkit.css.Classes}.
853    */
854   @Deprecated
855   public static void removeStyleClasses(final UIComponent cell) {
856     Deprecation.LOG.warn("cell = '" + cell + "'");
857   }
858 
859   public static void encodeContextMenu(
860       final FacesContext facesContext, final TobagoResponseWriter writer, final UIComponent parent)
861       throws IOException {
862     final UIComponent contextMenu = FacetUtils.getContextMenu(parent);
863     if (contextMenu != null) {
864       writer.startElement(HtmlElements.OL, contextMenu);
865       writer.writeClassAttribute("tobago-menuBar tobago-menu-contextMenu");
866       RenderUtils.encode(facesContext, contextMenu);
867       writer.endElement(HtmlElements.OL);
868     }
869   }
870 
871   public static void addAcceleratorKey(
872       final FacesContext facesContext, final UIComponent component, final Character accessKey) {
873     final String clientId = component.getClientId(facesContext);
874     final String jsStatement = createOnclickAcceleratorKeyJsStatement(clientId, accessKey, null);
875     FacesContextUtils.addMenuAcceleratorScript(facesContext, jsStatement);
876   }
877 
878   public static void writeDataAttributes(
879       final FacesContext context, final TobagoResponseWriter writer, final UIComponent component)
880       throws IOException {
881 
882     final Map<Object, Object> dataAttributes = ComponentUtils.getDataAttributes(component);
883     if (dataAttributes == null) {
884       return;
885     }
886 
887     final ELContext elContext = context.getELContext();
888 
889     for (final Map.Entry<Object, Object> entry : dataAttributes.entrySet()) {
890       final Object mapKey = entry.getKey();
891       final String name = mapKey instanceof ValueExpression
892           ? ((ValueExpression) mapKey).getValue(elContext).toString() : mapKey.toString();
893       final Object mapValue = entry.getValue();
894       final String value = mapValue instanceof ValueExpression
895           ? ((ValueExpression) mapValue).getValue(elContext).toString() : mapValue.toString();
896       writer.writeAttribute("data-" + name, value, true);
897     }
898   }
899 }