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