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      public static String replaceStyleAttribute(String style, String name,
288          String value) {
289        style = removeStyleAttribute(style != null ? style : "", name);
290        return style + " " + name + ": " + value + ";";
291      }
292    
293      public static String removeStyleAttribute(String style, String name) {
294        if (style == null) {
295          return null;
296        }
297        String pattern = name + "\\s*?:[^;]*?;";
298        return style.replaceAll(pattern, "").trim();
299      }
300    
301      public static void removeStyleAttribute(UIComponent component, String name) {
302        ensureStyleAttributeMap(component).remove(name);
303      }
304    
305      /**
306       * @deprecated Please use StyleClasses.ensureStyleClasses(component).add(clazz);
307       */
308      @Deprecated
309      public static void addCssClass(UIComponent component, String clazz) {
310        StyleClasses.ensureStyleClasses(component).addFullQualifiedClass(clazz);
311      }
312    
313      public static void layoutWidth(FacesContext facesContext, UIComponent component) {
314        layoutSpace(facesContext, component, true);
315      }
316    
317      public static void layoutHeight(FacesContext facesContext, UIComponent component) {
318        layoutSpace(facesContext, component, false);
319      }
320    
321      public static void layoutSpace(FacesContext facesContext, UIComponent component,
322          boolean width) {
323    
324        // prepare html 'style' attribute
325        Integer layoutSpace;
326        String layoutAttribute;
327        String styleAttribute;
328        if (width) {
329          layoutSpace = LayoutUtil.getLayoutWidth(component);
330          layoutAttribute = ATTR_LAYOUT_WIDTH;
331          styleAttribute = HtmlAttributes.WIDTH;
332        } else {
333          layoutSpace = LayoutUtil.getLayoutHeight(component);
334          layoutAttribute = ATTR_LAYOUT_HEIGHT;
335          styleAttribute = HtmlAttributes.HEIGHT;
336        }
337        int space = -1;
338        if (layoutSpace != null) {
339          space = layoutSpace.intValue();
340        }
341        if (space == -1 && (!RENDERER_TYPE_OUT.equals(component.getRendererType()))) {
342          UIComponent parent = component.getParent();
343          space = LayoutUtil.getInnerSpace(facesContext, parent, width);
344          if (space > 0 && !ComponentUtil.isFacetOf(component, parent)) {
345            component.getAttributes().put(layoutAttribute, Integer.valueOf(space));
346            if (width) {
347              component.getAttributes().remove(ATTR_INNER_WIDTH);
348            } else {
349              component.getAttributes().remove(ATTR_INNER_HEIGHT);
350            }
351          }
352        }
353        if (space > 0) {
354          LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component);
355          if (layoutSpace != null
356              || !ComponentUtil.getBooleanAttribute(component, ATTR_INLINE)) {
357            int styleSpace = space;
358            if (renderer != null) {
359              if (width) {
360                styleSpace -= renderer.getComponentExtraWidth(facesContext, component);
361              } else {
362                styleSpace -= renderer.getComponentExtraHeight(facesContext, component);
363              }
364            }
365    
366            replaceStyleAttribute(component, styleAttribute, styleSpace);
367    
368          }
369          UIComponent layout = component.getFacet(FACET_LAYOUT);
370          if (layout != null) {
371            int layoutSpace2 = LayoutUtil.getInnerSpace(facesContext, component,
372                width);
373            if (layoutSpace2 > 0) {
374              layout.getAttributes().put(layoutAttribute, Integer.valueOf(layoutSpace2));
375            }
376          }
377        }
378      }
379    
380      public static void createHeaderAndBodyStyles(FacesContext facesContext, UIComponent component) {
381        createHeaderAndBodyStyles(facesContext, component, true);
382        createHeaderAndBodyStyles(facesContext, component, false);
383      }
384    
385      public static void createHeaderAndBodyStyles(FacesContext facesContext, UIComponent component, boolean width) {
386        LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component);
387        HtmlStyleMap style = (HtmlStyleMap) component.getAttributes().get(ATTR_STYLE);
388        Integer styleSpace = null;
389        try {
390          styleSpace = style.getInt(width ? "width" : "height");
391        } catch (Exception e) {
392          /* ignore */
393        }
394        if (styleSpace != null) {
395          int bodySpace = 0;
396          int headerSpace = 0;
397          if (!width) {
398            if (renderer != null) {
399              headerSpace = renderer.getHeaderHeight(facesContext, component);
400            }
401            bodySpace = styleSpace - headerSpace;
402          }
403          HtmlStyleMap headerStyle = ensureStyleAttributeMap(component, ATTR_STYLE_HEADER);
404          HtmlStyleMap bodyStyle = ensureStyleAttributeMap(component, ATTR_STYLE_BODY);
405          if (width) {
406            headerStyle.put("width", styleSpace);
407            bodyStyle.put("width", styleSpace);
408          } else {
409            headerStyle.put("height", headerSpace);
410            bodyStyle.put("height", bodySpace);
411          }
412        }
413      }
414    
415      /**
416       * @deprecated Please use StyleClasses.ensureStyleClasses(component).updateClassAttribute(renderer, component);
417       */
418      @Deprecated
419      public static void updateClassAttribute(String cssClass, String rendererName, UIComponent component) {
420        throw new UnsupportedOperationException(
421            "Please use StyleClasses.ensureStyleClasses(component).updateClassAttribute(renderer, component)");
422      }
423    
424      /**
425       * @deprecated Please use StyleClasses.addMarkupClass()
426       */
427      @Deprecated
428      public static void addMarkupClass(UIComponent component, String rendererName,
429          String subComponent, StringBuilder tobagoClass) {
430        throw new UnsupportedOperationException("Please use StyleClasses.addMarkupClass()");
431      }
432    
433      /**
434       * @deprecated Please use StyleClasses.addMarkupClass()
435       */
436      @Deprecated
437      public static void addMarkupClass(UIComponent component, String rendererName, StyleClasses classes) {
438        classes.addMarkupClass(component, rendererName);
439      }
440    
441      public static void addImageSources(FacesContext facesContext, TobagoResponseWriter writer, String src, String id)
442          throws IOException {
443        StringBuilder buffer = new StringBuilder();
444        buffer.append("new Tobago.Image('");
445        buffer.append(id);
446        buffer.append("','");
447        buffer.append(ResourceManagerUtil.getImageWithPath(facesContext, src, false));
448        buffer.append("','");
449        buffer.append(ResourceManagerUtil.getImageWithPath(facesContext, createSrc(src, "Disabled"), true));
450        buffer.append("','");
451        buffer.append(ResourceManagerUtil.getImageWithPath(facesContext, createSrc(src, "Hover"), true));
452        buffer.append("');");
453        writer.writeJavascript(buffer.toString());
454      }
455    
456      public static String createSrc(String src, String ext) {
457        int dot = src.lastIndexOf('.');
458        if (dot == -1) {
459          LOG.warn("Image src without extension: '" + src + "'");
460          return src;
461        } else {
462          return src.substring(0, dot) + ext + src.substring(dot);
463        }
464      }
465    
466      public static TobagoResponseWriter getTobagoResponseWriter(FacesContext facesContext) {
467    
468        ResponseWriter writer = facesContext.getResponseWriter();
469        if (writer instanceof TobagoResponseWriter) {
470          return (TobagoResponseWriter) writer;
471        } else {
472          return new TobagoResponseWriterWrapper(writer);
473        }
474      }
475    
476      /**
477       * @deprecated use TobagoResponseWriter.writeJavascript()
478       */
479      @Deprecated
480      public static void writeJavascript(ResponseWriter writer, String script) throws IOException {
481        startJavascript(writer);
482        writer.write(script);
483        endJavascript(writer);
484      }
485    
486      /**
487       * @deprecated use TobagoResponseWriter.writeJavascript()
488       */
489      @Deprecated
490      public static void startJavascript(ResponseWriter writer) throws IOException {
491        writer.startElement(HtmlConstants.SCRIPT, null);
492        writer.writeAttribute(HtmlAttributes.TYPE, "text/javascript", null);
493        writer.write("\n<!--\n");
494      }
495    
496      /**
497       * @deprecated use TobagoResponseWriter.writeJavascript()
498       */
499      @Deprecated
500      public static void endJavascript(ResponseWriter writer) throws IOException {
501        writer.write("\n// -->\n");
502        writer.endElement(HtmlConstants.SCRIPT);
503      }
504    
505      public static void writeScriptLoader(FacesContext facesContext, String script)
506          throws IOException {
507        writeScriptLoader(facesContext, new String[]{script}, null);
508      }
509    
510      public static void writeScriptLoader(FacesContext facesContext, String[] scripts, String[] afterLoadCmds)
511          throws IOException {
512        TobagoResponseWriter writer = HtmlRendererUtil.getTobagoResponseWriter(facesContext);
513    
514        String allScripts = "[]";
515        if (scripts != null) {
516          allScripts = ResourceManagerUtil.getScriptsAsJSArray(facesContext, scripts);
517        }
518    
519        StringBuilder script = new StringBuilder();
520        script.append("new Tobago.ScriptLoader(\n    ");
521        script.append(allScripts);
522    
523        if (afterLoadCmds != null && afterLoadCmds.length > 0) {
524          script.append(", \n");
525          boolean first = true;
526          for (String afterLoadCmd : afterLoadCmds) {
527            String[] splittedStrings = StringUtils.split(afterLoadCmd, '\n'); // split on <CR> to have nicer JS
528            for (String splitted : splittedStrings) {
529              String cmd = StringUtils.replace(splitted, "\\", "\\\\");
530              cmd = StringUtils.replace(cmd, "\"", "\\\"");
531              script.append(first ? "          " : "        + ");
532              script.append("\"");
533              script.append(cmd);
534              script.append("\"\n");
535              first = false;
536            }
537          }
538        }
539        script.append(");");
540    
541        writer.writeJavascript(script.toString());
542      }
543    
544      public static void writeStyleLoader(
545          FacesContext facesContext, String[] styles) throws IOException {
546        TobagoResponseWriter writer = HtmlRendererUtil.getTobagoResponseWriter(facesContext);
547    
548        StringBuilder builder = new StringBuilder();
549        builder.append("Tobago.ensureStyleFiles(\n    ");
550        builder.append(ResourceManagerUtil.getStylesAsJSArray(facesContext, styles));
551        builder.append(");");
552        writer.writeJavascript(builder.toString());
553      }
554    
555      public static String getTitleFromTipAndMessages(FacesContext facesContext, UIComponent component) {
556        String messages = ComponentUtil.getFacesMessageAsString(facesContext, component);
557        return HtmlRendererUtil.addTip(messages, component.getAttributes().get(ATTR_TIP));
558      }
559    
560      public static String addTip(String title, Object tip) {
561        if (tip != null) {
562          if (title != null && title.length() > 0) {
563            title += " :: ";
564          } else {
565            title = "";
566          }
567          title += tip;
568        }
569        return title;
570      }
571    
572      public static void renderSelectItems(UIInput component, List<SelectItem> items, Object[] values,
573          TobagoResponseWriter writer, FacesContext facesContext) throws IOException {
574    
575        if (LOG.isDebugEnabled()) {
576          LOG.debug("value = '" + Arrays.toString(values) + "'");
577        }
578        for (SelectItem item : items) {
579          if (item instanceof SelectItemGroup) {
580            writer.startElement(HtmlConstants.OPTGROUP, null);
581            writer.writeAttribute(HtmlAttributes.LABEL, item.getLabel(), true);
582            if (item.isDisabled()) {
583              writer.writeAttribute(HtmlAttributes.DISABLED, true);
584            }
585            SelectItem[] selectItems = ((SelectItemGroup) item).getSelectItems();
586            renderSelectItems(component, Arrays.asList(selectItems), values, writer, facesContext);
587            writer.endElement(HtmlConstants.OPTGROUP);
588          } else {
589            writer.startElement(HtmlConstants.OPTION, null);
590            final Object itemValue = item.getValue();
591            String formattedValue = RenderUtil.getFormattedValue(facesContext, component, itemValue);
592            writer.writeAttribute(HtmlAttributes.VALUE, formattedValue, true);
593            if (item instanceof org.apache.myfaces.tobago.model.SelectItem) {
594              String image = ((org.apache.myfaces.tobago.model.SelectItem) item).getImage();
595              if (image != null) {
596                String imagePath = ResourceManagerUtil.getImageWithPath(facesContext, image);
597                writer.writeStyleAttribute("background-image: url('" + imagePath + "')");
598              }
599            }
600            if (item instanceof SupportsMarkup) {
601              StyleClasses optionStyle = new StyleClasses();
602              optionStyle.addMarkupClass((SupportsMarkup) item, getRendererName(facesContext, component), "option");
603              writer.writeClassAttribute(optionStyle);
604            }
605            if (RenderUtil.contains(values, item.getValue())) {
606              writer.writeAttribute(HtmlAttributes.SELECTED, true);
607            }
608            if (item.isDisabled()) {
609              writer.writeAttribute(HtmlAttributes.DISABLED, true);
610            }
611            writer.writeText(item.getLabel());
612            writer.endElement(HtmlConstants.OPTION);
613          }
614        }
615      }
616    
617      public static String getComponentId(FacesContext context, UIComponent component, String componentId) {
618        UIComponent partiallyComponent = ComponentUtil.findComponent(component, componentId);
619        if (partiallyComponent != null) {
620          String clientId = partiallyComponent.getClientId(context);
621          if (partiallyComponent instanceof UIData) {
622            int rowIndex = ((UIData) partiallyComponent).getRowIndex();
623            if (rowIndex >= 0 && clientId.endsWith(Integer.toString(rowIndex))) {
624              return clientId.substring(0, clientId.lastIndexOf(NamingContainer.SEPARATOR_CHAR));
625            }
626          }
627          return clientId;
628        }
629        LOG.error("No Component found for id " + componentId);
630        return null;
631      }
632    
633      public static String toStyleString(String key, Integer value) {
634        StringBuilder buf = new StringBuilder();
635        buf.append(key);
636        buf.append(":");
637        buf.append(value);
638        buf.append("px; ");
639        return buf.toString();
640      }
641    
642      public static String toStyleString(String key, String value) {
643        StringBuilder buf = new StringBuilder();
644        buf.append(key);
645        buf.append(":");
646        buf.append(value);
647        buf.append("; ");
648        return buf.toString();
649      }
650    
651      public static void renderTip(UIComponent component, TobagoResponseWriter writer) throws IOException {
652        Object objTip = component.getAttributes().get(ATTR_TIP);
653        if (objTip != null) {
654          writer.writeAttribute(HtmlAttributes.TITLE, String.valueOf(objTip), true);
655        }
656      }
657    
658      public static void renderImageTip(UIComponent component, TobagoResponseWriter writer) throws IOException {
659        Object objTip = component.getAttributes().get(ATTR_TIP);
660        if (objTip != null) {
661          writer.writeAttribute(HtmlAttributes.ALT, String.valueOf(objTip), true);
662        } else {
663          writer.writeAttribute(HtmlAttributes.ALT, "", false);
664        }
665      }
666    
667      public static String getJavascriptString(String str) {
668        if (str != null) {
669          return "\"" + str + "\"";
670        }
671        return null;
672      }
673    
674      public static String getRenderedPartiallyJavascriptArray(FacesContext facesContext, UICommand command) {
675        if (command == null) {
676          return null;
677        }
678        String[] list = command.getRenderedPartially();
679        StringBuilder strBuilder = new StringBuilder();
680        strBuilder.append("[");
681        for (int i = 0; i < list.length; i++) {
682          if (i != 0) {
683            strBuilder.append(",");
684          }
685          strBuilder.append("\"");
686          strBuilder.append(HtmlRendererUtil.getComponentId(facesContext, command, list[i]));
687          strBuilder.append("\"");
688        }
689        strBuilder.append("]");
690        return strBuilder.toString();
691      }
692    
693      public static String getJavascriptArray(String[] list) {
694        StringBuilder strBuilder = new StringBuilder();
695        strBuilder.append("[");
696        for (int i = 0; i < list.length; i++) {
697          if (i != 0) {
698            strBuilder.append(",");
699          }
700          strBuilder.append("\"");
701          strBuilder.append(list[i]);
702          strBuilder.append("\"");
703        }
704        strBuilder.append("]");
705        return strBuilder.toString();
706      }
707    
708      public static void removeStyleClasses(UIComponent cell) {
709        Object obj = cell.getAttributes().get(org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_CLASS);
710        if (obj != null && obj instanceof StyleClasses && cell.getRendererType() != null) {
711          StyleClasses styleClasses = (StyleClasses) obj;
712          if (!styleClasses.isEmpty()) {
713            String rendererName = cell.getRendererType().substring(0, 1).toLowerCase(Locale.ENGLISH)
714                + cell.getRendererType().substring(1);
715            styleClasses.removeTobagoClasses(rendererName);
716          }
717          if (styleClasses.isEmpty()) {
718            cell.getAttributes().remove(org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_CLASS);
719          }
720        }
721      }
722    }