Author: hlship
Date: Fri Sep  5 17:00:36 2008
New Revision: 692586

URL: http://svn.apache.org/viewvc?rev=692586&view=rev
Log:
TAPESTRY-2324: Wrong Submit 'selected' event fired when using a Form with a 
Zone parameter

Modified:
    tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt
    tapestry/tapestry5/trunk/src/site/apt/index.apt
    
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/nested/ZoneDemo.tml
    
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/ZoneDemo.java

Modified: tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt?rev=692586&r1=692585&r2=692586&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt Fri Sep  5 17:00:36 
2008
@@ -19,8 +19,8 @@
 Changes to Prototype
 
   Tapestry currently uses
-  
{{{http://www.thebungeebook.net/wp-content/uploads/2008/01/prototype-1601.js}Prototype
 1.6.0.1}}.  Version 1.6.0.2
-  appears to have some issues supporting Internet Explorer.  Tapestry's 
version is slightly patched
+  
{{{http://www.thebungeebook.net/wp-content/uploads/2008/01/prototype-1601.js}Prototype
 1.6.0.2}}.
+  Tapestry's version is slightly patched
   for {{{https://issues.apache.org/jira/browse/TAPESTRY-2108}TAPESTRY-2108}}.
 
 Changes to Scriptaculous
@@ -65,7 +65,7 @@
   ignored.  In this way, each component can add the assets it needs, without 
worrying about conflicts
   with other components.
 
-  Note that the Prototype, Scriptaculous main and effects libraries, and the 
base Tapestry library (which largely consists of
+  Note that the Prototype, Scriptaculous main and effects libraries, and the 
standard Tapestry library (which largely consists of
   support for form input validation) are included automatically.
 
   If you are need access to other Scriptaculous libraries, you can provide 
them as follows:
@@ -291,8 +291,6 @@
 
 ** Coming Soon
 
-  * zone parameter for Form components
-
   * Extending a Form with a Zone
 
   * Additional Tapestry.ElementEffect functions, plus documentation
@@ -330,3 +328,41 @@
   []
 
 
+The Standard Tapestry Library
+
+  Tapestry's client-side support, the standard Tapestry library, is the file 
tapestry.js,
+  which has dependencies on Prototype and on Scriptaculous Effects.     
tapestry.js, along with
+  its dependencies, are automatically added to the page when your code adds 
any other JavaScript or JavaScript library.
+
+* Tapestry Namespace
+
+  Tapestry defines a number of object and classes inside the Tapestry 
namespace.
+
+  It also adds a handful of methods to the Form class, and to Form elements.  
These are mostly related to
+  input validation and determining element visibility.
+
+* The Tapestry Object
+
+  The standard library adds a new function, <<<$T()>>>.  This function is used 
much like
+  Prototype's <<<$()>>>, except that instead of returning a DOM object, it 
returns a hash (an initially
+  empty JavaScript object) that is associated with the DOM object.  This hash 
is known as <the Tapestry object>.
+
+  You may pass in an object id (as a string) or an object reference. The 
Tapestry Object is created on first invocation.
+  Note: you'll see it as a property name _tapestry on the DOM object (which 
may be useful when debugging).
+
+  When Tapestry adds information to a DOM object, it does so in the Tapestry 
object. This helps avoid name
+  conflicts, and groups all Tapestry-added properties into one place which is 
much easier to debug.
+
+  For example, you might store a value for an element in one place:
+
+----
+  $T(myid).fadeDuration = .5;
+----
+
+  Then use it somewhere else:
+
+----
+  new Effect.Fade($(myId), { duration: $T(myid).fadeDuration });
+----
+
+  
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/src/site/apt/index.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/index.apt?rev=692586&r1=692585&r2=692586&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/index.apt Fri Sep  5 17:00:36 2008
@@ -84,6 +84,8 @@
 
 New And Of Note
 
+  * Tapestry now bundles Prototype version 1.6.0.2.
+
   * New methods have been added to
     {{{../apidocs/org/apache/tapestry5/dom/Node.html}Node}} to allow nodes to 
be moved about or otherwise
     manipulated.

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js?rev=692586&r1=692585&r2=692586&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
 Fri Sep  5 17:00:36 2008
@@ -16,7 +16,7 @@
 
     /** Event that allows observers to perform cross-form validation after 
individual
      *  fields have performed their validation. The form element is passed as 
the
-     *  event memo. Observers may set the form's validationError property to 
true (which
+     *  event memo. Observers may set the validationError property of the 
Form's Tapestry object to true (which
      *  will prevent form submission).
      */
     FORM_VALIDATE_EVENT : "tapestry:formvalidate",
@@ -64,9 +64,13 @@
     },
 
     /** Find all elements marked with the "t-invisible" CSS class and hide()s 
them, so that
-     * Prototype's visible() method operates correctly. This is invoked when 
the
+     * Prototype's visible() method operates correctly.                        
            In addition,
+     * finds form control elements and adds additional listeners to them to 
support
+     * form field input validation.
+     *
+     * <p>This is invoked when the
      * DOM is first loaded, and AGAIN whenever dynamic content is loaded via 
the Zone
-     * mechanism. In addition, adds a focus listener for each form element.
+     * mechanism.
      */
     onDomLoadedCallback : function()
     {
@@ -87,14 +91,35 @@
             // and we don't want to add multiple listeners to the same
             // element.
 
-            if (! element.isObservingFocusChange)
+            var t = $T(element);
+
+            if (! t.observingFocusChange)
             {
                 element.observe("focus", function()
                 {
                     document.fire(Tapestry.FOCUS_CHANGE_EVENT, element);
                 });
 
-                element.isObservingFocusChange = true;
+                t.observingFocusChange = true;
+            }
+        });
+
+        // When a submit element is clicked, record the name of the element
+        // on the associated form. This is necessary for some Ajax processing
+        // TAPESTRY-2324.
+
+        $$("INPUT[type=submit]").each(function(element)
+        {
+            var t = $T(element);
+
+            if (!t.trackingClicks)
+            {
+                element.observe("click", function()
+                {
+                    $T(element.form).lastSubmit = element;
+                });
+
+                t.trackingClicks = true;
             }
         });
     },
@@ -330,12 +355,79 @@
      */
     getFormEventManager : function(form)
     {
-        var manager = form.eventManager;
+        form = $(form);
+        var t = $T(form);
+
+        var manager = t.formEventManager;
 
         if (manager == undefined)
+        {
             manager = new Tapestry.FormEventManager(form);
+            t.formEventManager = manager;
+        }
 
         return manager;
+    },
+
+    /**
+     * Sends an Ajax request to the Form's action. This encapsulates
+     * a few things, such as a default onFailure handler, and working
+     * around bugs/features in Prototype concerning how
+     * submit buttons are processed.
+     *
+     * @param form used to define the data to be sent in the request
+     * @param options      standard Prototype Ajax Options
+     * @return Ajax.Request the Ajax.Request created for the request
+     */
+    sendAjaxRequest : function (form, options)
+    {
+        form = $(form);
+
+        // Generally, options should not be null or missing,
+        // because otherwise there's no way to provide any callbacks!
+
+        options = Object.clone(options || { });
+
+        // Set a default failure handler if none is provided.
+
+        options.onFailure |= Tapestry.ajaxFailureHandler;
+
+        // Find the elements, skipping over any submit buttons.
+        // This works around bugs in Prototype 1.6.0.2.
+
+        var elements = form.getElements().reject(function(e)
+        {
+            return e.tagName == "INPUT" && e.type == "submit";
+        });
+
+        var hash = Form.serializeElements(elements, true);
+
+        var lastSubmit = $T(form).lastSubmit;
+
+        // Put the last submit clicked into the hash, emulating
+        // what a normal form submit would do.
+
+        if (lastSubmit && lastSubmit.name)
+        {
+            hash[lastSubmit.name] = $F(lastSubmit);
+        }
+
+
+        // Copy the parameters in, overwriting field values,
+        // because Prototype 1.6.0.2 does not.
+
+        Object.extend(hash, options.parameters);
+
+        options.parameters = hash;
+
+        // We'll just assume that the form has an action; this
+        // will always be true in Tapestry.
+
+        var url = form.readAttribute('action');
+
+        // Ajax.Request will convert the hash into a query string and post it.
+
+        return new Ajax.Request(url, options);
     }
 });
 
