001    package org.apache.myfaces.tobago.renderkit.html;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.commons.lang.StringUtils;
021    import org.apache.commons.logging.Log;
022    import org.apache.commons.logging.LogFactory;
023    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_FOCUS;
024    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INLINE;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_HEIGHT;
026    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_WIDTH;
027    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_HEIGHT;
028    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_WIDTH;
029    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE;
030    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_BODY;
031    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_HEADER;
032    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TIP;
033    import static org.apache.myfaces.tobago.TobagoConstants.FACET_LAYOUT;
034    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_OUT;
035    import org.apache.myfaces.tobago.component.ComponentUtil;
036    import org.apache.myfaces.tobago.component.SupportsMarkup;
037    import org.apache.myfaces.tobago.component.UICommand;
038    import org.apache.myfaces.tobago.component.UIData;
039    import org.apache.myfaces.tobago.component.UIPage;
040    import org.apache.myfaces.tobago.context.ResourceManagerUtil;
041    import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
042    import org.apache.myfaces.tobago.renderkit.LayoutInformationProvider;
043    import org.apache.myfaces.tobago.renderkit.LayoutableRendererBase;
044    import org.apache.myfaces.tobago.renderkit.RenderUtil;
045    import org.apache.myfaces.tobago.renderkit.RendererBaseWrapper;
046    import org.apache.myfaces.tobago.util.LayoutUtil;
047    import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
048    import org.apache.myfaces.tobago.webapp.TobagoResponseWriterWrapper;
049    
050    import javax.faces.component.NamingContainer;
051    import javax.faces.component.UIComponent;
052    import javax.faces.component.UIInput;
053    import javax.faces.context.FacesContext;
054    import javax.faces.context.ResponseWriter;
055    import javax.faces.model.SelectItem;
056    import javax.faces.model.SelectItemGroup;
057    import java.io.IOException;
058    import java.util.Arrays;
059    import java.util.List;
060    import java.util.Locale;
061    import java.util.Map;
062    import java.util.StringTokenizer;
063    
064    /*
065     * User: weber
066     * Date: Jan 11, 2005
067     * Time: 4:59:36 PM
068     */
069    public final class HtmlRendererUtil {
070    
071      private static final Log LOG = LogFactory.getLog(HtmlRendererUtil.class);
072    
073      private HtmlRendererUtil() {
074        // to prevent instantiation
075      }
076    
077      public static void renderFocusId(FacesContext facesContext, UIComponent component)
078          throws IOException {
079    
080        if (ComponentUtil.getBooleanAttribute(component, ATTR_FOCUS)) {
081          UIPage page = ComponentUtil.findPage(facesContext, component);
082          String id = component.getClientId(facesContext);
083          if (!StringUtils.isBlank(page.getFocusId()) && !page.getFocusId().equals(id)) {
084            LOG.warn("page focusId = \"" + page.getFocusId() + "\" ignoring new value \""
085                + id + "\"");
086          } else {
087            TobagoResponseWriter writer = HtmlRendererUtil.getTobagoResponseWriter(facesContext);
088            writer.writeJavascript("Tobago.focusId = '" + id + "';");
089          }
090        }
091      }
092    
093      public static void prepareRender(FacesContext facesContext, UIComponent component) {
094        // xxx find a better way for this question: isTobago or isLayoutable something like that.
095        LayoutableRendererBase layoutRendererBase = ComponentUtil.getRenderer(facesContext, component);
096        if (layoutRendererBase != null && !(layoutRendererBase instanceof RendererBaseWrapper)) {
097          createCssClass(facesContext, component);
098          layoutWidth(facesContext, component);
099          layoutHeight(facesContext, component);
100        }
101      }
102    
103      public static HtmlStyleMap prepareInnerStyle(UIComponent component) {
104        HtmlStyleMap htmlStyleMap = new HtmlStyleMap();
105        Integer innerSpaceInteger = (Integer)
106            component.getAttributes().get(ATTR_INNER_WIDTH);
107        if (innerSpaceInteger != null && innerSpaceInteger != -1) {
108          htmlStyleMap.put("width", innerSpaceInteger);
109        }
110        innerSpaceInteger = (Integer)
111            component.getAttributes().get(ATTR_INNER_HEIGHT);
112        if (innerSpaceInteger != null && innerSpaceInteger != -1) {
113          htmlStyleMap.put("height", innerSpaceInteger);
114        }
115        return htmlStyleMap;
116      }
117    
118    
119      public static void createCssClass(FacesContext facesContext, UIComponent component) {
120        String rendererName = getRendererName(facesContext, component);
121        if (rendererName != null) {
122          StyleClasses classes = StyleClasses.ensureStyleClasses(component);
123          classes.updateClassAttributeAndMarkup(component, rendererName);
124        }
125      }
126    
127      public static String getRendererName(FacesContext facesContext, UIComponent component) {
128        final String rendererType = component.getRendererType();
129        //final String family = component.getFamily();
130        if (rendererType != null//&& !"facelets".equals(family)
131            ) {
132          LayoutableRendererBase layoutableRendererBase = ComponentUtil.getRenderer(facesContext, component);
133          if (layoutableRendererBase != null) {
134            return layoutableRendererBase.getRendererName(rendererType);
135          }
136        }
137        return null;
138      }
139    
140      public static void writeLabelWithAccessKey(TobagoResponseWriter writer, LabelWithAccessKey label)
141          throws IOException {
142        int pos = label.getPos();
143        String text = label.getText();
144        if (pos == -1) {
145          writer.writeText(text);
146        } else {
147          writer.writeText(text.substring(0, pos));
148          writer.startElement(HtmlConstants.U, null);
149          writer.writeText(Character.toString(text.charAt(pos)));
150          writer.endElement(HtmlConstants.U);
151          writer.writeText(text.substring(pos + 1));
152        }
153      }
154    
155      public static void setDefaultTransition(FacesContext facesContext, boolean transition)
156          throws IOException {
157        writeScriptLoader(facesContext, null, new String[]{"Tobago.transition = " + transition + ";"});
158      }
159    
160      public static void addClickAcceleratorKey(
161          FacesContext facesContext, String clientId, char key)
162          throws IOException {
163        addClickAcceleratorKey(facesContext, clientId, key, null);
164      }
165    
166      public static void addClickAcceleratorKey(
167          FacesContext facesContext, String clientId, char key, String modifier)
168          throws IOException {
169        String str
170            = createOnclickAcceleratorKeyJsStatement(clientId, key, modifier);
171        writeScriptLoader(facesContext, null, new String[]{str});
172      }
173    
174      public static void addAcceleratorKey(
175          FacesContext facesContext, String func, char key) throws IOException {
176        addAcceleratorKey(facesContext, func, key, null);
177      }
178    
179      public static void addAcceleratorKey(
180          FacesContext facesContext, String func, char key, String modifier)
181          throws IOException {
182        String str = createAcceleratorKeyJsStatement(func, key, modifier);
183        writeScriptLoader(facesContext, null, new String[]{str});
184      }
185    
186      public static String createOnclickAcceleratorKeyJsStatement(
187          String clientId, char key, String modifier) {
188        String func = "Tobago.clickOnElement('" + clientId + "');";
189        return createAcceleratorKeyJsStatement(func, key, modifier);
190      }
191    
192      public static String createAcceleratorKeyJsStatement(
193          String func, char key, String modifier) {
194        StringBuilder buffer = new StringBuilder();
195        buffer.append("new Tobago.AcceleratorKey(function() {");
196        buffer.append(func);
197        if (!func.endsWith(";")) {
198          buffer.append(';');
199        }
200        buffer.append("}, \"");
201        buffer.append(key);
202        if (modifier != null) {
203          buffer.append("\", \"");
204          buffer.append(modifier);
205        }
206        buffer.append("\");");
207        return buffer.toString();
208      }
209    
210      public static String getLayoutSpaceStyle(UIComponent component) {
211        StringBuilder sb = new StringBuilder();
212        Integer space = LayoutUtil.getLayoutSpace(component, ATTR_LAYOUT_WIDTH, ATTR_LAYOUT_WIDTH);
213        if (space != null) {
214          sb.append(" width: ");
215          sb.append(space);
216          sb.append("px;");
217        }
218        space = LayoutUtil.getLayoutSpace(component, ATTR_LAYOUT_HEIGHT, ATTR_LAYOUT_HEIGHT);
219        if (space != null) {
220          sb.append(" height: ");
221          sb.append(space);
222          sb.append("px;");
223        }
224        return sb.toString();
225      }
226    
227      public static Integer getStyleAttributeIntValue(HtmlStyleMap style, String name) {
228        if (style == null) {
229          return null;
230        }
231        return style.getInt(name);
232      }
233    
234      public static String getStyleAttributeValue(String style, String name) {
235        if (style == null) {
236          return null;
237        }
238        String value = null;
239        StringTokenizer st = new StringTokenizer(style, ";");
240        while (st.hasMoreTokens()) {
241          String attribute = st.nextToken().trim();
242          if (attribute.startsWith(name)) {
243            value = attribute.substring(attribute.indexOf(':') + 1).trim();
244          }
245        }
246        return value;
247      }
248    
249    
250      public static void replaceStyleAttribute(UIComponent component, String styleAttribute, String value) {
251        HtmlStyleMap style = ensureStyleAttributeMap(component);
252        style.put(styleAttribute, value);
253      }
254    
255      public static void replaceStyleAttribute(UIComponent component, String attribute,
256          String styleAttribute, String value) {
257        HtmlStyleMap style = ensureStyleAttributeMap(component, attribute);
258        style.put(styleAttribute, value);
259      }
260    
261      public static void replaceStyleAttribute(UIComponent component, String styleAttribute, int value) {
262        HtmlStyleMap style = ensureStyleAttributeMap(component);
263        style.put(styleAttribute, value);
264      }
265    
266      public static void replaceStyleAttribute(UIComponent component, String attribute,
267          String styleAttribute, int value) {
268        HtmlStyleMap style = ensureStyleAttributeMap(component, attribute);
269        style.put(styleAttribute, value);
270    
271      }
272    
273      private static HtmlStyleMap ensureStyleAttributeMap(UIComponent component) {
274        return ensureStyleAttributeMap(component, ATTR_STYLE);
275      }
276    
277      private static HtmlStyleMap ensureStyleAttributeMap(UIComponent component, String attribute) {
278        final Map attributes = component.getAttributes();
279        HtmlStyleMap style = (HtmlStyleMap) attributes.get(attribute);
280        if (style == null) {
281          style = new HtmlStyleMap();
282          attributes.put(attribute, style);
283        }
284        return style;
285      }
286    
287      /**
288       * @deprecated
289       */
290      public static String replaceStyleAttribute(String style, String name,
291          String value) {
292        style = removeStyleAttribute(style != null ? style : "", name);
293        return style + " " + name + ": " + value + ";";
294      }
295    
296      /**
297       * @deprecated
298       */
299      public static String removeStyleAttribute(String style, String name) {
300        if (style == null) {
301          return null;
302        }
303        String pattern = name + "\\s*?:[^;]*?;";
304        return style.replaceAll(pattern, "").trim();
305      }
306    
307      public static void removeStyleAttribute(UIComponent component, String name) {
308        ensureStyleAttributeMap(component).remove(name);
309      }
310    
311      /**
312       * @deprecated Please use StyleClasses.ensureStyleClasses(component).add(clazz);
313       */
314      @Deprecated
315      public static void addCssClass(UIComponent component, String clazz) {
316        StyleClasses.ensureStyleClasses(component).addFullQualifiedClass(clazz);
317      }
318    
319      public static void layoutWidth(FacesContext facesContext, UIComponent component) {
320        layoutSpace(facesContext, component, true);
321      }
322    
323      public static void layoutHeight(FacesContext facesContext, UIComponent component) {
324        layoutSpace(facesContext, component, false);
325      }
326    
327      public static void layoutSpace(FacesContext facesContext, UIComponent component,
328          boolean width) {
329    
330        // prepare html 'style' attribute
331        Integer layoutSpace;
332        String layoutAttribute;
333        String styleAttribute;
334        if (width) {
335          layoutSpace = LayoutUtil.getLayoutWidth(component);
336          layoutAttribute = ATTR_LAYOUT_WIDTH;
337          styleAttribute = HtmlAttributes.WIDTH;
338        } else {
339          layoutSpace = LayoutUtil.getLayoutHeight(component);
340          layoutAttribute = ATTR_LAYOUT_HEIGHT;
341          styleAttribute = HtmlAttributes.HEIGHT;
342        }
343        int space = -1;
344        if (layoutSpace != null) {
345          space = layoutSpace.intValue();
346        }
347        if (space == -1 && (!RENDERER_TYPE_OUT.equals(component.getRendererType()))) {
348          UIComponent parent = component.getParent();
349          space = LayoutUtil.getInnerSpace(facesContext, parent, width);
350          if (space > 0 && !ComponentUtil.isFacetOf(component, parent)) {
351            component.getAttributes().put(layoutAttribute, Integer.valueOf(space));
352            if (width) {
353              component.getAttributes().remove(ATTR_INNER_WIDTH);
354            } else {
355              component.getAttributes().remove(ATTR_INNER_HEIGHT);
356            }
357          }
358        }
359        if (space > 0) {
360          LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component);
361          if (layoutSpace != null
362              || !ComponentUtil.getBooleanAttribute(component, ATTR_INLINE)) {
363            int styleSpace = space;
364            if (renderer != null) {
365              if (width) {
366                styleSpace -= renderer.getComponentExtraWidth(facesContext, component);
367              } else {
368                styleSpace -= renderer.getComponentExtraHeight(facesContext, component);
369              }
370            }
371    
372            replaceStyleAttribute(component, styleAttribute, styleSpace);
373    
374          }
375          UIComponent layout = component.getFacet(FACET_LAYOUT);
376          if (layout != null) {
377            int layoutSpace2 = LayoutUtil.getInnerSpace(facesContext, component,
378                width);
379            if (layoutSpace2 > 0) {
380              layout.getAttributes().put(layoutAttribute, Integer.valueOf(layoutSpace2));
381            }
382          }
383        }
384      }
385    
386      public static void createHeaderAndBodyStyles(FacesContext facesContext, UIComponent component) {
387        createHeaderAndBodyStyles(facesContext, component, true);
388        createHeaderAndBodyStyles(facesContext, component, false);
389      }
390    
391      public static void createHeaderAndBodyStyles(FacesContext facesContext, UIComponent component, boolean width) {
392        LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component);
393        HtmlStyleMap style = (HtmlStyleMap) component.getAttributes().get(ATTR_STYLE);
394        Integer styleSpace = null;
395        try {
396          styleSpace = style.getInt(width ? "width" : "height");
397        } catch (Exception e) {
398          /* ignore */
399        }
400        if (styleSpace != null) {
401          int bodySpace = 0;
402          int headerSpace = 0;
403          if (!width) {
404            if (renderer != null) {
405              headerSpace = renderer.getHeaderHeight(facesContext, component);
406            }
407            bodySpace = styleSpace - headerSpace;
408          }
409          HtmlStyleMap headerStyle = ensureStyleAttributeMap(component, ATTR_STYLE_HEADER);
410          HtmlStyleMap bodyStyle = ensureStyleAttributeMap(component, ATTR_STYLE_BODY);
411          if (width) {
412            headerStyle.put("width", styleSpace);
413            bodyStyle.put("width", styleSpace);
414          } else {
415            headerStyle.put("height", headerSpace);
416            bodyStyle.put("height", bodySpace);
417          }
418        }
419      }
420    
421      /**
422       * @deprecated Please use StyleClasses.ensureStyleClasses(component).updateClassAttribute(renderer, component);
423       */
424      @Deprecated
425      public static void updateClassAttribute(String cssClass, String rendererName, UIComponent component) {
426        throw new UnsupportedOperationException(
427            "Please use StyleClasses.ensureStyleClasses(component).updateClassAttribute(renderer, component)");
428      }
429    
430      /**
431       * @deprecated Please use StyleClasses.addMarkupClass()
432       */
433      @Deprecated
434      public static void addMarkupClass(UIComponent component, String rendererName,
435          String subComponent, StringBuilder tobagoClass) {
436        throw new UnsupportedOperationException("Please use StyleClasses.addMarkupClass()");
437      }
438    
439      /**
440       * @deprecated Please use StyleClasses.addMarkupClass()
441       */
442      @Deprecated
443      public static void addMarkupClass(UIComponent component, String rendererName, StyleClasses classes) {
444        classes.addMarkupClass(component, rendererName);
445      }
446    
447      public static void addImageSources(FacesContext facesContext, TobagoResponseWriter writer, String src, String id)
448          throws IOException {
449        StringBuilder buffer = new StringBuilder();
450        buffer.append("new Tobago.Image('");
451        buffer.append(id);
452        buffer.append("','");
453        buffer.append(ResourceManagerUtil.getImageWithPath(facesContext, src, false));
454        buffer.append("','");
455        buffer.append(ResourceManagerUtil.getImageWithPath(facesContext, createSrc(src, "Disabled"), true));
456        buffer.append("','");
457        buffer.append(ResourceManagerUtil.getImageWithPath(facesContext, createSrc(src, "Hover"), true));
458        buffer.append("');");
459        writer.writeJavascript(buffer.toString());
460      }
461    
462      public static String createSrc(String src, String ext) {
463        int dot = src.lastIndexOf('.');
464        if (dot == -1) {
465          LOG.warn("Image src without extension: '" + src + "'");
466          return src;
467        } else {
468          return src.substring(0, dot) + ext + src.substring(dot);
469        }
470      }
471    
472      public static TobagoResponseWriter getTobagoResponseWriter(FacesContext facesContext) {
473    
474        ResponseWriter writer = facesContext.getResponseWriter();
475        if (writer instanceof TobagoResponseWriter) {
476          return (TobagoResponseWriter) writer;
477        } else {
478          return new TobagoResponseWriterWrapper(writer);
479        }
480      }
481    
482      /**
483       * @deprecated use TobagoResponseWriter.writeJavascript()
484       */
485      @Deprecated
486      public static void writeJavascript(ResponseWriter writer, String script) throws IOException {
487        startJavascript(writer);
488        writer.write(script);
489        endJavascript(writer);
490      }
491    
492      /**
493       * @deprecated use TobagoResponseWriter.writeJavascript()
494       */
495      @Deprecated
496      public static void startJavascript(ResponseWriter writer) throws IOException {
497        writer.startElement(HtmlConstants.SCRIPT, null);
498        writer.writeAttribute(HtmlAttributes.TYPE, "text/javascript", null);
499        writer.write("\n<!--\n");
500      }
501    
502      /**
503       * @deprecated use TobagoResponseWriter.writeJavascript()
504       */
505      @Deprecated
506      public static void endJavascript(ResponseWriter writer) throws IOException {
507        writer.write("\n// -->\n");
508        writer.endElement(HtmlConstants.SCRIPT);
509      }
510    
511      public static void writeScriptLoader(FacesContext facesContext, String script)
512          throws IOException {
513        writeScriptLoader(facesContext, new String[]{script}, null);
514      }
515    
516      public static void writeScriptLoader(FacesContext facesContext, String[] scripts, String[] afterLoadCmds)
517          throws IOException {
518        TobagoResponseWriter writer = HtmlRendererUtil.getTobagoResponseWriter(facesContext);
519    
520        String allScripts = "[]";
521        if (scripts != null) {
522          allScripts = ResourceManagerUtil.getScriptsAsJSArray(facesContext, scripts);
523        }
524    
525        StringBuilder script = new StringBuilder();
526        script.append("new Tobago.ScriptLoader(\n    ");
527        script.append(allScripts);
528    
529        if (afterLoadCmds != null && afterLoadCmds.length > 0) {
530          script.append(", \n");
531          boolean first = true;
532          for (String afterLoadCmd : afterLoadCmds) {
533            String[] splittedStrings = StringUtils.split(afterLoadCmd, '\n'); // split on <CR> to have nicer JS
534            for (String splitted : splittedStrings) {
535              String cmd = StringUtils.replace(splitted, "\\", "\\\\");
536              cmd = StringUtils.replace(cmd, "\"", "\\\"");
537              script.append(first ? "          " : "        + ");
538              script.append("\"");
539              script.append(cmd);
540              script.append("\"\n");
541              first = false;
542            }
543          }
544        }
545        script.append(");");
546    
547        writer.writeJavascript(script.toString());
548      }
549    
550      public static void writeStyleLoader(
551          FacesContext facesContext, String[] styles) throws IOException {
552        TobagoResponseWriter writer = HtmlRendererUtil.getTobagoResponseWriter(facesContext);
553    
554        StringBuilder builder = new StringBuilder();
555        builder.append("Tobago.ensureStyleFiles(\n    ");
556        builder.append(ResourceManagerUtil.getStylesAsJSArray(facesContext, styles));
557        builder.append(");");
558        writer.writeJavascript(builder.toString());
559      }
560    
561      public static String getTitleFromTipAndMessages(FacesContext facesContext, UIComponent component) {
562        String messages = ComponentUtil.getFacesMessageAsString(facesContext, component);
563        return HtmlRendererUtil.addTip(messages, component.getAttributes().get(ATTR_TIP));
564      }
565    
566      public static String addTip(String title, Object tip) {
567        if (tip != null) {
568          if (title != null && title.length() > 0) {
569            title += " :: ";
570          } else {
571            title = "";
572          }
573          title += tip;
574        }
575        return title;
576      }
577    
578      public static void renderSelectItems(UIInput component, List<SelectItem> items, Object[] values,
579          TobagoResponseWriter writer, FacesContext facesContext) throws IOException {
580    
581        if (LOG.isDebugEnabled()) {
582          LOG.debug("value = '" + Arrays.toString(values) + "'");
583        }
584        for (SelectItem item : items) {
585          if (item instanceof SelectItemGroup) {
586            writer.startElement(HtmlConstants.OPTGROUP, null);
587            writer.writeAttribute(HtmlAttributes.LABEL, item.getLabel(), true);
588            if (item.isDisabled()) {
589              writer.writeAttribute(HtmlAttributes.DISABLED, true);
590            }
591            SelectItem[] selectItems = ((SelectItemGroup) item).getSelectItems();
592            renderSelectItems(component, Arrays.asList(selectItems), values, writer, facesContext);
593            writer.endElement(HtmlConstants.OPTGROUP);
594          } else {
595            writer.startElement(HtmlConstants.OPTION, null);
596            final Object itemValue = item.getValue();
597            String formattedValue = RenderUtil.getFormattedValue(facesContext, component, itemValue);
598            writer.writeAttribute(HtmlAttributes.VALUE, formattedValue, true);
599            if (item instanceof org.apache.myfaces.tobago.model.SelectItem) {
600              String image = ((org.apache.myfaces.tobago.model.SelectItem) item).getImage();
601              if (image != null) {
602                String imagePath = ResourceManagerUtil.getImageWithPath(facesContext, image);
603                writer.writeStyleAttribute("background-image: url('" + imagePath + "')");
604              }
605            }
606            if (item instanceof SupportsMarkup) {
607              StyleClasses optionStyle = new StyleClasses();
608              optionStyle.addMarkupClass((SupportsMarkup) item, getRendererName(facesContext, component), "option");
609              writer.writeClassAttribute(optionStyle);
610            }
611            if (RenderUtil.contains(values, item.getValue())) {
612              writer.writeAttribute(HtmlAttributes.SELECTED, true);
613            }
614            if (item.isDisabled()) {
615              writer.writeAttribute(HtmlAttributes.DISABLED, true);
616            }
617            writer.writeText(item.getLabel());
618            writer.endElement(HtmlConstants.OPTION);
619          }
620        }
621      }
622    
623      public static String getComponentId(FacesContext context, UIComponent component, String componentId) {
624        UIComponent partiallyComponent = ComponentUtil.findComponent(component, componentId);
625        if (partiallyComponent != null) {
626          String clientId = partiallyComponent.getClientId(context);
627          if (partiallyComponent instanceof UIData) {
628            int rowIndex = ((UIData) partiallyComponent).getRowIndex();
629            if (rowIndex >= 0 && clientId.endsWith(Integer.toString(rowIndex))) {
630              return clientId.substring(0, clientId.lastIndexOf(NamingContainer.SEPARATOR_CHAR));
631            }
632          }
633          return clientId;
634        }
635        LOG.error("No Component found for id " + componentId + " search base component " + component.getClientId(context));
636        return null;
637      }
638    
639      public static String toStyleString(String key, Integer value) {
640        StringBuilder buf = new StringBuilder();
641        buf.append(key);
642        buf.append(":");
643        buf.append(value);
644        buf.append("px; ");
645        return buf.toString();
646      }
647    
648      public static String toStyleString(String key, String value) {
649        StringBuilder buf = new StringBuilder();
650        buf.append(key);
651        buf.append(":");
652        buf.append(value);
653        buf.append("; ");
654        return buf.toString();
655      }
656    
657      public static void renderTip(UIComponent component, TobagoResponseWriter writer) throws IOException {
658        Object objTip = component.getAttributes().get(ATTR_TIP);
659        if (objTip != null) {
660          writer.writeAttribute(HtmlAttributes.TITLE, String.valueOf(objTip), true);
661        }
662      }
663    
664      public static void renderImageTip(UIComponent component, TobagoResponseWriter writer) throws IOException {
665        Object objTip = component.getAttributes().get(ATTR_TIP);
666        if (objTip != null) {
667          writer.writeAttribute(HtmlAttributes.ALT, String.valueOf(objTip), true);
668        } else {
669          writer.writeAttribute(HtmlAttributes.ALT, "", false);
670        }
671      }
672    
673      public static String getJavascriptString(String str) {
674        if (str != null) {
675          return "\"" + str + "\"";
676        }
677        return null;
678      }
679    
680      public static String getRenderedPartiallyJavascriptArray(FacesContext facesContext, UICommand command) {
681        if (command == null) {
682          return null;
683        }
684        String[] list = command.getRenderedPartially();
685        StringBuilder strBuilder = new StringBuilder();
686        strBuilder.append("[");
687        for (int i = 0; i < list.length; i++) {
688          if (i != 0) {
689            strBuilder.append(",");
690          }
691          strBuilder.append("\"");
692          strBuilder.append(HtmlRendererUtil.getComponentId(facesContext, command, list[i]));
693          strBuilder.append("\"");
694        }
695        strBuilder.append("]");
696        return strBuilder.toString();
697      }
698    
699      public static String getJavascriptArray(String[] list) {
700        StringBuilder strBuilder = new StringBuilder();
701        strBuilder.append("[");
702        for (int i = 0; i < list.length; i++) {
703          if (i != 0) {
704            strBuilder.append(",");
705          }
706          strBuilder.append("\"");
707          strBuilder.append(list[i]);
708          strBuilder.append("\"");
709        }
710        strBuilder.append("]");
711        return strBuilder.toString();
712      }
713    
714      public static void removeStyleClasses(UIComponent cell) {
715        Object obj = cell.getAttributes().get(org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_CLASS);
716        if (obj != null && obj instanceof StyleClasses && cell.getRendererType() != null) {
717          StyleClasses styleClasses = (StyleClasses) obj;
718          if (!styleClasses.isEmpty()) {
719            String rendererName = cell.getRendererType().substring(0, 1).toLowerCase(Locale.ENGLISH)
720                + cell.getRendererType().substring(1);
721            styleClasses.removeTobagoClasses(rendererName);
722          }
723          if (styleClasses.isEmpty()) {
724            cell.getAttributes().remove(org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_CLASS);
725          }
726        }
727      }
728    }