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; }
}