View Javadoc

1   /*
2    * Copyright 2004 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.nordija.tapestry.bayeux.form;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  import org.apache.tapestry.IActionListener;
21  import org.apache.tapestry.IBinding;
22  import org.apache.tapestry.IForm;
23  import org.apache.tapestry.IMarkupWriter;
24  import org.apache.tapestry.IRequestCycle;
25  import org.apache.tapestry.form.AbstractFormComponent;
26  
27  /**
28   * Component which renders a HTML4 button element (<button type="...">Create</button>).
29   * <p/>
30   * The implementation of this component has been heavily inspired by the standard Tapestry Button and Submit components.
31   *
32   * @author Per Olesen, www.nordija.com
33   * @version $Id: Html4Button.java 90 2007-05-18 13:53:39Z jeyben $
34   */
35  public abstract class Html4Button extends AbstractFormComponent {
36  
37      /**
38       * the value of hidden button control element which is set when button is clicked (workaround on an IE bug)
39       */
40      public static final String BUTTON_SUBMIT_VALUE = "x";
41  
42      private static final Log LOG = LogFactory.getLog(Html4Button.class);
43  
44      public abstract String getLabel();
45  
46      public abstract String getType();
47  
48      public abstract IBinding getSelectedBinding();
49  
50      public abstract boolean isDisabled();
51  
52      public abstract String getOnclickEventHandler();
53  
54      public abstract IActionListener getListener();
55  
56      public abstract Object getTag();
57  
58      protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) {
59          IForm form = getForm();
60          String buttonName = form.getElementId(this);
61          String formName = form.getName();
62  
63          if (LOG.isDebugEnabled()) {
64              LOG.debug("form=" + form + ", buttonName=" + buttonName);
65          }
66          processRendering(cycle, formName, buttonName, writer);
67      }
68  
69      protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) {
70          IForm form = getForm();
71          String buttonName = form.getElementId(this);
72         if (LOG.isDebugEnabled()) {
73              LOG.debug("rewinding: form=" + form + ", buttonName=" + buttonName);
74          }
75           processRewinding(cycle, buttonName);
76      }
77  
78      private void processRendering(IRequestCycle cycle, String formName, String buttonName, IMarkupWriter writer) {
79          //
80          // A bug in IE6 makes it submit ALL buttons on a form if one of the buttons are clicked
81          // which again makes tapestry think all buttons where clicked which results in all listeners
82          // being activated.
83          //
84          // We work around this by putting a hidden input field in form for each submit button, and
85          // letting the button set the value of this field if it is clicked, before submit.
86          // At rewind, we then dispatch on this hidden value instead of button name.
87          //
88          writer.begin("input");
89          writer.attribute("type", "hidden");
90          writer.attribute("name", getHiddenElementName(buttonName));
91          writer.end();
92  
93          writer.begin("button");
94          writer.attribute("type", getType());
95          writer.attribute("name", buttonName);
96  
97          if (isDisabled()) {
98              writer.attribute("disabled", "disabled");
99          }
100 
101         String label = getLabel();
102         if (label != null) {
103             writer.attribute("value", label);
104         }
105 
106         // this is the js code, that sets the hidden element before the button-click submits the form
107         String onclickEvent = "document." + formName + "." + getHiddenElementName(buttonName) + ".value='" + BUTTON_SUBMIT_VALUE + "'";
108 
109         // if a specific onclick handler was specified, this js code is appended
110         if ((getOnclickEventHandler() != null) && (getOnclickEventHandler().trim().length() > 0)) {
111             onclickEvent += "; " + getOnclickEventHandler();
112         }
113 
114         writer.attribute("onclick", onclickEvent);
115 
116         renderInformalParameters(writer, cycle);
117         renderBody(writer, cycle);
118         writer.end();
119     }
120 
121     private void processRewinding(IRequestCycle cycle, String buttonName) {
122         if (isDisabled()) {
123             return;
124         }
125 
126         // As with the standard Submit component, the value of the button is submitted with the
127         // form and is used to determine if we should dispatch to listener (ie. if it was this button that was clicked).
128         // A bug in IE6 makes it submit all button-elements in a form if one such is clicked, which makes
129         // dispatching on the button value break. This is worked around by letting the button set a hidden
130         // value in form and use that to determine if we should dispatch.
131         String value = cycle.getParameter(getHiddenElementName(buttonName));
132         if (value == null || (!value.equals(BUTTON_SUBMIT_VALUE))) {
133             return; // button was not clicked, submit of form was from some other component on same form
134         }
135 
136         if (LOG.isDebugEnabled()) {
137             LOG.debug("determined that it was this button '" + buttonName + "' that was clicked");
138         }
139         // copy tag over into selected paramter
140         IBinding selectedBinding = getSelectedBinding();
141         if (selectedBinding != null) {
142             selectedBinding.setObject(getTag());
143         }
144 
145         IActionListener listener = getListener();
146         if (listener != null) {
147             listener.actionTriggered(this, cycle);
148         }
149     }
150 
151     private String getHiddenElementName(String buttonName) {
152         return buttonName + "_ctrl";
153     }
154 }