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