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 java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.StringTokenizer;
26
27 import javax.faces.FacesException;
28 import javax.faces.application.Resource;
29 import javax.faces.application.ResourceHandler;
30 import javax.faces.component.UIComponent;
31 import javax.faces.context.ExternalContext;
32 import javax.faces.context.FacesContext;
33 import javax.faces.context.ResponseWriter;
34 import javax.faces.el.MethodBinding;
35 import javax.faces.el.ValueBinding;
36 import javax.faces.event.ActionEvent;
37 import javax.faces.event.ComponentSystemEvent;
38 import javax.faces.event.ComponentSystemEventListener;
39 import javax.faces.event.ListenerFor;
40
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43 import org.apache.myfaces.component.LibraryLocationAware;
44 import org.apache.myfaces.custom.navmenu.NavigationMenuItem;
45 import org.apache.myfaces.custom.navmenu.NavigationMenuUtils;
46 import org.apache.myfaces.custom.navmenu.UINavigationMenuItem;
47 import org.apache.myfaces.renderkit.html.util.AddResource;
48 import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
49 import org.apache.myfaces.shared_tomahawk.el.SimpleActionMethodBinding;
50 import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
51 import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
52 import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
53 import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlFormRendererBase;
54 import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRenderer;
55 import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;
56 import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
57 import org.apache.myfaces.tomahawk.application.PreRenderViewAddResourceEvent;
58 import org.apache.myfaces.tomahawk.util.TomahawkResourceUtils;
59
60
61
62
63
64
65
66
67
68
69 @ListenerFor(systemEventClass=PreRenderViewAddResourceEvent.class)
70 public class HtmlJSCookMenuRenderer
71 extends HtmlRenderer implements ComponentSystemEventListener
72 {
73 private static final String MYFACES_HACK_SCRIPT = "MyFacesHack.js";
74
75 private static final String JSCOOK_MENU_SCRIPT = "JSCookMenu.js";
76
77 private static final String JSCOOK_EFFECT_SCRIPT = "effect.js";
78
79 private static final Log log = LogFactory.getLog(HtmlJSCookMenuRenderer.class);
80
81 private static final String JSCOOK_ACTION_PARAM = "jscook_action";
82 private static final Class[] ACTION_LISTENER_ARGS = {ActionEvent.class};
83
84 private static final Map builtInThemes = new java.util.HashMap();
85
86 static {
87 builtInThemes.put("ThemeOffice", "ThemeOffice/");
88 builtInThemes.put("ThemeMiniBlack", "ThemeMiniBlack/");
89 builtInThemes.put("ThemeIE", "ThemeIE/");
90 builtInThemes.put("ThemePanel", "ThemePanel/");
91 builtInThemes.put("ThemeGray", "ThemeGray/");
92 }
93
94 public void processEvent(ComponentSystemEvent event)
95 {
96 HtmlCommandJSCookMenu menu = (HtmlCommandJSCookMenu) event.getComponent();
97 String theme = menu.getTheme();
98 if (theme == null) {
99
100 throw new IllegalArgumentException("theme name is mandatory for a jscookmenu.");
101 }
102
103 addResourcesToHeaderWithJSF2ResourceAPI(theme, menu, FacesContext.getCurrentInstance());
104 }
105
106 public void decode(FacesContext context, UIComponent component) {
107 RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);
108
109 Map parameter = context.getExternalContext().getRequestParameterMap();
110 String actionParam = (String) parameter.get(JSCOOK_ACTION_PARAM);
111 if (actionParam != null && !actionParam.trim().equals("") &&
112 !actionParam.trim().equals("null")) {
113 String compId = getMenuId(context, component);
114 StringTokenizer tokenizer = new StringTokenizer(actionParam, ":");
115 if (tokenizer.countTokens() > 1) {
116 String actionId = tokenizer.nextToken();
117 if (! compId.equals(actionId)) {
118 return;
119 }
120 while (tokenizer.hasMoreTokens()) {
121 String action = tokenizer.nextToken();
122 if (action.startsWith("A]")) {
123 action = action.substring(2, action.length());
124 action = decodeValueBinding(action, context);
125 MethodBinding mb;
126 if (NavigationMenuUtils.isValueReference(action)) {
127 mb = context.getApplication().createMethodBinding(action, null);
128 }
129 else {
130 mb = new SimpleActionMethodBinding(action);
131 }
132 ((HtmlCommandJSCookMenu) component).setAction(mb);
133 }
134 else if (action.startsWith("L]")) {
135 action = action.substring(2, action.length());
136 String value = null;
137 int idx = action.indexOf(";");
138 if (idx > 0 && idx < action.length() - 1) {
139 value = action.substring(idx + 1, action.length());
140 action = action.substring(0, idx);
141 ((HtmlCommandJSCookMenu) component).setValue(value);
142 }
143 else if (idx == action.length() - 1)
144 {
145 value = null;
146 action = action.substring(0, idx);
147 }
148 MethodBinding mb;
149 if (NavigationMenuUtils.isValueReference(action)) {
150 mb = context.getApplication().createMethodBinding(action, ACTION_LISTENER_ARGS);
151 ((HtmlCommandJSCookMenu) component).setActionListener(mb);
152 if (value != null)
153 ((HtmlCommandJSCookMenu) component).setValue(value);
154 }
155 }
156 }
157 }
158 component.queueEvent(new ActionEvent(component));
159 }
160 }
161
162 private String decodeValueBinding(String actionParam, FacesContext context) {
163 int idx = actionParam.indexOf(";#{");
164 if (idx == -1) {
165 return actionParam;
166 }
167
168 String newActionParam = actionParam.substring(0, idx);
169 String vbParam = actionParam.substring(idx + 1);
170
171 idx = vbParam.indexOf('=');
172 if (idx == -1) {
173 return newActionParam;
174 }
175 String vbExpressionString = vbParam.substring(0, idx);
176 String vbValue = vbParam.substring(idx + 1);
177
178 ValueBinding vb =
179 context.getApplication().createValueBinding(vbExpressionString);
180 vb.setValue(context, vbValue);
181
182 return newActionParam;
183 }
184
185 public boolean getRendersChildren() {
186 return true;
187 }
188
189 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
190 RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);
191
192 List list = NavigationMenuUtils.getNavigationMenuItemList(component);
193 if (list.size() > 0) {
194 FormInfo parentFormInfo = RendererUtils.findNestingForm(component, context);
195 ResponseWriter writer = context.getResponseWriter();
196
197 if (parentFormInfo == null)
198 throw new FacesException("jscook menu is not embedded in a form.");
199 String formName = parentFormInfo.getFormName();
200 List uiNavMenuItemList = component.getChildren();
201
202
203
204
205
206
207
208
209 if (RendererUtils.isAdfOrTrinidadForm(parentFormInfo.getForm())) {
210
211 writer.write("<input type=\"hidden\" name=\"");
212 writer.write(JSCOOK_ACTION_PARAM);
213 writer.write("\" />");
214 }
215 else {
216 HtmlFormRendererBase.addHiddenCommandParameter(context, parentFormInfo.getForm(), JSCOOK_ACTION_PARAM);
217 }
218
219
220
221 String myId = getMenuId(context, component);
222
223 writer.startElement(HTML.SCRIPT_ELEM, component);
224 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
225 StringBuffer script = new StringBuffer();
226 script.append("var ").append(getMenuId(context, component)).append(" =\n[");
227 encodeNavigationMenuItems(context, script,
228 (NavigationMenuItem[]) list.toArray(new NavigationMenuItem[list.size()]),
229 uiNavMenuItemList,
230 myId, formName);
231
232 script.append("];");
233 writer.writeText(script.toString(), null);
234 writer.endElement(HTML.SCRIPT_ELEM);
235 }
236 }
237
238 private void encodeNavigationMenuItems(FacesContext context,
239 StringBuffer writer,
240 NavigationMenuItem[] items,
241 List uiNavMenuItemList,
242 String menuId, String formName)
243 throws IOException {
244 for (int i = 0; i < items.length; i++) {
245 NavigationMenuItem item = items[i];
246 Object tempObj = null;
247 UINavigationMenuItem uiNavMenuItem = null;
248 if (i < uiNavMenuItemList.size()) {
249 tempObj = uiNavMenuItemList.get(i);
250 }
251 if (tempObj != null) {
252 if (tempObj instanceof UINavigationMenuItem) {
253 uiNavMenuItem = (UINavigationMenuItem) tempObj;
254 }
255 }
256
257 if (! item.isRendered()) {
258 continue;
259 }
260
261 if (i > 0) {
262 writer.append(",\n");
263 }
264
265 if (item.isSplit()) {
266 writer.append("_cmSplit,");
267
268 if (item.getLabel().equals("0")) {
269 continue;
270 }
271 }
272
273 writer.append("[");
274 if (item.getIcon() != null) {
275 String iconSrc = context.getApplication().getViewHandler().getResourceURL(context, item.getIcon());
276 writer.append("'<img src=\"");
277 writer.append(context.getExternalContext().encodeResourceURL(iconSrc));
278 writer.append("\"/>'");
279 }
280 else {
281 writer.append("null");
282 }
283 writer.append(", '");
284 if (item.getLabel() != null) {
285 writer.append(getString(context, item.getLabel()));
286 }
287 writer.append("', ");
288 StringBuffer actionStr = new StringBuffer();
289 if ((item.getAction() != null || item.getActionListener() != null) && ! item.isDisabled()) {
290 actionStr.append("'");
291 actionStr.append(menuId);
292 if (item.getActionListener() != null) {
293 actionStr.append(":L]");
294 actionStr.append(item.getActionListener());
295 if (uiNavMenuItem != null && uiNavMenuItem.getItemValue() != null) {
296 actionStr.append(';');
297 actionStr.append(getString(context, uiNavMenuItem.getItemValue()));
298 }
299 else if (item.getValue() != null) {
300 actionStr.append(';');
301 actionStr.append(getString(context, item.getValue()));
302 }
303 }
304 if (item.getAction() != null) {
305 actionStr.append(":A]");
306 actionStr.append(item.getAction());
307 if (uiNavMenuItem != null) {
308 encodeValueBinding(actionStr, uiNavMenuItem, item);
309 }
310 }
311 actionStr.append("'");
312 writer.append(actionStr.toString());
313 }
314 else {
315 writer.append("null");
316 }
317 writer.append(", '");
318
319 writer.append(formName);
320 writer.append("', null");
321
322 if (item.isRendered() && ! item.isDisabled()) {
323
324 NavigationMenuItem[] menuItems = item.getNavigationMenuItems();
325 if (menuItems != null && menuItems.length > 0) {
326 writer.append(",");
327 if (uiNavMenuItem != null) {
328 encodeNavigationMenuItems(context, writer, menuItems,
329 uiNavMenuItem.getChildren(), menuId, formName);
330 }
331 else {
332 encodeNavigationMenuItems(context, writer, menuItems,
333 new ArrayList(1), menuId, formName);
334 }
335 }
336 }
337 writer.append("]");
338 }
339 }
340
341 private String getString(FacesContext facesContext, Object value) {
342 String str = "";
343
344 if (value != null) {
345 str = value.toString();
346 }
347
348 if (NavigationMenuUtils.isValueReference(str)) {
349 value = facesContext.getApplication().createValueBinding(str).getValue(facesContext);
350
351 if (value != null) {
352 str = value.toString();
353 }
354 else {
355 str = "";
356 }
357 }
358
359 return JavascriptUtils.encodeString(str);
360 }
361
362 private void encodeValueBinding(StringBuffer writer, UINavigationMenuItem uiNavMenuItem,
363 NavigationMenuItem item) {
364 ValueBinding vb = uiNavMenuItem.getValueBinding("NavMenuItemValue");
365 if (vb == null) {
366 return;
367 }
368 String vbExpression = vb.getExpressionString();
369 if (vbExpression == null) {
370 return;
371 }
372 Object tempObj = item.getValue();
373 if (tempObj == null) {
374 return;
375 }
376
377 writer.append(";");
378 writer.append(vbExpression);
379 writer.append("=");
380 writer.append(tempObj.toString());
381 }
382
383 public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
384
385 HtmlCommandJSCookMenu menu = (HtmlCommandJSCookMenu) component;
386 String theme = menu.getTheme();
387 if (theme == null) {
388
389 throw new IllegalArgumentException("theme name is mandatory for a jscookmenu.");
390 }
391
392 addResourcesToHeader(theme, menu, context);
393
394 }
395
396 public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
397 RendererUtils.checkParamValidity(context, component, HtmlCommandJSCookMenu.class);
398 HtmlCommandJSCookMenu menu = (HtmlCommandJSCookMenu) component;
399 String theme = menu.getTheme();
400
401
402 ResponseWriter writer = context.getResponseWriter();
403
404 String menuId = getMenuId(context, component);
405
406 writer.write("<div id=\"");
407 writer.write(menuId);
408 writer.write("\"></div>\n");
409 writer.startElement(HTML.SCRIPT_ELEM, menu);
410 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
411
412 StringBuffer buf = new StringBuffer();
413 buf.append("\tif(window.cmDraw!=undefined) { cmDraw ('").
414 append(menuId).
415 append("', ").
416 append(menuId).
417 append(", '").
418 append(menu.getLayout()).
419 append("', cm").
420 append(theme).
421 append(", '").
422 append(theme).
423 append("');}");
424
425 writer.writeText(buf.toString(), null);
426 writer.endElement(HTML.SCRIPT_ELEM);
427 }
428
429 private void addResourcesToHeaderWithJSF2ResourceAPI(String themeName, HtmlCommandJSCookMenu menu, FacesContext context) {
430 String javascriptLocation = (String) menu.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
431 String imageLocation = (String) menu.getAttributes().get(JSFAttr.IMAGE_LOCATION);
432 String styleLocation = (String) menu.getAttributes().get(JSFAttr.STYLE_LOCATION);
433 String javascriptLibrary = (String) menu.getAttributes().get(LibraryLocationAware.JAVASCRIPT_LIBRARY_ATTR);
434 String imageLibrary = (String) menu.getAttributes().get(LibraryLocationAware.IMAGE_LIBRARY_ATTR);
435 String styleLibrary = (String) menu.getAttributes().get(LibraryLocationAware.STYLE_LIBRARY_ATTR);
436
437
438
439
440
441
442
443
444
445 if (javascriptLocation == null)
446 {
447 if (javascriptLibrary == null)
448 {
449
450 TomahawkResourceUtils.addOutputScriptResource(context, "oam.custom.navmenu.jscookmenu", JSCOOK_MENU_SCRIPT);
451
452 TomahawkResourceUtils.addOutputScriptResource(context, "oam.custom.navmenu.jscookmenu", JSCOOK_EFFECT_SCRIPT);
453
454 TomahawkResourceUtils.addOutputScriptResource(context, "oam.custom.navmenu.jscookmenu", MYFACES_HACK_SCRIPT);
455 }
456 else
457 {
458 TomahawkResourceUtils.addOutputScriptResource(context, javascriptLibrary, JSCOOK_MENU_SCRIPT);
459 TomahawkResourceUtils.addOutputScriptResource(context, javascriptLibrary, JSCOOK_EFFECT_SCRIPT);
460 TomahawkResourceUtils.addOutputScriptResource(context, javascriptLibrary, MYFACES_HACK_SCRIPT);
461 }
462 }
463
464 addThemeSpecificResourcesWithJSF2ResourceAPI(themeName, styleLocation, javascriptLocation, imageLocation,
465 styleLibrary, javascriptLibrary, imageLibrary, context);
466 }
467
468 private void addResourcesToHeader(String themeName, HtmlCommandJSCookMenu menu, FacesContext context) {
469 String javascriptLocation = (String) menu.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
470 String imageLocation = (String) menu.getAttributes().get(JSFAttr.IMAGE_LOCATION);
471 String styleLocation = (String) menu.getAttributes().get(JSFAttr.STYLE_LOCATION);
472 String javascriptLibrary = (String) menu.getAttributes().get(LibraryLocationAware.JAVASCRIPT_LIBRARY_ATTR);
473 String imageLibrary = (String) menu.getAttributes().get(LibraryLocationAware.IMAGE_LIBRARY_ATTR);
474 String styleLibrary = (String) menu.getAttributes().get(LibraryLocationAware.STYLE_LIBRARY_ATTR);
475
476 AddResource addResource = AddResourceFactory.getInstance(context);
477
478 if (javascriptLocation != null) {
479 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + JSCOOK_MENU_SCRIPT);
480 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + JSCOOK_EFFECT_SCRIPT);
481 addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, javascriptLocation + "/" + MYFACES_HACK_SCRIPT);
482 }
483
484 addThemeSpecificResources(themeName, styleLocation, javascriptLocation, imageLocation,
485 styleLibrary, javascriptLibrary, imageLibrary, context);
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518 private void addThemeSpecificResourcesWithJSF2ResourceAPI(String themeName, String styleLocation,
519 String javascriptLocation, String imageLocation,
520 String styleLibrary, String javascriptLibrary,
521 String imageLibrary, FacesContext context) {
522 String themeLocation = (String) builtInThemes.get(themeName);
523 if (themeLocation == null) {
524 log.debug("Unknown theme name '" + themeName + "' specified.");
525 }
526
527
528
529 if ((imageLocation != null) || (themeLocation != null)) {
530
531
532
533
534
535
536 StringBuffer buf = new StringBuffer();
537 buf.append("var my");
538 buf.append(themeName);
539 buf.append("Base='");
540 ExternalContext externalContext = context.getExternalContext();
541
542
543
544
545
546
547
548 if (imageLocation == null)
549 {
550
551
552 Resource resource = context.getApplication().getResourceHandler().
553 createResource(";j","oam.custom.navmenu.jscookmenu."+themeName);
554 buf.append(resource.getRequestPath());
555 buf.append("';");
556 TomahawkResourceUtils.addInlineOutputScriptResource(context, TomahawkResourceUtils.HEAD_LOCATION, buf.toString());
557 }
558 }
559 else
560 {
561 if ((imageLibrary != null))
562 {
563 StringBuffer buf = new StringBuffer();
564 buf.append("var my");
565 buf.append(themeName);
566 buf.append("Base='");
567 Resource resource = context.getApplication().getResourceHandler().
568 createResource(";j",imageLibrary+'.'+themeName);
569 buf.append(resource.getRequestPath());
570 buf.append("';");
571
572 TomahawkResourceUtils.addInlineOutputScriptResource(context, TomahawkResourceUtils.HEAD_LOCATION, buf.toString());
573 }
574 }
575
576 if ((javascriptLocation != null) || (themeLocation != null)) {
577
578
579
580
581
582
583
584
585
586
587
588
589 if (javascriptLocation == null)
590 {
591
592
593
594 TomahawkResourceUtils.addOutputScriptResource(context, "oam.custom.navmenu.jscookmenu."+themeName, "theme.js");
595 }
596 }
597 else
598 {
599 if ((javascriptLibrary != null))
600 {
601 TomahawkResourceUtils.addOutputScriptResource(context, javascriptLibrary+'.'+themeName, "theme.js");
602 }
603 }
604
605 if ((styleLocation != null) || (themeLocation != null)) {
606
607
608
609
610
611
612
613
614 if (styleLocation == null)
615 {
616
617
618 TomahawkResourceUtils.addOutputStylesheetResource(context, "oam.custom.navmenu.jscookmenu."+themeName, "theme.css");
619 }
620 }
621 else
622 {
623 if ((styleLibrary != null))
624 {
625 TomahawkResourceUtils.addOutputStylesheetResource(context, styleLibrary+'.'+themeName, "theme.css");
626 }
627 }
628 }
629
630 private void addThemeSpecificResources(String themeName,
631 String styleLocation, String javascriptLocation,
632 String imageLocation, String styleLibrary,
633 String javascriptLibrary, String imageLibrary, FacesContext context)
634 {
635 String themeLocation = (String) builtInThemes.get(themeName);
636 if (themeLocation == null)
637 {
638 log.debug("Unknown theme name '" + themeName + "' specified.");
639 }
640
641 AddResource addResource = AddResourceFactory.getInstance(context);
642
643 if ((imageLocation != null) || (themeLocation != null))
644 {
645
646
647
648
649
650
651 StringBuffer buf = new StringBuffer();
652 buf.append("var my");
653 buf.append(themeName);
654 buf.append("Base='");
655 ExternalContext externalContext = context.getExternalContext();
656 if (imageLocation != null)
657 {
658 buf.append(externalContext.encodeResourceURL(addResource
659 .getResourceUri(context, imageLocation + "/"
660 + themeName)));
661 buf.append("';");
662 addResource.addInlineScriptAtPosition(context,
663 AddResource.HEADER_BEGIN, buf.toString());
664 }
665 }
666
667 if ((javascriptLocation != null) || (themeLocation != null))
668 {
669
670
671
672
673 if (javascriptLocation != null)
674 {
675
676
677
678 addResource.addJavaScriptAtPosition(context,
679 AddResource.HEADER_BEGIN, javascriptLocation + "/"
680 + themeName + "/theme.js");
681 }
682 }
683
684 if ((styleLocation != null) || (themeLocation != null))
685 {
686
687
688
689
690 if (styleLocation != null)
691 {
692 addResource.addStyleSheet(context, AddResource.HEADER_BEGIN,
693 styleLocation + "/" + themeName + "/theme.css");
694 }
695 }
696 }
697
698
699
700
701
702
703
704
705 private String getMenuId(FacesContext context, UIComponent component) {
706 String menuId = component.getClientId(context).replaceAll(":", "_") + "_menu";
707 while (menuId.startsWith("_")) {
708 menuId = menuId.substring(1);
709 }
710 return menuId;
711 }
712 }