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