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