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   /**
371    * @deprecated Since Tobago 2.0.7
372    */
373   @Deprecated
374   public static void renderSelectItems(final UIInput component, final Iterable<SelectItem> items, final Object[] values,
375       final TobagoResponseWriter writer, final FacesContext facesContext) throws IOException {
376     renderSelectItems(component, items, values, null, null, writer, facesContext);
377   }
378 
379   public static void renderSelectItems(final UIInput component, final Iterable<SelectItem> items, final Object[] values,
380       final String[] submittedValues, final TobagoResponseWriter writer, final FacesContext facesContext)
381       throws IOException {
382     renderSelectItems(component, items, values, submittedValues, null, writer, facesContext);
383   }
384 
385   public static void renderSelectItems(final UIInput component, final Iterable<SelectItem> items, final Object value,
386       final String submittedValue, final TobagoResponseWriter writer, final FacesContext facesContext)
387       throws IOException {
388     renderSelectItems(component, items, value != null ? new Object[] {value}: null,
389         submittedValue != null ?  new String[] {submittedValue}: null, null, writer, facesContext);
390   }
391 
392   /**
393    * @deprecated Since Tobago 2.0.7
394    */
395   @Deprecated
396   public static void renderSelectItems(final UIInput component, final Iterable<SelectItem> items, final Object[] values,
397       final Boolean onlySelected, final TobagoResponseWriter writer, final FacesContext facesContext)
398       throws IOException {
399     renderSelectItems(component, items, values, null, onlySelected, writer, facesContext);
400   }
401   public static void renderSelectItems(final UIInput component, final Iterable<SelectItem> items, final Object[] values,
402       final String[] submittedValues, final Boolean onlySelected, final TobagoResponseWriter writer,
403       final FacesContext facesContext) throws IOException {
404 
405     if (LOG.isDebugEnabled()) {
406       LOG.debug("component id = '{}'", component.getId());
407       LOG.debug("values = '{}'", Arrays.toString(values));
408       LOG.debug("submittedValues = '{}'", Arrays.toString(submittedValues));
409     }
410     for (final SelectItem item : items) {
411       if (item instanceof SelectItemGroup) {
412         writer.startElement(HtmlElements.OPTGROUP, null);
413         writer.writeAttribute(HtmlAttributes.LABEL, item.getLabel(), true);
414         if (item.isDisabled()) {
415           writer.writeAttribute(HtmlAttributes.DISABLED, true);
416         }
417         final SelectItem[] selectItems = ((SelectItemGroup) item).getSelectItems();
418         renderSelectItems(component, Arrays.asList(selectItems), values, submittedValues,
419             onlySelected, writer, facesContext);
420         writer.endElement(HtmlElements.OPTGROUP);
421       } else {
422 
423         Object itemValue = item.getValue();
424         // when using selectItem tag with a literal value: use the converted value
425         if (itemValue instanceof String && values != null && values.length > 0 && !(values[0] instanceof String)) {
426           itemValue = ComponentUtils.getConvertedValue(facesContext, component, (String) itemValue);
427         }
428         final String formattedValue = RenderUtils.getFormattedValue(facesContext, component, itemValue);
429         boolean contains;
430         if (submittedValues == null) {
431           contains = RenderUtils.contains(values, itemValue);
432         } else {
433           contains = RenderUtils.contains(submittedValues, formattedValue);
434         }
435         if (onlySelected != null) {
436           if (onlySelected) {
437             if (!contains) {
438               continue;
439             }
440           } else {
441             if (contains) {
442               continue;
443             }
444           }
445         }
446         writer.startElement(HtmlElements.OPTION, null);
447         writer.writeAttribute(HtmlAttributes.VALUE, formattedValue, true);
448         if (item instanceof org.apache.myfaces.tobago.model.SelectItem) {
449           final String image = ((org.apache.myfaces.tobago.model.SelectItem) item).getImage();
450           if (image != null) {
451             final Style style = new Style();
452             style.setBackgroundImage("url('"
453                 + ResourceManagerUtils.getImageOrDisabledImageWithPath(facesContext, image, item.isDisabled())
454                 + "')");
455             writer.writeStyleAttribute(style);
456           }
457         }
458         Markup markup = item instanceof SupportsMarkup ? ((SupportsMarkup) item).getMarkup() : Markup.NULL;
459         if (onlySelected == null && contains) {
460           writer.writeAttribute(HtmlAttributes.SELECTED, true);
461           markup = Markup.SELECTED.add(markup);
462         }
463         if (item.isDisabled()) {
464           writer.writeAttribute(HtmlAttributes.DISABLED, true);
465           markup = Markup.DISABLED.add(markup);
466         }
467         writer.writeClassAttribute(Classes.create(component, "option", markup));
468 
469         writer.writeText(item.getLabel());
470         writer.endElement(HtmlElements.OPTION);
471       }
472     }
473   }
474 
475   /**
476    * @deprecated Since Tobago 2.0.3.
477    */
478   @Deprecated
479   public static String getComponentIds(
480       final FacesContext context, final UIComponent component, final String[] componentId) {
481     final StringBuilder sb = new StringBuilder();
482     for (final String id : componentId) {
483       if (!StringUtils.isBlank(id)) {
484         if (sb.length() > 0) {
485           sb.append(",");
486         }
487         final String clientId = ComponentUtils.evaluateClientId(context, component, id);
488         if (clientId != null) {
489           sb.append(clientId);
490         }
491       }
492     }
493     return sb.toString();
494   }
495 
496   /**
497    * @deprecated Since 2.0.3, please use {@link org.apache.myfaces.tobago.util.ComponentUtils#evaluateClientIds(
498    * javax.faces.context.FacesContext, javax.faces.component.UIComponent, String[])}
499    */
500   public static String[] getComponentIdsAsList(
501       final FacesContext context, final UIComponent component, final String[] componentIds) {
502     return ComponentUtils.evaluateClientIds(context, component, componentIds);
503   }
504 
505   /**
506    * @deprecated Since 2.0.3, please use {@link org.apache.myfaces.tobago.util.ComponentUtils#evaluateClientId(
507    * javax.faces.context.FacesContext, javax.faces.component.UIComponent, String)}
508    */
509   public static String getComponentId(
510       final FacesContext context, final UIComponent component, final String componentId) {
511     return ComponentUtils.evaluateClientId(context, component, componentId);
512   }
513 
514   /**
515    * @deprecated since Tobago 1.5.0.
516    */
517   @Deprecated
518   public static String toStyleString(final String key, final Integer value) {
519     final StringBuilder buf = new StringBuilder();
520     buf.append(key);
521     buf.append(":");
522     buf.append(value);
523     buf.append("px; ");
524     return buf.toString();
525   }
526 
527   /**
528    * @deprecated since Tobago 1.5.0.
529    */
530   @Deprecated
531   public static String toStyleString(final String key, final String value) {
532     final StringBuilder buf = new StringBuilder();
533     buf.append(key);
534     buf.append(":");
535     buf.append(value);
536     buf.append("; ");
537     return buf.toString();
538   }
539 
540   /**
541    * @deprecated since Tobago 1.5.0. Please use getTitleFromTipAndMessages and write it out.
542    */
543   @Deprecated
544   public static void renderTip(final UIComponent component, final TobagoResponseWriter writer) throws IOException {
545     final Object objTip = component.getAttributes().get(Attributes.TIP);
546     if (objTip != null) {
547       final String tip = String.valueOf(objTip);
548       writer.writeAttribute(HtmlAttributes.TITLE, tip, true);
549     }
550   }
551 
552   /**
553    * @deprecated since Tobago 1.5.0. Please use getTitleFromTipAndMessages and write it out.
554    */
555   public static void renderImageTip(final UIComponent component, final TobagoResponseWriter writer) throws IOException {
556     final Object objTip = component.getAttributes().get(Attributes.TIP);
557     if (objTip != null) {
558       final String tip = String.valueOf(objTip);
559       writer.writeAttribute(HtmlAttributes.ALT, tip, true);
560     } else {
561       writer.writeAttribute(HtmlAttributes.ALT, "", false);
562     }
563   }
564 
565   /**
566    * @deprecated Since Tobago 2.0.3, because of CSP.
567    */
568   @Deprecated
569   public static String getJavascriptString(final String str) {
570     if (str != null) {
571       return "\"" + str + "\"";
572     }
573     return null;
574   }
575 
576   /**
577    * @deprecated Since Tobago 2.0.3, because of CSP.
578    */
579   @Deprecated
580   public static String getRenderedPartiallyJavascriptArray(final FacesContext facesContext, final UICommand command) {
581     if (command == null) {
582       return null;
583     }
584     return getRenderedPartiallyJavascriptArray(facesContext, command, command);
585   }
586 
587   /**
588    * @deprecated Since Tobago 2.0.3, because of CSP.
589    */
590   @Deprecated
591   public static String getRenderedPartiallyJavascriptArray(
592       final FacesContext facesContext, final UIComponent searchBase,
593       final SupportsRenderedPartially supportsRenderedPartially) {
594     final String[] list = supportsRenderedPartially.getRenderedPartially();
595     if (list == null || list.length == 0) {
596       return null;
597     }
598     final StringBuilder strBuilder = new StringBuilder();
599     strBuilder.append("[");
600     for (int i = 0; i < list.length; i++) {
601       if (i != 0) {
602         strBuilder.append(",");
603       }
604       strBuilder.append("\"");
605       strBuilder.append(ComponentUtils.evaluateClientId(facesContext, searchBase, list[i]));
606       strBuilder.append("\"");
607     }
608     strBuilder.append("]");
609     return strBuilder.toString();
610   }
611 
612   /**
613    * @deprecated Since Tobago 2.0.3, because of CSP.
614    */
615   @Deprecated
616   public static String getJavascriptArray(final String[] list) {
617     final StringBuilder strBuilder = new StringBuilder();
618     strBuilder.append("[");
619     for (int i = 0; i < list.length; i++) {
620       if (i != 0) {
621         strBuilder.append(",");
622       }
623       strBuilder.append("\"");
624       strBuilder.append(list[i]);
625       strBuilder.append("\"");
626     }
627     strBuilder.append("]");
628     return strBuilder.toString();
629   }
630 
631   /**
632    * will be removed in later versions
633    * @deprecated since 2.0.0
634    */
635   @Deprecated
636   public static void renderDojoDndSource(final FacesContext context, final UIComponent component)
637       throws IOException {
638     final Object objDojoType = component.getAttributes().get("dojoType");
639     if (null != objDojoType && (objDojoType.equals("dojo.dnd.Source") || objDojoType.equals("dojo.dnd.Target"))) {
640       FacesContextUtils.addOnloadScript(context, createDojoDndType(component,
641           component.getClientId(context), String.valueOf(objDojoType)));
642     }
643   }
644 
645   /**
646    * will be removed in later versions
647    * @deprecated since 2.0.0
648    */
649   @Deprecated
650   public static void renderDojoDndItem(
651       final UIComponent component, final TobagoResponseWriter writer, final boolean addStyle)
652       throws IOException {
653     final Object objDndType = component.getAttributes().get("dndType");
654     if (objDndType != null) {
655       writer.writeAttribute("dndType", String.valueOf(objDndType), false);
656     }
657     final Object objDndData = component.getAttributes().get("dndData");
658     if (objDndData != null) {
659       writer.writeAttribute("dndData", String.valueOf(objDndData), false);
660     }
661   }
662 
663   /**
664    * will be removed in later versions
665    * @deprecated since 2.0.0
666    */
667   @Deprecated
668   public static String createDojoDndType(final UIComponent component, final String clientId, final String dojoType) {
669     final StringBuilder strBuilder = new StringBuilder();
670     strBuilder.append("new ").append(dojoType).append("('").append(clientId).append("'");
671     final StringBuilder parameter = new StringBuilder();
672 
673     final Object objHorizontal = component.getAttributes().get("horizontal");
674     if (objHorizontal != null) {
675       parameter.append("horizontal: ").append(String.valueOf(objHorizontal)).append(",");
676     }
677     final Object objCopyOnly = component.getAttributes().get("copyOnly");
678     if (objCopyOnly != null) {
679       parameter.append("copyOnly: ").append(String.valueOf(objCopyOnly)).append(",");
680     }
681     final Object objSkipForm = component.getAttributes().get("skipForm");
682     if (objSkipForm != null) {
683       parameter.append("skipForm: ").append(String.valueOf(objSkipForm)).append(",");
684     }
685     final Object objWithHandles = component.getAttributes().get("withHandles");
686     if (objWithHandles != null) {
687       parameter.append("withHandles: ").append(String.valueOf(objWithHandles)).append(",");
688     }
689     final Object objAccept = component.getAttributes().get("accept");
690     if (objAccept != null) {
691       String accept = null;
692       if (objAccept instanceof String[]) {
693         final String[] allowed = (String[]) objAccept;
694         if (allowed.length > 1) {
695           // TODO replace this
696           accept = "'" + allowed[0] + "'";
697           for (int i = 1; i < allowed.length; i++) {
698             accept += ",'" + allowed[i] + "'";
699           }
700         }
701       } else {
702         accept = (String) objAccept;
703       }
704       parameter.append("accept: [").append(accept).append("],");
705     }
706     final Object objSingular = component.getAttributes().get("singular");
707     if (objSingular != null) {
708       parameter.append("singular: ").append(String.valueOf(objSingular)).append(",");
709     }
710     final Object objCreator = component.getAttributes().get("creator");
711     if (objCreator != null) {
712       parameter.append("creator: ").append(String.valueOf(objCreator)).append(",");
713     }
714     if (parameter.length() > 0) {
715       parameter.deleteCharAt(parameter.lastIndexOf(","));
716       strBuilder.append(",{").append(parameter).append("}");
717     }
718     strBuilder.append(");");
719     return strBuilder.toString();
720   }
721 
722   public static void renderCommandFacet(final UIComponent component, final FacesContext facesContext,
723       final TobagoResponseWriter writer) throws IOException {
724     renderCommandFacet(component, component.getClientId(facesContext), facesContext, writer);
725   }
726 
727   public static void renderCommandFacet(
728       final UIComponent component, final String id, final FacesContext facesContext, final TobagoResponseWriter writer)
729       throws IOException {
730     if (ComponentUtils.getBooleanAttribute(component, Attributes.READONLY)
731         || ComponentUtils.getBooleanAttribute(component, Attributes.DISABLED)) {
732       return;
733     }
734     CommandMap commandMap = null;
735     final Map<String, UIComponent> facets = component.getFacets();
736     for (final Map.Entry<String, UIComponent> entry : facets.entrySet()) {
737       final UIComponent facetComponent = entry.getValue();
738       if (facetComponent.isRendered()
739           && (facetComponent instanceof AbstractUICommand || facetComponent instanceof UIForm)) {
740         if (commandMap == null) {
741           commandMap = new CommandMap();
742         }
743         final String key = entry.getKey();
744         commandMap.addCommand(key, new Command(facesContext, entry.getValue(), id));
745       }
746     }
747     if (commandMap != null) {
748       writer.writeAttribute(DataAttributes.COMMANDS, JsonUtils.encode(commandMap), true);
749     }
750   }
751 
752   public static boolean renderSheetCommands(final UISheet sheet, final FacesContext facesContext,
753                                          final TobagoResponseWriter writer) throws IOException {
754     CommandMap commandMap = null;
755     for (final UIComponent child : sheet.getChildren()) {
756       if (child instanceof UIColumnEvent) {
757         final UIColumnEvent columnEvent = (UIColumnEvent) child;
758         if (columnEvent.isRendered()) {
759           final UIComponent selectionChild = child.getChildren().get(0);
760           if (selectionChild != null && selectionChild instanceof AbstractUICommand && selectionChild.isRendered()) {
761             final UICommand action = (UICommand) selectionChild;
762             if (commandMap == null) {
763               commandMap = new CommandMap();
764             }
765             commandMap.addCommand(columnEvent.getEvent(), new Command(facesContext, action, null));
766           }
767         }
768       }
769     }
770     if (commandMap != null) {
771       writer.writeAttribute(DataAttributes.ROW_ACTION, JsonUtils.encode(commandMap), true);
772       return true;
773     }
774     return false;
775   }
776 
777   /**
778    * @deprecated Since Tobago 2.0.0. Because of CSP.
779    */
780   @Deprecated
781   public static void checkForCommandFacet(
782       final UIComponent component, final FacesContext facesContext, final TobagoResponseWriter writer)
783       throws IOException {
784     checkForCommandFacet(component, Arrays.asList(component.getClientId(facesContext)), facesContext, writer);
785   }
786 
787   /**
788    * @deprecated Since Tobago 2.0.0. Because of CSP.
789    */
790   @Deprecated
791   public static void checkForCommandFacet(
792       final UIComponent component, final List<String> clientIds, final FacesContext facesContext,
793       final TobagoResponseWriter writer) throws IOException {
794     if (ComponentUtils.getBooleanAttribute(component, Attributes.READONLY)
795         || ComponentUtils.getBooleanAttribute(component, Attributes.DISABLED)) {
796       return;
797     }
798     final Map<String, UIComponent> facets = component.getFacets();
799     for (final Map.Entry<String, UIComponent> entry : facets.entrySet()) {
800       if (entry.getValue() instanceof UICommand) {
801         addCommandFacet(clientIds, entry, facesContext, writer);
802       }
803     }
804   }
805 
806   /**
807    * @deprecated Since Tobago 2.0.0. Because of CSP.
808    */
809   @Deprecated
810   private static void addCommandFacet(
811       final List<String> clientIds, final Map.Entry<String, UIComponent> facetEntry,
812       final FacesContext facesContext, final TobagoResponseWriter writer)
813       throws IOException {
814     for (final String clientId : clientIds) {
815       writeScriptForClientId(clientId, facetEntry, facesContext, writer);
816     }
817   }
818 
819   /**
820    * @deprecated Since Tobago 2.0.0. Because of CSP.
821    */
822   @Deprecated
823   private static void writeScriptForClientId(
824       final String clientId, final Map.Entry<String, UIComponent> facetEntry,
825       final FacesContext facesContext, final TobagoResponseWriter writer) throws IOException {
826     if (facetEntry.getValue() instanceof UICommand
827         && ((UICommand) facetEntry.getValue()).getRenderedPartially().length > 0) {
828       writer.startJavascript();
829       writer.write("var element = Tobago.element(\"");
830       writer.write(clientId);
831       writer.write("\");\n");
832       writer.write("if (element) {\n");
833       writer.write("   Tobago.addEventListener(element, \"");
834       writer.write(facetEntry.getKey());
835       writer.write("\", function(){Tobago.reloadComponent(this, '");
836       writer.write(HtmlRendererUtils.getComponentIds(facesContext, facetEntry.getValue(),
837               ((UICommand) facetEntry.getValue()).getRenderedPartially()));
838       writer.write("','");
839       writer.write(facetEntry.getValue().getClientId(facesContext));
840       writer.write("', {})});\n");
841       writer.write("};");
842       writer.endJavascript();
843     } else {
844       final UIComponent facetComponent = facetEntry.getValue();
845 
846       writer.startJavascript();
847       writer.write("var element = Tobago.element(\"");
848       writer.write(clientId + "\");\n");
849       writer.write("if (element) {\n");
850       writer.write("   Tobago.addEventListener(element, \"");
851       writer.write(facetEntry.getKey());
852       writer.write("\", function(){");
853       String facetAction = (String) facetComponent.getAttributes().get(Attributes.ONCLICK);
854       if (facetAction != null) {
855          // Replace @autoId
856         facetAction = StringUtils.replace(facetAction, "@autoId", facetComponent.getClientId(facesContext));
857         writer.write(facetAction);
858       } else {
859         writer.write(createSubmitAction(
860             facetComponent.getClientId(facesContext),
861             ComponentUtils.getBooleanAttribute(facetComponent, Attributes.TRANSITION),
862             null,
863             clientId));
864       }
865       writer.write("});\n};");
866       writer.endJavascript();
867     }
868   }
869 
870   /**
871    * @deprecated since 2.0.0. JavaScript should not be rendered in the page. See CSP.
872    */
873   @Deprecated
874   public static String createSubmitAction(
875       final String clientId, final boolean transition, final String target, final String focus) {
876     final StringBuilder builder = new StringBuilder();
877     builder.append("Tobago.submitAction(this,'");
878     builder.append(clientId);
879     builder.append("',{");
880     if (!transition) { // transition == true is the default
881       builder.append("transition:false");
882       if (target != null || focus != null) {
883         builder.append(',');
884       }
885     }
886     if (target != null) {
887       builder.append("target:'");
888       builder.append(target);
889       builder.append('\'');
890       if (focus != null) {
891         builder.append(',');
892       }
893     }
894     if (focus != null) {
895       builder.append("focus:'");
896       builder.append(focus);
897       builder.append('\'');
898     }
899     builder.append("});");
900     return builder.toString();
901   }
902 
903   /**
904    * @deprecated since Tobago 1.5.0. Please use {@link org.apache.myfaces.tobago.renderkit.css.Classes}.
905    */
906   @Deprecated
907   public static void removeStyleClasses(final UIComponent cell) {
908     Deprecation.LOG.warn("cell = '" + cell + "'");
909   }
910 
911   public static void encodeContextMenu(
912       final FacesContext facesContext, final TobagoResponseWriter writer, final UIComponent parent)
913       throws IOException {
914     final UIComponent contextMenu = FacetUtils.getContextMenu(parent);
915     if (contextMenu != null) {
916       writer.startElement(HtmlElements.OL, contextMenu);
917       writer.writeClassAttribute("tobago-menuBar tobago-menu-contextMenu");
918       RenderUtils.encode(facesContext, contextMenu);
919       writer.endElement(HtmlElements.OL);
920     }
921   }
922 
923   /**
924    * @deprecated Since Tobago 2.0.3. Because of CSP.
925    */
926   @Deprecated
927   public static void addAcceleratorKey(
928       final FacesContext facesContext, final UIComponent component, final Character accessKey) {
929     final String clientId = component.getClientId(facesContext);
930     final String jsStatement = createOnclickAcceleratorKeyJsStatement(clientId, accessKey, null);
931     FacesContextUtils.addMenuAcceleratorScript(facesContext, jsStatement);
932   }
933 
934   public static void writeDataAttributes(
935       final FacesContext context, final TobagoResponseWriter writer, final UIComponent component)
936       throws IOException {
937 
938     final Map<Object, Object> dataAttributes = ComponentUtils.getDataAttributes(component);
939     if (dataAttributes == null) {
940       return;
941     }
942 
943     final ELContext elContext = context.getELContext();
944 
945     for (final Map.Entry<Object, Object> entry : dataAttributes.entrySet()) {
946       final Object mapKey = entry.getKey();
947       final String name = mapKey instanceof ValueExpression
948           ? ((ValueExpression) mapKey).getValue(elContext).toString() : mapKey.toString();
949       final Object mapValue = entry.getValue();
950       final String value = mapValue instanceof ValueExpression
951           ? ((ValueExpression) mapValue).getValue(elContext).toString() : mapValue.toString();
952       writer.writeAttribute("data-" + name, value, true);
953     }
954   }
955 }