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