1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.custom.navmenu.jscookmenu;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.myfaces.custom.navmenu.NavigationMenuItem;
24 import org.apache.myfaces.custom.navmenu.NavigationMenuUtils;
25 import org.apache.myfaces.custom.navmenu.UINavigationMenuItem;
26 import org.apache.myfaces.renderkit.html.util.AddResource;
27 import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
28 import org.apache.myfaces.shared_tomahawk.el.SimpleActionMethodBinding;
29 import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
30 import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
31 import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
32 import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlFormRendererBase;
33 import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRenderer;
34 import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;
35 import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
36
37 import javax.faces.FacesException;
38 import javax.faces.component.UIComponent;
39 import javax.faces.context.ExternalContext;
40 import javax.faces.context.FacesContext;
41 import javax.faces.context.ResponseWriter;
42 import javax.faces.el.MethodBinding;
43 import javax.faces.el.ValueBinding;
44 import javax.faces.event.ActionEvent;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.StringTokenizer;
50
51
52
53
54
55
56
57
58
59
60 public class HtmlJSCookMenuRenderer
61 extends HtmlRenderer {
62 private static final String MYFACES_HACK_SCRIPT = "MyFacesHack.js";
63
64 private static final String JSCOOK_MENU_SCRIPT = "JSCookMenu.js";
65
66 private static final String JSCOOK_EFFECT_SCRIPT = "effect.js";
67
68 private static final Log log = LogFactory.getLog(HtmlJSCookMenuRenderer.class);
69
70 private static final String JSCOOK_ACTION_PARAM = "jscook_action";
71 private static final Class[] ACTION_LISTENER_ARGS = {ActionEvent.class};
72
73 private static final Map builtInThemes = new java.util.HashMap();
74
75 static {
76 builtInThemes.put("ThemeOffice", "ThemeOffice/");
77 builtInThemes.put("ThemeMiniBlack", "ThemeMiniBlack/");
78 builtInThemes.put("ThemeIE", "ThemeIE/");
79 builtInThemes.put("ThemePanel", "ThemePanel/");
80 builtInThemes.put("ThemeGray", "ThemeGray/");
81 }
82
83 public void decode(FacesContext context, UIComponent component) {
84 RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);
85
86 Map parameter = context.getExternalContext().getRequestParameterMap();
87 String actionParam = (String) parameter.get(JSCOOK_ACTION_PARAM);
88 if (actionParam != null && !actionParam.trim().equals("") &&
89 !actionParam.trim().equals("null")) {
90 String compId = getMenuId(context, component);
91 StringTokenizer tokenizer = new StringTokenizer(actionParam, ":");
92 if (tokenizer.countTokens() > 1) {
93 String actionId = tokenizer.nextToken();
94 if (! compId.equals(actionId)) {
95 return;
96 }
97 while (tokenizer.hasMoreTokens()) {
98 String action = tokenizer.nextToken();
99 if (action.startsWith("A]")) {
100 action = action.substring(2, action.length());
101 action = decodeValueBinding(action, context);
102 MethodBinding mb;
103 if (NavigationMenuUtils.isValueReference(action)) {
104 mb = context.getApplication().createMethodBinding(action, null);
105 }
106 else {
107 mb = new SimpleActionMethodBinding(action);
108 }
109 ((HtmlCommandJSCookMenu) component).setAction(mb);
110 }
111 else if (action.startsWith("L]")) {
112 action = action.substring(2, action.length());
113 String value = null;
114 int idx = action.indexOf(";");
115 if (idx > 0 && idx < action.length() - 1) {
116 value = action.substring(idx + 1, action.length());
117 action = action.substring(0, idx);
118 ((HtmlCommandJSCookMenu) component).setValue(value);
119 }
120 else if (idx == action.length() - 1)
121 {
122 value = null;
123 action = action.substring(0, idx);
124 }
125 MethodBinding mb;
126 if (NavigationMenuUtils.isValueReference(action)) {
127 mb = context.getApplication().createMethodBinding(action, ACTION_LISTENER_ARGS);
128 ((HtmlCommandJSCookMenu) component).setActionListener(mb);
129 if (value != null)
130 ((HtmlCommandJSCookMenu) component).setValue(value);
131 }
132 }
133 }
134 }
135 component.queueEvent(new ActionEvent(component));
136 }
137 }
138
139 private String decodeValueBinding(String actionParam, FacesContext context) {
140 int idx = actionParam.indexOf(";#{");
141 if (idx == -1) {
142 return actionParam;
143 }
144
145 String newActionParam = actionParam.substring(0, idx);
146 String vbParam = actionParam.substring(idx + 1);
147
148 idx = vbParam.indexOf('=');
149 if (idx == -1) {
150 return newActionParam;
151 }
152 String vbExpressionString = vbParam.substring(0, idx);
153 String vbValue = vbParam.substring(idx + 1);
154
155 ValueBinding vb =
156 context.getApplication().createValueBinding(vbExpressionString);
157 vb.setValue(context, vbValue);
158
159 return newActionParam;
160 }
161
162 public boolean getRendersChildren() {
163 return true;
164 }
165
166 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
167 RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);
168
169 List list = NavigationMenuUtils.getNavigationMenuItemList(component);
170 if (list.size() > 0) {
171 FormInfo parentFormInfo = RendererUtils.findNestingForm(component, context);
172 ResponseWriter writer = context.getResponseWriter();
173
174 if (parentFormInfo == null)
175 throw new FacesException("jscook menu is not embedded in a form.");
176 String formName = parentFormInfo.getFormName();
177 List uiNavMenuItemList = component.getChildren();
178
179
180
181
182
183
184
185
186 if (RendererUtils.isAdfOrTrinidadForm(parentFormInfo.getForm())) {
187
188 writer.write("<input type=\"hidden\" name=\"");
189 writer.write(JSCOOK_ACTION_PARAM);
190 writer.write("\" />");
191 }
192 else {
193 HtmlFormRendererBase.addHiddenCommandParameter(context, parentFormInfo.getForm(), JSCOOK_ACTION_PARAM);
194 }
195
196
197
198 String myId = getMenuId(context, component);
199
200 writer.startElement(HTML.SCRIPT_ELEM, component);
201 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
202 StringBuffer script = new StringBuffer();
203 script.append("var ").append(getMenuId(context, component)).append(" =\n[");
204 encodeNavigationMenuItems(context, script,
205 (NavigationMenuItem[]) list.toArray(new NavigationMenuItem[list.size()]),
206 uiNavMenuItemList,
207 myId, formName);
208
209 script.append("];");
210 writer.writeText(script.toString(), null);
211 writer.endElement(HTML.SCRIPT_ELEM);
212 }
213 }
214
215 private void encodeNavigationMenuItems(FacesContext context,
216 StringBuffer writer,
217 NavigationMenuItem[] items,
218 List uiNavMenuItemList,
219 String menuId, String formName)
220 throws IOException {
221 for (int i = 0; i < items.length; i++) {
222 NavigationMenuItem item = items[i];
223 Object tempObj = null;
224 UINavigationMenuItem uiNavMenuItem = null;
225 if (i < uiNavMenuItemList.size()) {
226 tempObj = uiNavMenuItemList.get(i);
227 }
228 if (tempObj != null) {
229 if (tempObj instanceof UINavigationMenuItem) {
230 uiNavMenuItem = (UINavigationMenuItem) tempObj;
231 }
232 }
233
234 if (! item.isRendered()) {
235 continue;
236 }
237
238 if (i > 0) {
239 writer.append(",\n");
240 }
241
242 if (item.isSplit()) {
243 writer.append("_cmSplit,");
244
245 if (item.getLabel().equals("0")) {
246 continue;
247 }
248 }
249
250 writer.append("[");
251 if (item.getIcon() != null) {
252 String iconSrc = context.getApplication().getViewHandler().getResourceURL(context, item.getIcon());
253 writer.append("'<img src=\"");
254 writer.append(context.getExternalContext().encodeResourceURL(iconSrc));
255 writer.append("\"/>'");
256 }
257 else {
258 writer.append("null");
259 }
260 writer.append(", '");
261 if (item.getLabel() != null) {
262 writer.append(getString(context, item.getLabel()));
263 }
264 writer.append("', ");
265 StringBuffer actionStr = new StringBuffer();
266 if ((item.getAction() != null || item.getActionListener() != null) && ! item.isDisabled()) {
267 actionStr.append("'");
268 actionStr.append(menuId);
269 if (item.getActionListener() != null) {
270 actionStr.append(":L]");
271 actionStr.append(item.getActionListener());
272 if (uiNavMenuItem != null && uiNavMenuItem.getItemValue() != null) {
273 actionStr.append(';');
274 actionStr.append(getString(context, uiNavMenuItem.getItemValue()));
275 }
276 else if (item.getValue() != null) {
277 actionStr.append(';');
278 actionStr.append(getString(context, item.getValue()));
279 }
280 }
281 if (item.getAction() != null) {
282 actionStr.append(":A]");
283 actionStr.append(item.getAction());
284 if (uiNavMenuItem != null) {
285 encodeValueBinding(actionStr, uiNavMenuItem, item);
286 }
287 }
288 actionStr.append("'");
289 writer.append(actionStr.toString());
290 }
291 else {
292 writer.append("null");
293 }
294 writer.append(", '");
295
296 writer.append(formName);
297 writer.append("', null");
298
299 if (item.isRendered() && ! item.isDisabled()) {
300
301 NavigationMenuItem[] menuItems = item.getNavigationMenuItems();
302 if (menuItems != null && menuItems.length > 0) {
303 writer.append(",");
304 if (uiNavMenuItem != null) {
305 encodeNavigationMenuItems(context, writer, menuItems,
306 uiNavMenuItem.getChildren(), menuId, formName);
307 }
308 else {
309 encodeNavigationMenuItems(context, writer, menuItems,
310 new ArrayList(1), menuId, formName);
311 }
312 }
313 }
314 writer.append("]");
315 }
316 }
317
318 private String getString(FacesContext facesContext, Object value) {
319 String str = "";
320
321 if (value != null) {
322 str = value.toString();
323 }
324
325 if (NavigationMenuUtils.isValueReference(str)) {
326 value = facesContext.getApplication().createValueBinding(str).getValue(facesContext);
327
328 if (value != null) {
329 str = value.toString();
330 }
331 else {
332 str = "";
333 }
334 }
335
336 return JavascriptUtils.encodeString(str);
337 }
338
339 private void encodeValueBinding(StringBuffer writer, UINavigationMenuItem uiNavMenuItem,
340 NavigationMenuItem item) {
341 ValueBinding vb = uiNavMenuItem.getValueBinding("NavMenuItemValue");
342 if (vb == null) {
343 return;
344 }
345 String vbExpression = vb.getExpressionString();
346 if (vbExpression == null) {
347 return;
348 }
349 Object tempObj = item.getValue();
350 if (tempObj == null) {
351 return;
352 }
353
354 writer.append(";");
355 writer.append(vbExpression);
356 writer.append("=");
357 writer.append(tempObj.toString());
358 }
359
360 public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
361 HtmlCommandJSCookMenu menu = (HtmlCommandJSCookMenu) component;
362 String theme = menu.getTheme();
363 if (theme == null) {
364
365 throw new IllegalArgumentException("theme name is mandatory for a jscookmenu.");
366 }
367
368 addResourcesToHeader(theme, menu, context);
369 }
370
371 public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
372 RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);
373 HtmlCommandJSCookMenu menu = (HtmlCommandJSCookMenu) component;
374 String theme = menu.getTheme();
375
376
377 ResponseWriter writer = context.getResponseWriter();
378
379 String menuId = getMenuId(context, component);
380
381 writer.write("<div id=\"");
382 writer.write(menuId);
383 writer.write("\"></div>\n");
384 writer.startElement(HTML.SCRIPT_ELEM, menu);
385 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
386
387 StringBuffer buf = new StringBuffer();
388 buf.append("\tif(window.cmDraw!=undefined) { cmDraw ('").
389 append(menuId).
390 append("', ").
391 append(menuId).
392 append(", '").
393 append(menu.getLayout()).
394 append("', cm").
395 append(theme).
396 append(", '").
397 append(theme).
398 append("');}");
399
400 writer.writeText(buf.toString(), null);
401 writer.endElement(HTML.SCRIPT_ELEM);
402 }
403
404 private void addResourcesToHeader(String themeName, HtmlCommandJSCookMenu menu, FacesContext context) {
405 String javascriptLocation = (String) menu.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
406 String imageLocation = (String) menu.getAttributes().get(JSFAttr.IMAGE_LOCATION);
407 String styleLocation = (String) menu.getAttributes().get(JSFAttr.STYLE_LOCATION);
408
409 AddResource addResource = AddResourceFactory.getInstance(context);
410
411 if (javascriptLocation != null) {
412 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + JSCOOK_MENU_SCRIPT);
413 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + JSCOOK_EFFECT_SCRIPT);
414 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + MYFACES_HACK_SCRIPT);
415 }
416 else {
417 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, HtmlJSCookMenuRenderer.class, JSCOOK_MENU_SCRIPT);
418 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, HtmlJSCookMenuRenderer.class, JSCOOK_EFFECT_SCRIPT);
419 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, HtmlJSCookMenuRenderer.class, MYFACES_HACK_SCRIPT);
420 }
421
422 addThemeSpecificResources(themeName, styleLocation, javascriptLocation, imageLocation, context);
423 }
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455 private void addThemeSpecificResources(String themeName, String styleLocation,
456 String javascriptLocation, String imageLocation, FacesContext context) {
457 String themeLocation = (String) builtInThemes.get(themeName);
458 if (themeLocation == null) {
459 log.debug("Unknown theme name '" + themeName + "' specified.");
460 }
461
462 AddResource addResource = AddResourceFactory.getInstance(context);
463
464 if ((imageLocation != null) || (themeLocation != null)) {
465
466
467
468
469
470
471 StringBuffer buf = new StringBuffer();
472 buf.append("var my");
473 buf.append(themeName);
474 buf.append("Base='");
475 ExternalContext externalContext = context.getExternalContext();
476 if (imageLocation != null) {
477 buf.append(externalContext.encodeResourceURL(addResource.getResourceUri(context,
478 imageLocation + "/" + themeName)));
479 }
480 else {
481 buf.append(externalContext.encodeResourceURL(addResource.getResourceUri(context,
482 HtmlJSCookMenuRenderer.class, themeLocation)));
483 }
484 buf.append("';");
485 addResource.addInlineScriptAtPosition(context, AddResource.HEADER_BEGIN, buf.toString());
486 }
487
488
489 if ((javascriptLocation != null) || (themeLocation != null)) {
490
491
492
493
494 if (javascriptLocation != null) {
495
496
497
498 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + themeName
499 + "/theme.js");
500 }
501 else {
502
503 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, HtmlJSCookMenuRenderer.class, themeName
504 + "/theme.js");
505 }
506 }
507
508 if ((styleLocation != null) || (themeLocation != null)) {
509
510
511
512
513 if (styleLocation != null) {
514 addResource.addStyleSheet(context, AddResource.HEADER_BEGIN, styleLocation + "/" + themeName + "/theme.css");
515 }
516 else {
517 addResource.addStyleSheet(context, AddResource.HEADER_BEGIN, HtmlJSCookMenuRenderer.class, themeName
518 + "/theme.css");
519 }
520 }
521 }
522
523
524
525
526
527
528
529
530 private String getMenuId(FacesContext context, UIComponent component) {
531 String menuId = component.getClientId(context).replaceAll(":", "_") + "_menu";
532 while (menuId.startsWith("_")) {
533 menuId = menuId.substring(1);
534 }
535 return menuId;
536 }
537
538 }