@@ -350,17 +442,22 @@
     getFieldEventManager : function(field)
     {
         field = $(field);
+        var t = $T(field);
 
-        var manager = field.fieldEventManager;
+        var manager = t.fieldEventManager;
 
-        if (manager == undefined) manager = new 
Tapestry.FieldEventManager(field);
+        if (manager == undefined)
+        {
+            manager = new Tapestry.FieldEventManager(field);
+            t.fieldEventManager = manager;
+        }
 
         return manager;
     },
 
     /**
      * Obtains the Tapestry.FieldEventManager and asks it to show
-     * the validation message.   Sets the element's validationError property 
to true.
+     * the validation message.   Sets the  validationError property of the 
elements tapestry object to true.
      * @param element
      * @param message to display
      */
@@ -368,8 +465,8 @@
     {
         element = $(element);
 
-        element.validationError = true;
-        element.form.validationError = true;
+        $T(element).validationError = true;
+        $T(element.form).validationError = true;
 
         element.getFieldEventManager().showValidationMessage(message);
 
@@ -458,6 +555,7 @@
     formLoopRemoveLink : function(spec)
     {
         var link = $(spec.link);
+        var fragmentId = spec.fragment;
 
         link.observe("click", function(event)
         {
@@ -465,11 +563,12 @@
 
             var successHandler = function(transport)
             {
-                var container = $(spec.fragment);
+                var container = $(fragmentId);
+                var fragment = $T(container).formFragment;
 
-                if (container.formFragment != undefined)
+                if (fragment != undefined)
                 {
-                    container.formFragment.hideAndRemove();
+                    fragment.hideAndRemove();
                 }
                 else
                 {
@@ -498,16 +597,22 @@
         // Update the element with the id of zone div. This may be changed 
dynamically on the client
         // side.
 
-        element.zone = zoneDiv;
+        $T(element).zone = zoneDiv;
 
         var successHandler = function(transport)
         {
             var reply = transport.responseJSON;
 
             // Find the zone id for the element, and from there, the 
Tapestry.Zone object
-            // responsible for the zone.
+            // responsible for the zone.  This is evaluated late so that zone 
can be dynamically
+            // changed on the client.  Sorry about the confusion; there's the 
zone <div>, and
+            // there's the Tapestry.Zone object.    Perhaps should rename 
Tapestry.Zone
+            // to Tapestry.ZoneManager?
 
-            $(element.zone).zone.show(reply.content);
+            var zoneId = $T(element).zone;
+            var zoneObject = $T(zoneId).zone;
+
+            zoneObject.show(reply.content);
 
             Tapestry.processScriptInReply(reply);
         };
@@ -524,7 +629,7 @@
 
             element.observe(Tapestry.FORM_PROCESS_SUBMIT_EVENT, function()
             {
-                element.request({ onSuccess : successHandler, onFailure: 
Tapestry.ajaxFailureHandler });
+                element.sendAjaxRequest({ onSuccess : successHandler });
             });
 
             return;
@@ -601,7 +706,7 @@
         {
             $(trigger.form).observe("click", function()
             {
-                $(element).formFragment.setVisible(trigger.checked);
+                $T(element).formFragment.setVisible(trigger.checked);
             });
 
             return;
@@ -614,7 +719,7 @@
 
         trigger.observe("click", function()
         {
-            $(element).formFragment.setVisible(trigger.checked);
+            $T(element).formFragment.setVisible(trigger.checked);
         });
 
     }
@@ -862,14 +967,15 @@
     initialize : function(form)
     {
         this.form = $(form);
-        this.form.eventManager = this;
 
         this.form.onsubmit = this.handleSubmit.bindAsEventListener(this);
     },
 
     handleSubmit : function(domevent)
     {
-        this.form.validationError = false;
+        var t = $T(this.form);
+
+        t.validationError = false;
 
         var firstErrorField = null;
 
@@ -879,11 +985,13 @@
 
         this.form.getElements().each(function(element)
         {
-            if (element.fieldEventManager != undefined)
+            var fem = $T(element).fieldEventManager;
+
+            if (fem != undefined)
             {
                 // Ask the FEM to validate input for the field, which fires
                 // a number of events.
-                var error = element.fieldEventManager.validateInput();
+                var error = fem.validateInput();
 
                 if (error && ! firstErrorField)
                 {
@@ -899,13 +1007,18 @@
 
         this.form.fire(Tapestry.FORM_VALIDATE_EVENT, this.form);
 
-        if (this.form.validationError)
+        if (t.validationError)
         {
             Event.stop(domevent); // Should be domevent.stop(), but that fails 
under IE
 
             if (firstErrorField) firstErrorField.activate();
 
-            return;
+            // Because the submission failed, the last submit property is 
cleared,
+            // since the form may be submitted for some other reason later.
+
+            t.lastSubmit = null;
+
+            return false;
         }
 
         this.form.fire(Tapestry.FORM_PREPARE_FOR_SUBMIT_EVENT, this.form);
@@ -926,6 +1039,10 @@
 
             return false;
         }
+
+        // Validation is OK, not doing Ajax, continue as planned.
+
+        return true;
     }
 });
 
@@ -934,7 +1051,6 @@
     initialize : function(field)
     {
         this.field = $(field);
-        this.field.fieldEventManager = this;
 
         var id = this.field.id;
         this.label = $(id + ':label');
@@ -1005,13 +1121,15 @@
 
         if (! this.field.isDeepVisible()) return;
 
-        this.field.validationError = false;
+        var t = $T(this.field);
+
+        t.validationError = false;
 
         this.field.fire(Tapestry.FIELD_FORMAT_EVENT, this.field);
 
         // If Format went ok, perhaps do the other validations.
 
-        if (! this.field.validationError)
+        if (! t.validationError)
         {
             var value = $F(this.field);
 
@@ -1021,10 +1139,10 @@
 
         // Lastly, if no validation errors were found, remove the decorations.
 
-        if (! this.field.validationError)
+        if (! t.validationError)
             this.field.removeDecorations();
 
-        return this.field.validationError;
+        return t.validationError;
     }
 });
 
@@ -1078,7 +1196,7 @@
 
         // Link the div back to this zone.
 
-        this.element.zone = this;
+        $T(this.element).zone = this;
 
         // Look inside the Zone element for an element with the CSS class 
"t-zone-update".
         // If present, then this is the elements whose content will be 
changed, rather
@@ -1088,7 +1206,7 @@
 
         var updates = this.element.select(".t-zone-update");
 
-        this.updateElement = updates.length == 0 ? this.element : updates[0];
+        this.updateElement = updates.first() || this.element;
     },
 
     // Updates the content of the div controlled by this Zone, then
@@ -1116,7 +1234,7 @@
 
         this.element = $(spec.element);
 
-        this.element.formFragment = this;
+        $T(this.element).formFragment = this;
 
         this.hidden = $(spec.element + ":hidden");
 
@@ -1197,8 +1315,7 @@
                 // Insert the new element before or after the existing element.
 
                 var param = { };
-                var key = this.below ? "after" : "before";
-                param[key] = newElement;
+                param[this.below ? "after" : "before"] = newElement;
 
                 // Add the new element with the downloaded content.
 
@@ -1397,4 +1514,31 @@
     }
 };
 
+/**
+ * In the spirit of $(), $T() exists to access the <em>Tapestry object</em> 
for the element. The Tapestry object
+ * is used to store additional values related to the element; it is simply an 
annoymous object stored as property
+ * <code>_tapestry</code> of the element, created the first time it is 
accessed.
+ * <p>This mechanism acts as a namespace, and so helps prevent name
+ * conflicts that would occur if properties were stored directly on DOM 
elements, and makes debugging a bit easier (the Tapestry-specific
+ * properties are all in one place!).
+ * For the moment,
+ * added methods stored directly on the object, and are not prefixed in any 
way, valuing readability over preventing naming conflicts.
+ *
+ * @param element an element instance or element id
+ * @return object Tapestry object for the element
+ */
+function $T(element)
+{
+    var e = $(element);
+    var t = e._tapestry;
+
+    if (!t)
+    {
+        t = { };
+        e._tapestry = t;
+    }
+
+    return t;
+}
+
 Tapestry.onDOMLoaded(Tapestry.onDomLoadedCallback);

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/app1/nested/ZoneDemo.tml
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/nested/ZoneDemo.tml?rev=692586&r1=692585&r2=692586&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/nested/ZoneDemo.tml 
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/nested/ZoneDemo.tml 
Fri Sep  5 17:00:36 2008
@@ -17,7 +17,7 @@
     <t:block id="registrationForm">
 
 
-        <t:beaneditform t:id="form" object="registration" zone="output" 
add="roles">
+        <t:beaneditform t:id="registrationForm" object="registration" 
zone="output" add="roles">
 
             <t:parameter name="roles">
                 <t:palette selected="registration.roles" encoder="encoder" 
model="literal:guest,user,admin"/>
@@ -37,6 +37,19 @@
         </t:beandisplay>
     </t:block>
 
+    <t:block id="voteForm">
+        <t:form t:id="vote" zone="output">
+            Vote:
+            <input type="submit" name="abstain" value="Abstain"/>
+            <t:submit t:id="voteYes" value="Yes"/>
+            <t:submit t:id="voteNo" value="No"/>
+        </t:form>
+    </t:block>
+
+    <t:block id="voteOutput">
+        You voted: ${vote}
+    </t:block>
+
 
     <ul>
         <li t:type="loop" source="names" value="name">

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/ZoneDemo.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/ZoneDemo.java?rev=692586&r1=692585&r2=692586&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/ZoneDemo.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/ZoneDemo.java
 Fri Sep  5 17:00:36 2008
@@ -19,6 +19,7 @@
 import org.apache.tapestry5.annotations.ApplicationState;
 import org.apache.tapestry5.annotations.Component;
 import org.apache.tapestry5.annotations.Log;
+import org.apache.tapestry5.annotations.Property;
 import org.apache.tapestry5.corelib.components.BeanEditForm;
 import org.apache.tapestry5.integration.app1.data.RegistrationData;
 import org.apache.tapestry5.internal.services.StringValueEncoder;
@@ -29,24 +30,21 @@
 
 public class ZoneDemo
 {
-    @Component
-    private BeanEditForm form;
+    @Component(id = "registrationForm")
+    private BeanEditForm regform;
 
     private String name;
 
     @ApplicationState
     private RegistrationData registration;
 
-    private static final String[] NAMES = { "Fred & Wilma", "Mr. <Roboto>", 
"Grim Fandango", "Registration" };
+    private static final String[] NAMES = {"Fred & Wilma", "Mr. <Roboto>", 
"Grim Fandango", "Registration", "Vote"};
 
     @Inject
-    private Block showName;
+    private Block showName, registrationForm, registrationOutput, voteForm, 
voteOutput;
 
-    @Inject
-    private Block registrationForm;
-
-    @Inject
-    private Block registrationOutput;
+    @Property
+    private String vote;
 
     public String[] getNames()
     {
@@ -71,17 +69,19 @@
 
         if (name.equals("Registration")) return registrationForm;
 
+        if (name.equals("Vote")) return voteForm;
+
         return showName;
     }
 
-    Object onSuccess()
+    Object onSuccessFromRegistrationForm()
     {
         return registrationOutput;
     }
 
     Object onActionFromClear()
     {
-        form.clearErrors();
+        regform.clearErrors();
         registration = null;
 
         return registrationForm;
@@ -110,4 +110,10 @@
     {
         return new StringValueEncoder();
     }
+
+    void onSelectedFromVoteYes() { vote = "Yes"; }
+
+    void onSelectedFromVoteNo() { vote = "No"; }
+
+    Object onSuccessFromVote() { return voteOutput; }
 }


Reply via email to