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 }