You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2008/02/28 19:52:56 UTC

svn commit: r632085 [1/2] - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/corelib/base/ main/java/org/apache/tapestry/corelib/components/ main/java/org/apache/tapestry/corelib/data/ main/ja...

Author: hlship
Date: Thu Feb 28 10:52:45 2008
New Revision: 632085

URL: http://svn.apache.org/viewvc?rev=632085&view=rev
Log:
TAPESTRY-1650: Add support for injecting (via Ajax) new content into an existing client-side form

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormInjector.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/SubmitNotifier.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/data/InsertPosition.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupport.java
      - copied, changed from r630031, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetup.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java
      - copied, changed from r630031, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetupImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/FormInjectorDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/FormInjectorDemo.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImplTest.java
      - copied, changed from r630031, tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ZoneSetupImplTest.java
Removed:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxUIDManager.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxUIDManagerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetup.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetupImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/AjaxUIDManagerImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ZoneSetupImplTest.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResources.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/PageRenderSupport.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractComponentEventLink.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Form.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormFragment.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Loop.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PageLink.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Radio.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Zone.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/FormSupportImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueue.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueueImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderSupportImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PartialRenderPageRenderSupport.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FormSupport.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/StringToEnumCoercion.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/tapestry.js
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/FormSupportImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/components/Any.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResources.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResources.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResources.java Thu Feb 28 10:52:45 2008
@@ -192,4 +192,11 @@
      * <em>next</em> request (the attached page instance is not affected).
      */
     void discardPersistentFieldChanges();
+
+    /**
+     * Returns the name of element that represents the component in its template, or null if not known.
+     *
+     * @return the element name or null
+     */
+    String getElementName();
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java Thu Feb 28 10:52:45 2008
@@ -53,6 +53,18 @@
     /**
      * A convienience for invoking {@link #triggerContextEvent(String, EventContext , ComponentEventCallback)}. Wraps
      * the context values into an {@link org.apache.tapestry.EventContext}.
+     *
+     * @param eventType event type (as determined from the request, or otherwise by design)
+     * @param context   Values that may be provided to the event handler method as method parameters, or null if no
+     *                  context values are available
+     * @param callback  the handler to be informed of the result, or null if the event is a notification that does not
+     *                  support return values from event handler methods (the value true is allowed even if the handler
+     *                  is null).
+     * @return true if any event handler was invoked (even if no event handler method returns a non-null value)
+     * @throws org.apache.tapestry.runtime.ComponentEventException
+     *          if an event handler method throws a checked or unchecked exception
+     * @see org.apache.tapestry.internal.transform.OnEventWorker
+     * @see org.apache.tapestry.annotations.OnEvent
      */
     boolean triggerEvent(String eventType, Object[] contextValues, ComponentEventCallback callback);
 
@@ -97,12 +109,13 @@
     Locale getLocale();
 
     /**
-     * Returns the name of element that represents the component in its template, or null if the element was a component
-     * type (in the Tapestry namespace).
+     * Returns the name of element that represents the component in its template, or the provided default element name
+     * if the element was a component type (in the Tapestry namespace).
      *
+     * @param defaultElementName element name to return if the element name is not known (may be null)
      * @return the element name
      */
-    String getElementName();
+    String getElementName(String defaultElementName);
 
     /**
      * Returns a block from the component's template, referenced by its id.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/PageRenderSupport.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/PageRenderSupport.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/PageRenderSupport.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/PageRenderSupport.java Thu Feb 28 10:52:45 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
 
 package org.apache.tapestry;
 
-import org.apache.tapestry.ioc.internal.util.IdAllocator;
 import org.apache.tapestry.ioc.services.SymbolSource;
 import org.apache.tapestry.services.AssetSource;
 
@@ -30,10 +29,18 @@
      * precisely match the input value (an underscore and a unique index value may be appended).
      *
      * @param id the component id from which a unique id will be generated
-     * @return a unqiue id for this rendering of the page
-     * @see IdAllocator
+     * @return a unique id for this rendering of the page
+     * @see org.apache.tapestry.ioc.internal.util.IdAllocator
      */
     String allocateClientId(String id);
+
+    /**
+     * As with {@link #allocateClientId(String)} but uses the id of the component extracted
+     * from the resources.
+     * @param resources of the component which requires an id
+     * @return a unique id for this rendering of the page
+     */
+    String allocateClientId(ComponentResources resources);
 
     /**
      * Adds one or more new script assets to the page. Assets are added uniquely, and appear as

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractComponentEventLink.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractComponentEventLink.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractComponentEventLink.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractComponentEventLink.java Thu Feb 28 10:52:45 2008
@@ -17,7 +17,7 @@
 import org.apache.tapestry.*;
 import org.apache.tapestry.annotations.Environmental;
 import org.apache.tapestry.annotations.Parameter;
-import org.apache.tapestry.internal.services.ZoneSetup;
+import org.apache.tapestry.internal.services.ClientBehaviorSupport;
 import org.apache.tapestry.ioc.annotations.Inject;
 
 import java.util.List;
@@ -49,13 +49,13 @@
     private PageRenderSupport _support;
 
     @Environmental
-    private ZoneSetup _zoneSetup;
+    private ClientBehaviorSupport _clientBehaviorSupport;
 
     void beginRender(MarkupWriter writer)
     {
         if (isDisabled()) return;
 
-        String clientId = _support.allocateClientId(_resources.getId());
+        String clientId = _support.allocateClientId(_resources);
 
         Object[] contextArray = _context == null ? new Object[0] : _context.toArray();
 
@@ -63,7 +63,7 @@
 
         writeLink(writer, clientId, link);
 
-        if (_zone != null) _zoneSetup.linkZone(clientId, _zone);
+        if (_zone != null) _clientBehaviorSupport.linkZone(clientId, _zone);
     }
 
     /**

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Form.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Form.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Form.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Form.java Thu Feb 28 10:52:45 2008
@@ -22,10 +22,10 @@
 import org.apache.tapestry.corelib.internal.FormSupportImpl;
 import org.apache.tapestry.corelib.mixins.RenderInformals;
 import org.apache.tapestry.dom.Element;
+import org.apache.tapestry.internal.services.ClientBehaviorSupport;
 import org.apache.tapestry.internal.services.ComponentInvocationMap;
 import org.apache.tapestry.internal.services.ComponentResultProcessorWrapper;
 import org.apache.tapestry.internal.services.HeartbeatImpl;
-import org.apache.tapestry.internal.services.ZoneSetup;
 import org.apache.tapestry.internal.util.Base64ObjectInputStream;
 import org.apache.tapestry.internal.util.Base64ObjectOutputStream;
 import org.apache.tapestry.ioc.Location;
@@ -38,7 +38,6 @@
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.ObjectInputStream;
-import static java.lang.String.format;
 import java.util.List;
 
 /**
@@ -180,7 +179,7 @@
     private ComponentEventResultProcessor _componentEventResultProcessor;
 
     @Environmental
-    private ZoneSetup _zoneSetup;
+    private ClientBehaviorSupport _clientBehaviorSupport;
 
     private String _name;
 
@@ -208,17 +207,18 @@
             throw new RuntimeException(ex);
         }
 
-        _name = _pageRenderSupport.allocateClientId(_resources.getId());
+        _name = _pageRenderSupport.allocateClientId(_resources);
 
-        _formSupport = new FormSupportImpl(_name, _actions);
+        _formSupport = new FormSupportImpl(_name, _actions, _clientBehaviorSupport, _clientValidation);
 
-        if (_zone != null) _zoneSetup.linkZone(_name, _zone);
+        if (_zone != null) _clientBehaviorSupport.linkZone(_name, _zone);
 
         // TODO: Forms should not allow to nest. Perhaps a set() method instead of a push() method
         // for this kind of check?  
 
         _environment.push(FormSupport.class, _formSupport);
         _environment.push(ValidationTracker.class, _tracker);
+
         // Now that the environment is setup, inform the component or other listeners that the form
         // is about to render.  
 
@@ -261,14 +261,6 @@
 
         _formSupport.executeDeferred();
 
-        if (_clientValidation)
-        {
-            // The validations are a JSON object that doesn't need to be enclosed in quotes.
-            // Invoking addScript() will add in the default JavaScript (prototype, scriptaculous, tapestry.js).
-            _pageRenderSupport.addScript(
-                    format("Tapestry.registerForm('%s', %s);", _name, _formSupport.getValidations()));
-        }
-
         String encodingType = _formSupport.getEncodingType();
 
         if (encodingType != null) _form.forceAttributes("enctype", encodingType);
@@ -286,7 +278,13 @@
             throw new RuntimeException(ex);
         }
 
-        _div.element("input", "type", "hidden", "name", FORM_DATA, "value", _actions.toBase64());
+        _div.element("input",
+
+                     "type", "hidden",
+
+                     "name", FORM_DATA,
+
+                     "value", _actions.toBase64());
     }
 
     void cleanupRender()
@@ -318,6 +316,7 @@
 
         try
         {
+
             ComponentResultProcessorWrapper callback = new ComponentResultProcessorWrapper(
                     _componentEventResultProcessor);
 
@@ -329,45 +328,7 @@
 
             if (callback.isAborted()) return true;
 
-            // TODO: Ajax stuff will eventually mean there are multiple values for this parameter
-            // name
-
-            String actionsBase64 = _request.getParameter(FORM_DATA);
-
-            ObjectInputStream ois = null;
-
-            Component component = null;
-
-            try
-            {
-                ois = new Base64ObjectInputStream(actionsBase64);
-
-                while (true)
-                {
-                    String componentId = ois.readUTF();
-                    ComponentAction action = (ComponentAction) ois.readObject();
-
-                    component = _source.getComponent(componentId);
-
-                    action.execute(component);
-
-                    component = null;
-                }
-            }
-            catch (EOFException ex)
-            {
-                // Expected
-            }
-            catch (Exception ex)
-            {
-                Location location = component == null ? null : component.getComponentResources().getLocation();
-
-                throw new TapestryException(ex.getMessage(), location, ex);
-            }
-            finally
-            {
-                InternalUtils.close(ois);
-            }
+            executeStoredActions();
 
             heartbeat.end();
 
@@ -409,6 +370,59 @@
         {
             _environment.pop(Heartbeat.class);
             _environment.pop(FormSupport.class);
+        }
+    }
+
+    /**
+     * Pulls the stored actions out of the request, converts them from MIME stream back to object stream and then
+     * objects, and executes them.
+     */
+    private void executeStoredActions()
+    {
+        String[] values = _request.getParameters(FORM_DATA);
+
+        System.out.println("values = " + values);
+
+        if (values == null) return;
+
+        // Due to Ajax (FormInjector) there may be multiple values here, so handle each one individually.
+
+        for (String actionsBase64 : values)
+        {
+            ObjectInputStream ois = null;
+
+            Component component = null;
+
+            try
+            {
+                ois = new Base64ObjectInputStream(actionsBase64);
+
+                while (true)
+                {
+                    String componentId = ois.readUTF();
+                    ComponentAction action = (ComponentAction) ois.readObject();
+
+                    component = _source.getComponent(componentId);
+
+                    action.execute(component);
+
+                    component = null;
+                }
+            }
+            catch (EOFException ex)
+            {
+                // Expected
+            }
+            catch (Exception ex)
+            {
+                Location location = component == null ? null : component.getComponentResources().getLocation();
+
+                throw new TapestryException(ex.getMessage(), location, ex);
+            }
+            finally
+            {
+                InternalUtils.close(ois);
+            }
         }
     }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormFragment.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormFragment.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormFragment.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormFragment.java Thu Feb 28 10:52:45 2008
@@ -21,7 +21,7 @@
 import org.apache.tapestry.corelib.internal.FormSupportAdapter;
 import org.apache.tapestry.corelib.internal.WrappedComponentAction;
 import org.apache.tapestry.dom.Element;
-import org.apache.tapestry.internal.services.ZoneSetup;
+import org.apache.tapestry.internal.services.ClientBehaviorSupport;
 import org.apache.tapestry.ioc.annotations.Inject;
 import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry.ioc.internal.util.Defense;
@@ -84,7 +84,7 @@
     private ComponentResources _resources;
 
     @Environmental
-    private ZoneSetup _zoneSetup;
+    private ClientBehaviorSupport _clientBehaviorSupport;
 
     private String _clientId;
 
@@ -162,7 +162,7 @@
         writer.end();
 
 
-        _zoneSetup.addFormFragment(_clientId, _show, _hide);
+        _clientBehaviorSupport.addFormFragment(_clientId, _show, _hide);
 
         _componentActions = CollectionFactory.newList();
 

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormInjector.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormInjector.java?rev=632085&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormInjector.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/FormInjector.java Thu Feb 28 10:52:45 2008
@@ -0,0 +1,212 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.components;
+
+import org.apache.tapestry.*;
+import org.apache.tapestry.annotations.Environmental;
+import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.annotations.SupportsInformalParameters;
+import org.apache.tapestry.corelib.data.InsertPosition;
+import org.apache.tapestry.corelib.internal.FormSupportImpl;
+import org.apache.tapestry.internal.services.ClientBehaviorSupport;
+import org.apache.tapestry.internal.services.ComponentResultProcessorWrapper;
+import org.apache.tapestry.internal.services.PageRenderQueue;
+import org.apache.tapestry.internal.util.Base64ObjectOutputStream;
+import org.apache.tapestry.ioc.annotations.Inject;
+import org.apache.tapestry.ioc.internal.util.IdAllocator;
+import org.apache.tapestry.runtime.RenderCommand;
+import org.apache.tapestry.runtime.RenderQueue;
+import org.apache.tapestry.services.*;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * A way to add new content to an existing Form. The FormInjector emulates its tag from
+ * the template (or uses a &lt;div&gt;).
+ * When triggered, new content is obtained from the application and is injected before or after the
+ * element.
+ */
+@SupportsInformalParameters
+public class FormInjector implements ClientElement
+{
+    public static final String INJECT_EVENT = "inject";
+
+    public static final String FORMID_PARAMETER = "t:formid";
+
+    /**
+     * The context for the link (optional parameter). This list of values will be converted into strings and included in
+     * the URI. The strings will be coerced back to whatever their values are and made available to event handler
+     * methods.
+     */
+    @Parameter
+    private List<?> _context;
+
+    @Parameter(defaultPrefix = TapestryConstants.LITERAL_BINDING_PREFIX, value = "above")
+    private InsertPosition _position;
+
+    /**
+     * Name of a function on the client-side Tapestry.ZoneEffect object that is invoked to make added content visible.
+     * Leaving as null uses the default function, "highlight".
+     */
+    @Parameter(defaultPrefix = TapestryConstants.LITERAL_BINDING_PREFIX)
+    private String _show;
+
+
+    @Environmental
+    private PageRenderSupport _pageRenderSupport;
+
+    @Environmental
+    private FormSupport _formSupport;
+
+    @Environmental
+    private ClientBehaviorSupport _clientBehaviorSupport;
+
+    @Inject
+    @Ajax
+    private ComponentEventResultProcessor _componentEventResultProcessor;
+
+
+    @Inject
+    private PageRenderQueue _pageRenderQueue;
+
+    private String _clientId;
+
+    @Inject
+    private ComponentResources _resources;
+
+    @Inject
+    private Request _request;
+
+    @Inject
+    private Environment _environment;
+
+    void beginRender(MarkupWriter writer)
+    {
+        _clientId = _pageRenderSupport.allocateClientId(_resources);
+
+        writer.element(_resources.getElementName("div"),
+
+                       "id", _clientId);
+
+        _resources.renderInformalParameters(writer);
+
+        // Now work on the JavaScript side of things.
+
+        Link link = _resources.createActionLink(INJECT_EVENT, false,
+                                                _context == null ? new Object[0] : _context.toArray());
+
+        link.addParameter(FORMID_PARAMETER, _formSupport.getClientId());
+
+        _clientBehaviorSupport.addFormInjector(_clientId, link, _position, _show);
+    }
+
+    void afterRender(MarkupWriter writer)
+    {
+        writer.end();
+    }
+
+
+    /**
+     * Returns the unique client-side id of the rendered element.
+     */
+    public String getClientId()
+    {
+        return _clientId;
+    }
+
+    /**
+     * Invoked via an Ajax request.  Triggers an action event and captures the return value. The return value from the
+     * event notification is what will ultimately render (typically, its a Block).  However, we do a <em>lot</em> of
+     * tricks to provide the desired FormSupport around the what renders.
+     */
+    void onInject(EventContext context) throws IOException
+    {
+        ComponentResultProcessorWrapper callback = new ComponentResultProcessorWrapper(
+                _componentEventResultProcessor);
+
+        _resources.triggerContextEvent(TapestryConstants.ACTION_EVENT, context, callback);
+
+        if (!callback.isAborted()) return;
+
+        // Here's where it gets very, very tricky.
+
+        final RenderCommand rootRenderCommand = _pageRenderQueue.getRootRenderCommand();
+
+        final String formId = _request.getParameter(FORMID_PARAMETER);
+
+        final Base64ObjectOutputStream actions = new Base64ObjectOutputStream();
+
+        final RenderCommand cleanup = new RenderCommand()
+        {
+            public void render(MarkupWriter writer, RenderQueue queue)
+            {
+                try
+                {
+                    actions.close();
+                }
+                catch (IOException ex)
+                {
+                    throw new RuntimeException(ex);
+                }
+
+                _environment.pop(ValidationTracker.class);
+                FormSupportImpl formSupport = (FormSupportImpl) _environment.pop(FormSupport.class);
+
+                formSupport.executeDeferred();
+
+                writer.element("input",
+
+                               "type", "hidden",
+
+                               "name", Form.FORM_DATA,
+
+                               "value", actions.toBase64());
+            }
+        };
+
+        final RenderCommand setup = new RenderCommand()
+        {
+            public void render(MarkupWriter writer, RenderQueue queue)
+            {
+                // Kind of ugly, but the only way to ensure we don't have name collisions on the
+                // client side is to force a unique id into each name (as well as each id, but that's
+                // PageRenderSupport's job).  It would be nice if we could agree on the uid, but
+                // not essential.
+
+                String uid = Long.toHexString(System.currentTimeMillis());
+
+                IdAllocator idAllocator = new IdAllocator(":" + uid);
+
+                FormSupportImpl formSupport = new FormSupportImpl(formId, actions, _clientBehaviorSupport, true,
+                                                                  idAllocator);
+                _environment.push(FormSupport.class, formSupport);
+
+
+                _environment.push(ValidationTracker.class, new ValidationTrackerImpl());
+
+                // Queue up the root render command to execute first, and the cleanup
+                // to execute after it is done.
+
+                queue.push(cleanup);
+                queue.push(rootRenderCommand);
+            }
+        };
+
+        // Replace rootRenderCommand with our more complicated setup.
+
+        _pageRenderQueue.initializeForPartialPageRender(setup);
+    }
+}
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Loop.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Loop.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Loop.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Loop.java Thu Feb 28 10:52:45 2008
@@ -14,13 +14,11 @@
 
 package org.apache.tapestry.corelib.components;
 
-import org.apache.tapestry.ComponentAction;
-import org.apache.tapestry.ComponentResources;
-import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.PrimaryKeyEncoder;
+import org.apache.tapestry.*;
 import org.apache.tapestry.annotations.*;
 import org.apache.tapestry.ioc.annotations.Inject;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+import org.apache.tapestry.services.ComponentDefaultProvider;
 import org.apache.tapestry.services.FormSupport;
 import org.apache.tapestry.services.Heartbeat;
 
@@ -160,9 +158,10 @@
     }
 
     /**
-     * Defines the collection of values for the loop to iterate over.
+     * Defines the collection of values for the loop to iterate over. If not specified, defaults to a property of the
+     * container whose name
      */
-    @Parameter(required = true)
+    @Parameter(required = true, principal = true)
     private Iterable<?> _source;
 
     /**
@@ -186,7 +185,7 @@
      * The element to render. If not null, then the loop will render the indicated element around its body (on each pass
      * through the loop). The default is derived from the component template.
      */
-    @Parameter(value = "prop:componentResources.elementName", defaultPrefix = "literal")
+    @Parameter(value = "prop:componentResources.elementName", defaultPrefix = TapestryConstants.LITERAL_BINDING_PREFIX)
     private String _elementName;
 
     /**
@@ -210,6 +209,14 @@
 
     @Inject
     private ComponentResources _resources;
+
+    @Inject
+    private ComponentDefaultProvider _componentDefaultProvider;
+
+    Binding defaultSource()
+    {
+        return _componentDefaultProvider.defaultBinding("source", _resources);
+    }
 
     @SetupRender
     boolean setup()

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PageLink.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PageLink.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PageLink.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PageLink.java Thu Feb 28 10:52:45 2008
@@ -66,7 +66,7 @@
     {
         if (_disabled) return;
 
-        _clientId = _support.allocateClientId(_resources.getId());
+        _clientId = _support.allocateClientId(_resources);
 
         Object[] activationContext = _context != null ? _context.toArray() : _emptyContext;
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Radio.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Radio.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Radio.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Radio.java Thu Feb 28 10:52:45 2008
@@ -124,7 +124,7 @@
     {
         String value = _container.toClient(_value);
 
-        _clientId = _pageRenderSupport.allocateClientId(_resources.getId());
+        _clientId = _pageRenderSupport.allocateClientId(_resources);
         _controlName = _container.getElementName();
 
         writer.element("input", "type", "radio", "id", _clientId, "name", _controlName, "value", value);

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/SubmitNotifier.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/SubmitNotifier.java?rev=632085&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/SubmitNotifier.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/SubmitNotifier.java Thu Feb 28 10:52:45 2008
@@ -0,0 +1,67 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.components;
+
+import org.apache.tapestry.ComponentAction;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.annotations.Environmental;
+import org.apache.tapestry.ioc.annotations.Inject;
+import org.apache.tapestry.services.FormSupport;
+
+/**
+ * A non visual component used to provide notifications to its container during a form submission. Records actions into
+ * the form on {@link org.apache.tapestry.annotations.BeginRender} and {@link org.apache.tapestry.annotations.AfterRender}
+ * that (during the form submission) triggers "BeginSubmit" and "AfterSubmit" events.  The container can receive these
+ * events to perform setup before a group of components process their submission, and perform cleanup afterwards.
+ */
+public class SubmitNotifier
+{
+    private static final class TriggerEvent implements ComponentAction<SubmitNotifier>
+    {
+        private final String _eventType;
+
+        public TriggerEvent(String eventType)
+        {
+            _eventType = eventType;
+        }
+
+        public void execute(SubmitNotifier component)
+        {
+            component.trigger(_eventType);
+        }
+    }
+
+
+    @Inject
+    private ComponentResources _resources;
+
+    @Environmental
+    private FormSupport _formSupport;
+
+    void beginRender()
+    {
+        _formSupport.store(this, new TriggerEvent("BeginSubmit"));
+    }
+
+    void afterRender()
+    {
+        _formSupport.store(this, new TriggerEvent("AfterSubmit"));
+    }
+
+    private void trigger(String eventType)
+    {
+        _resources.triggerEvent(eventType, null, null);
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Zone.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Zone.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Zone.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Zone.java Thu Feb 28 10:52:45 2008
@@ -19,7 +19,7 @@
 import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.annotations.SupportsInformalParameters;
 import org.apache.tapestry.dom.Element;
-import org.apache.tapestry.internal.services.ZoneSetup;
+import org.apache.tapestry.internal.services.ClientBehaviorSupport;
 import org.apache.tapestry.ioc.annotations.Inject;
 import org.apache.tapestry.json.JSONObject;
 
@@ -65,7 +65,7 @@
     private PageRenderSupport _pageRenderSupport;
 
     @Environmental
-    private ZoneSetup _zoneSetup;
+    private ClientBehaviorSupport _clientBehaviorSupport;
 
     /**
      * If true (the default) then the zone will render normally.  If false, then the "t-invisible"
@@ -79,7 +79,7 @@
 
     void beginRender(MarkupWriter writer)
     {
-        _clientId = _pageRenderSupport.allocateClientId(_resources.getId());
+        _clientId = _pageRenderSupport.allocateClientId(_resources);
 
         Element e = writer.element("div", "id", _clientId);
 
@@ -94,7 +94,7 @@
         JSONObject spec = new JSONObject();
         spec.put("div", _clientId);
 
-        _zoneSetup.addZone(_clientId, _show, _update);
+        _clientBehaviorSupport.addZone(_clientId, _show, _update);
     }
 
     void afterRender(MarkupWriter writer)

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/data/InsertPosition.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/data/InsertPosition.java?rev=632085&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/data/InsertPosition.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/data/InsertPosition.java Thu Feb 28 10:52:45 2008
@@ -0,0 +1,26 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.data;
+
+public enum InsertPosition
+{
+    /**
+     * Insert the new content above (i.e., before) the insertion position. 
+     */
+    ABOVE,
+
+    /** Insert the new context below (i.e., after) the insertion position. */
+    BELOW;
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/FormSupportImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/FormSupportImpl.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/FormSupportImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/FormSupportImpl.java Thu Feb 28 10:52:45 2008
@@ -16,11 +16,10 @@
 
 import org.apache.tapestry.ComponentAction;
 import org.apache.tapestry.Field;
+import org.apache.tapestry.internal.services.ClientBehaviorSupport;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
 import static org.apache.tapestry.ioc.internal.util.Defense.*;
 import org.apache.tapestry.ioc.internal.util.IdAllocator;
-import org.apache.tapestry.json.JSONArray;
-import org.apache.tapestry.json.JSONObject;
 import org.apache.tapestry.runtime.Component;
 import org.apache.tapestry.services.FormSupport;
 
@@ -29,22 +28,22 @@
 import java.util.List;
 
 /**
- * Provides support to components enclosed by a form when the form is rendering (allowing the
- * components to registry form submit callback commands), and also during form submission time.
+ * Provides support to components enclosed by a form when the form is rendering (allowing the components to registry
+ * form submit callback commands), and also during form submission time.
  * <p/>
- * TODO: Most methods should only be invokable depending on whether the form is rendering or
- * processing a submission.
+ * TODO: Most methods should only be invokable depending on whether the form is rendering or processing a submission.
  */
 public class FormSupportImpl implements FormSupport
 {
-    private final IdAllocator _idAllocator = new IdAllocator();
+    private final ClientBehaviorSupport _clientBehaviorSupport;
 
-    private final String _clientId;
+    private final boolean _clientValidationEnabled;
 
-    private final ObjectOutputStream _actions;
+    private final IdAllocator _idAllocator;
 
-    private final JSONObject _validations = new JSONObject();
+    private final String _clientId;
 
+    private final ObjectOutputStream _actions;
 
     private List<Runnable> _commands;
 
@@ -55,16 +54,34 @@
      */
     public FormSupportImpl()
     {
-        this(null, null);
+        this(null, null, null, false, null);
     }
 
     /**
      * Constructor used when rendering.
      */
-    public FormSupportImpl(String clientId, ObjectOutputStream actions)
+    public FormSupportImpl(String clientId, ObjectOutputStream actions, ClientBehaviorSupport clientBehaviorSupport,
+                           boolean clientValidationEnabled)
+    {
+        this(clientId, actions, clientBehaviorSupport, clientValidationEnabled, new IdAllocator());
+    }
+
+    /**
+     * Full constructor.
+     */
+    public FormSupportImpl(String clientId, ObjectOutputStream actions, ClientBehaviorSupport clientBehaviorSupport,
+                           boolean clientValidationEnabled, IdAllocator idAllocator)
     {
         _clientId = clientId;
         _actions = actions;
+        _clientBehaviorSupport = clientBehaviorSupport;
+        _clientValidationEnabled = clientValidationEnabled;
+        _idAllocator = idAllocator;
+    }
+
+    public String getFormId()
+    {
+        return _clientId;
     }
 
     public String allocateControlName(String id)
@@ -138,39 +155,9 @@
 
     public void addValidation(Field field, String validationName, String message, Object constraint)
     {
-        String fieldId = field.getClientId();
-
-        JSONArray specs;
-
-        if (_validations.has(fieldId)) specs = _validations.getJSONArray(fieldId);
-        else
-        {
-            specs = new JSONArray();
-            _validations.put(fieldId, specs);
-        }
-
-        JSONArray thisSpec = new JSONArray();
-
-        thisSpec.put(validationName);
-        thisSpec.put(message);
-
-        if (constraint != null) thisSpec.put(constraint);
-
-        specs.put(thisSpec);
+        if (_clientValidationEnabled)
+            _clientBehaviorSupport.addValidation(field, validationName, message, constraint);
     }
 
-    /**
-     * Returns the combined validation data collected via {@link #addValidation(org.apache.tapestry.Field, String, String, Object)}.
-     * The keys of this object are the client ids of the {@link org.apache.tapestry.Field}s, the values
-     * are an array of validation specifications for that field. Each validation specification is itself
-     * an array of two or three values: the validation name (i.e., a method of the client-side Tapestry.Validation
-     * object), the message if the field is invalid and, optionally, the constraint value.
-     *
-     * @return the validation object
-     */
-    public JSONObject getValidations()
-    {
-        return _validations;
-    }
 
 }

Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupport.java (from r630031, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetup.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupport.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupport.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetup.java&r1=630031&r2=632085&rev=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetup.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupport.java Thu Feb 28 10:52:45 2008
@@ -14,30 +14,32 @@
 
 package org.apache.tapestry.internal.services;
 
+import org.apache.tapestry.Field;
+import org.apache.tapestry.Link;
+import org.apache.tapestry.corelib.data.InsertPosition;
+
 /**
- * Collects details about zone usage for efficient initialization of the client side objects.  This has grown
- * to include the client-side behavior associated with {@link org.apache.tapestry.corelib.components.FormFragment}s.
+ * Collects details about zone usage for efficient initialization of the client side objects.  This has grown to include
+ * the client-side behavior associated with {@link org.apache.tapestry.corelib.components.FormFragment}s.
  *
  * @see org.apache.tapestry.corelib.components.Zone
  */
-public interface ZoneSetup
+public interface ClientBehaviorSupport
 {
     /**
-     * Adds a new client-side Tapestry.Zone object. Zones are linked to a
-     * an element (typically, a &lt;div&gt;).  A Zone may have handlers
-     * used to initially show it, or to highlight it when its content changes.
-     * Such handlers are referenced by name, as functions of the
-     * Tapestry.ZoneEffect object.
+     * Adds a new client-side Tapestry.Zone object. Zones are linked to a an element (typically, a &lt;div&gt;).  A Zone
+     * may have handlers used to initially show it, or to highlight it when its content changes. Such handlers are
+     * referenced by name, as functions of the Tapestry.ZoneEffect object.
      *
      * @param clientId           client-side id of the element that will be updated by the zone
-     * @param showFunctionName   name of the function used to initially show the zone (if not visible), or null for default
+     * @param showFunctionName   name of the function used to initially show the zone (if not visible), or null for
+     *                           default
      * @param updateFunctionName name of function used to highlight the function after an update, or null for default
      */
     void addZone(String clientId, String showFunctionName, String updateFunctionName);
 
     /**
-     * Sets the client-side onclick handler for an &lt;a&gt; element to perform an Ajax update
-     * of a zone.
+     * Sets the client-side onclick handler for an &lt;a&gt; element to perform an Ajax update of a zone.
      *
      * @param linkId    id of the link to Ajax enable
      * @param elementId id of an element that has been previously registered as a Zone
@@ -45,9 +47,8 @@
     void linkZone(String linkId, String elementId);
 
     /**
-     * Adds a new client-side Tapestry.FormFragment object.  FormFragment's are used to make parts of a
-     * client-side form visible or invisible, which involves interactions with both the server-side and client-side
-     * validation.
+     * Adds a new client-side Tapestry.FormFragment object.  FormFragment's are used to make parts of a client-side form
+     * visible or invisible, which involves interactions with both the server-side and client-side validation.
      *
      * @param clientId         client-side id of the element that will be made visible or invisible
      * @param showFunctionName name of function (of the Tapestry.ZoneEffect object) used to make the SubForm visible, or
@@ -55,5 +56,27 @@
      * @param hideFunctionName name of the function used to make the SubForm invisible, or null for the default
      */
     void addFormFragment(String clientId, String showFunctionName, String hideFunctionName);
+
+    /**
+     * Adds a new client-side Tapestry.FormInjector object.  FormInjectors are used to extend an existing Form with new
+     * content.
+     *
+     * @param clientId         client-side id of the element that identifiess where the new content will be placed
+     * @param link             action request link used to trigger the server-side object, to render the new content
+     * @param insertPosition   where the new content should go (above or below the element)
+     * @param showFunctionName name of function (of the Tapestry.ZoneEffect object) used to make the new element
+     *                         visible, or null for the default
+     */
+    void addFormInjector(String clientId, Link link, InsertPosition insertPosition, String showFunctionName);
+
+    /**
+     * Collects field validation information.
+     *
+     * @param field          for which validation is being generated
+     * @param validationName name of validation method (see Tapestry.Validation in tapestry.js)
+     * @param message        the error message to display if the field is invalid
+     * @param constraint     additional constraint value, or null for validations that don't require a constraint
+     */
+    void addValidation(Field field, String validationName, String message, Object constraint);
 }
 

Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java (from r630031, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetupImpl.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetupImpl.java&r1=630031&r2=632085&rev=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ZoneSetupImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java Thu Feb 28 10:52:45 2008
@@ -14,11 +14,14 @@
 
 package org.apache.tapestry.internal.services;
 
+import org.apache.tapestry.Field;
+import org.apache.tapestry.Link;
 import org.apache.tapestry.PageRenderSupport;
+import org.apache.tapestry.corelib.data.InsertPosition;
 import org.apache.tapestry.json.JSONArray;
 import org.apache.tapestry.json.JSONObject;
 
-public class ZoneSetupImpl implements ZoneSetup
+public class ClientBehaviorSupportImpl implements ClientBehaviorSupport
 {
     static final String ZONE_INITIALIZER_STRING = "Tapestry.initializeZones(%s, %s);";
 
@@ -30,11 +33,13 @@
 
     private final JSONArray _subForms = new JSONArray();
 
-    private boolean _zonesDirty;
+    private final JSONArray _injectors = new JSONArray();
+
+    private final JSONObject _validations = new JSONObject();
 
-    private boolean _subformsDirty;
+    private boolean _zonesDirty;
 
-    public ZoneSetupImpl(PageRenderSupport pageRenderSupport)
+    public ClientBehaviorSupportImpl(PageRenderSupport pageRenderSupport)
     {
         _pageRenderSupport = pageRenderSupport;
     }
@@ -78,16 +83,57 @@
         addFunction(spec, "hide", hideFunctionName);
 
         _subForms.put(spec);
+    }
+
+    public void addFormInjector(String clientId, Link link, InsertPosition insertPosition, String showFunctionName)
+    {
+        JSONObject spec = new JSONObject();
+        spec.put("element", clientId);
+
+        spec.put("url", link.toAbsoluteURI());
+
+        if (insertPosition == InsertPosition.BELOW)
+            spec.put("below", true);
+
+        addFunction(spec, "show", showFunctionName);
+
+        _injectors.put(spec);
+    }
+
+    public void addValidation(Field field, String validationName, String message, Object constraint)
+    {
+        String fieldId = field.getClientId();
+
+        JSONArray specs;
 
-        _subformsDirty = true;
+        if (_validations.has(fieldId)) specs = _validations.getJSONArray(fieldId);
+        else
+        {
+            specs = new JSONArray();
+            _validations.put(fieldId, specs);
+        }
 
+        JSONArray thisSpec = new JSONArray();
+
+        thisSpec.put(validationName);
+        thisSpec.put(message);
+
+        if (constraint != null) thisSpec.put(constraint);
+
+        specs.put(thisSpec);
     }
 
     public void writeInitializationScript()
     {
         if (_zonesDirty) _pageRenderSupport.addScript(ZONE_INITIALIZER_STRING, _zones, _links);
 
-        if (_subformsDirty)
+        if (_subForms.length() > 0)
             _pageRenderSupport.addScript("Tapestry.initializeFormFragments(%s);", _subForms);
+
+        if (_injectors.length() > 0)
+            _pageRenderSupport.addScript("Tapestry.initializeFormInjectors(%s);", _injectors);
+
+        if (_validations.length() > 0)
+            _pageRenderSupport.addScript("Tapestry.registerValidation(%s);", _validations);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueue.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueue.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueue.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueue.java Thu Feb 28 10:52:45 2008
@@ -48,7 +48,16 @@
     void initializeForPartialPageRender(RenderCommand rootCommand);
 
     /**
-     * Returns true if {@link #initializeForPartialPageRender(org.apache.tapestry.runtime.RenderCommand)} has been invoked.
+     * Obtains the value previously supplied to {@link #initializeForPartialPageRender(org.apache.tapestry.runtime.RenderCommand)}.
+     * This allows the "natural" renderer to be substituted or otherwise manipulated.
+     *
+     * @return the root renderer
+     */
+    RenderCommand getRootRenderCommand();
+
+    /**
+     * Returns true if {@link #initializeForPartialPageRender(org.apache.tapestry.runtime.RenderCommand)} has been
+     * invoked.
      */
     boolean isPartialRenderInitialized();
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueueImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueueImpl.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueueImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderQueueImpl.java Thu Feb 28 10:52:45 2008
@@ -63,6 +63,11 @@
         _rootCommand = rootCommand;
     }
 
+    public RenderCommand getRootRenderCommand()
+    {
+        return _rootCommand;
+    }
+
     public Page getRenderingPage()
     {
         return _page;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderSupportImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderSupportImpl.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderSupportImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageRenderSupportImpl.java Thu Feb 28 10:52:45 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry.internal.services;
 
 import org.apache.tapestry.Asset;
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.PageRenderSupport;
 import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
 import org.apache.tapestry.ioc.internal.util.IdAllocator;
@@ -41,11 +42,11 @@
 
     /**
      * @param builder      Used to assemble JavaScript includes and snippets
-     * @param symbolSource Used to example symbols (in
-     *                     {@linkplain #addClasspathScriptLink(String...) in classpath scripts)
+     * @param symbolSource Used to example symbols (in {@linkplain #addClasspathScriptLink(String...) in classpath
+     *                     scripts)
      * @param assetSource  Used to convert classpath scripts to {@link Asset}s
-     * @param coreScripts  core scripts (evaluated as classpaths scripts) that are added to any page that
-     *                     includes a script link or script block
+     * @param coreScripts  core scripts (evaluated as classpaths scripts) that are added to any page that includes a
+     *                     script link or script block
      */
     public PageRenderSupportImpl(DocumentHeadBuilder builder, SymbolSource symbolSource,
                                  AssetSource assetSource, String... coreScripts)
@@ -60,6 +61,11 @@
     public String allocateClientId(String id)
     {
         return _idAllocator.allocateId(id);
+    }
+
+    public String allocateClientId(ComponentResources resources)
+    {
+        return allocateClientId(resources.getId());
     }
 
     public void addScriptLink(Asset... scriptAssets)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PartialRenderPageRenderSupport.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PartialRenderPageRenderSupport.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PartialRenderPageRenderSupport.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PartialRenderPageRenderSupport.java Thu Feb 28 10:52:45 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry.internal.services;
 
 import org.apache.tapestry.Asset;
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.PageRenderSupport;
 import org.apache.tapestry.ioc.internal.util.IdAllocator;
 import org.apache.tapestry.json.JSONObject;
@@ -44,6 +45,11 @@
         return _idAllocator.allocateId(id);
     }
 
+    public String allocateClientId(ComponentResources resources)
+    {
+        return allocateClientId(resources.getId());
+    }
+
     /**
      * Does nothing.  Script links are only supported during full page renders, not partials (at this time).
      */
@@ -73,7 +79,8 @@
     }
 
     /**
-     * Updates the reply with a "script" key, if any scripting has been collected via {@link #addScript(String, Object[])} }.
+     * Updates the reply with a "script" key, if any scripting has been collected via {@link #addScript(String,
+     * Object[])} }.
      *
      * @param reply
      */

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Thu Feb 28 10:52:45 2008
@@ -1065,9 +1065,9 @@
         return _page.getLocale();
     }
 
-    public String getElementName()
+    public String getElementName(String defaultElementName)
     {
-        return _elementName;
+        return _elementName != null ? _elementName : defaultElementName;
     }
 
     public Block getBlock(String id)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java Thu Feb 28 10:52:45 2008
@@ -134,6 +134,10 @@
         _page.discardPersistentFieldChanges();
     }
 
+    public String getElementName()
+    {
+        return getElementName(null);
+    }
 
     public String getCompleteId()
     {
@@ -347,9 +351,9 @@
         return _messages;
     }
 
-    public String getElementName()
+    public String getElementName(String defaultElementName)
     {
-        return _element.getElementName();
+        return _element.getElementName(defaultElementName);
     }
 
     public void queueRender(RenderQueue queue)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java Thu Feb 28 10:52:45 2008
@@ -91,7 +91,9 @@
 
     public String[] getParameters(String name)
     {
-        return nyi("getParameters");
+        String value = getParameter(name);
+
+        return value == null ? null : new String[] { value };
     }
 
     public String getPath()

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FormSupport.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FormSupport.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FormSupport.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FormSupport.java Thu Feb 28 10:52:45 2008
@@ -19,35 +19,31 @@
 import org.apache.tapestry.Field;
 
 /**
- * Services provided by an enclosing Form control component to the various form element components
- * it encloses. Implements {@link ClientElement}, to share the id of the enclosing form.
+ * Services provided by an enclosing Form control component to the various form element components it encloses.
+ * Implements {@link org.apache.tapestry.ClientElement}, to share the id of the enclosing form.
  *
  * @see org.apache.tapestry.Field
  */
 public interface FormSupport extends ClientElement
 {
     /**
-     * Allocates a unique (within the form) control name for some enclosed component,
-     * based on the component's id.
+     * Allocates a unique (within the form) control name for some enclosed component, based on the component's id.
      *
      * @param id the component's id
-     * @return a unique string, usually the component's id, but sometime extended with a unique
-     *         number or string
+     * @return a unique string, usually the component's id, but sometime extended with a unique number or string
      */
     String allocateControlName(String id);
 
     /**
-     * Stores an action for execution during a later request.  If the action contains any mutable
-     * state, it should be in its final state before invoking this method and its internal
-     * state should not be changed subsequently.
+     * Stores an action for execution during a later request.  If the action contains any mutable state, it should be in
+     * its final state before invoking this method and its internal state should not be changed subsequently.
      */
     <T> void store(T component, ComponentAction<T> action);
 
     /**
-     * As with {@link #store(Object, org.apache.tapestry.ComponentAction)}}, but the
-     * action is also invoked immediately. This is useful for defining an action that
-     * should occur symmetrically in both the render request and the form submission's
-     * action request.
+     * As with {@link #store(Object, org.apache.tapestry.ComponentAction)}}, but the action is also invoked immediately.
+     * This is useful for defining an action that should occur symmetrically in both the render request and the form
+     * submission's action request.
      *
      * @param component component against which to trigger the action
      * @param action    the action that will be triggered (and passed the component)
@@ -55,11 +51,11 @@
     <T> void storeAndExecute(T component, ComponentAction<T> action);
 
     /**
-     * Defers a command until the end of the form submission. The command will be executed after the
-     * Form's validate notification, but before the Form's submit, success or failure notifications.
-     * During a form render, runnables are executed after the body of the form has rendered.
+     * Defers a command until the end of the form submission. The command will be executed after the Form's validate
+     * notification, but before the Form's submit, success or failure notifications. During a form render, runnables are
+     * executed after the body of the form has rendered.
      *
-     * @param command
+     * @param command to be executed
      */
     void defer(Runnable command);
 
@@ -72,7 +68,8 @@
     void setEncodingType(String encodingType);
 
     /**
-     * Collects field validation information.
+     * Collects field validation information. A Form may turn off client-side validation, in which case these calls will
+     * be ignored.
      *
      * @param field          for which validation is being generated
      * @param validationName name of validation method (see Tapestry.Validation in tapestry.js)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java Thu Feb 28 10:52:45 2008
@@ -18,6 +18,7 @@
 import org.apache.tapestry.annotations.*;
 import org.apache.tapestry.beaneditor.Validate;
 import org.apache.tapestry.corelib.data.GridPagerPosition;
+import org.apache.tapestry.corelib.data.InsertPosition;
 import org.apache.tapestry.grid.GridDataSource;
 import org.apache.tapestry.internal.*;
 import org.apache.tapestry.internal.beaneditor.PrimitiveFieldConstraintGenerator;
@@ -563,9 +564,12 @@
     }
 
     /**
-     * Adds coercions: <ul> <li>String to {@link SelectModel} <li>Map to {@link SelectModel} <li>Collection to {@link
-     * GridDataSource} <li>null to {@link GridDataSource} <li>String to {@link GridPagerPosition} <li>List to {@link
-     * SelectModel} <li>{@link ComponentResourcesAware} (typically, a component) to {@link ComponentResources} </ul>
+     * Adds coercions: <ul> <li>String to {@link org.apache.tapestry.SelectModel} <li>String to {@link
+     * org.apache.tapestry.corelib.data.InsertPosition} <li>Map to {@link org.apache.tapestry.SelectModel}
+     * <li>Collection to {@link GridDataSource} <li>null to {@link org.apache.tapestry.grid.GridDataSource} <li>String
+     * to {@link org.apache.tapestry.corelib.data.GridPagerPosition} <li>List to {@link SelectModel} <li>{@link
+     * org.apache.tapestry.runtime.ComponentResourcesAware} (typically, a component) to {@link
+     * org.apache.tapestry.ComponentResources} </ul>
      */
     public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration)
     {
@@ -605,7 +609,9 @@
         });
 
         add(configuration, String.class, GridPagerPosition.class,
-            new StringToEnumCoercion<GridPagerPosition>(GridPagerPosition.class));
+            StringToEnumCoercion.create(GridPagerPosition.class));
+
+        add(configuration, String.class, InsertPosition.class, StringToEnumCoercion.create(InsertPosition.class));
 
         add(configuration, List.class, SelectModel.class, new Coercion<List, SelectModel>()
         {
@@ -1291,10 +1297,11 @@
     /**
      * Adds page render filters, each of which provides an {@link org.apache.tapestry.annotations.Environmental}
      * service.  Filters often provide {@link Environmental} services needed by components as they render. <dl>
-     * <dt>PageRenderSupport</dt>  <dd>Provides {@link PageRenderSupport}</dd> <dt>ZoneSetup</dt> <dd>Provides {@link
-     * ZoneSetup}</dd> <dt>Heartbeat</dt> <dd>Provides {@link org.apache.tapestry.services.Heartbeat}</dd>
-     * <dt>DefaultValidationDecorator</dt> <dd>Provides {@link org.apache.tapestry.ValidationDecorator} (as an instance
-     * of {@link org.apache.tapestry.internal.DefaultValidationDecorator})</dd> </dl>
+     * <dt>PageRenderSupport</dt>  <dd>Provides {@link PageRenderSupport}</dd> <dt>ClientBehaviorSupport</dt>
+     * <dd>Provides {@link org.apache.tapestry.internal.services.ClientBehaviorSupport}</dd> <dt>Heartbeat</dt>
+     * <dd>Provides {@link org.apache.tapestry.services.Heartbeat}</dd> <dt>DefaultValidationDecorator</dt> <dd>Provides
+     * {@link org.apache.tapestry.ValidationDecorator} (as an instance of {@link org.apache.tapestry.internal.DefaultValidationDecorator})</dd>
+     * </dl>
      */
     public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,
 
@@ -1340,21 +1347,21 @@
             }
         };
 
-        MarkupRendererFilter zoneSetup = new MarkupRendererFilter()
+        MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter()
         {
             public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
             {
                 PageRenderSupport pageRenderSupport = _environment.peekRequired(PageRenderSupport.class);
 
-                ZoneSetupImpl setup = new ZoneSetupImpl(pageRenderSupport);
+                ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(pageRenderSupport);
 
-                _environment.push(ZoneSetup.class, setup);
+                _environment.push(ClientBehaviorSupport.class, clientBehaviorSupport);
 
                 renderer.renderMarkup(writer);
 
-                _environment.pop(ZoneSetup.class);
+                _environment.pop(ClientBehaviorSupport.class);
 
-                setup.writeInitializationScript();
+                clientBehaviorSupport.writeInitializationScript();
             }
         };
 
@@ -1395,7 +1402,7 @@
 
 
         configuration.add("PageRenderSupport", pageRenderSupport);
-        configuration.add("ZoneSetup", zoneSetup, "after:PageRenderSupport");
+        configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:PageRenderSupport");
         configuration.add("Heartbeat", heartbeat, "after:PageRenderSupport");
         configuration.add("DefaultValidationDecorator", defaultValidationDecorator, "after:Heartbeat");
     }
@@ -1409,8 +1416,8 @@
      * @param configuration filters for the service
      * @param renderQueue   does most of the work
      * @return the service
-     * @see #contributePartialMarkupRenderer(org.apache.tapestry.ioc.OrderedConfiguration,
-     *      org.apache.tapestry.internal.services.AjaxUIDManager, org.apache.tapestry.Asset, ValidationMessagesSource)
+     * @see #contributePartialMarkupRenderer(org.apache.tapestry.ioc.OrderedConfiguration, org.apache.tapestry.Asset,
+     *      ValidationMessagesSource)
      */
     public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
                                                             List<PartialMarkupRendererFilter> configuration,
@@ -1434,15 +1441,14 @@
      * to {@link #contributeMarkupRenderer(org.apache.tapestry.ioc.OrderedConfiguration, org.apache.tapestry.Asset,
      * org.apache.tapestry.Asset, ValidationMessagesSource, org.apache.tapestry.ioc.services.SymbolSource, AssetSource)}
      * } and overlaps it to some degree. <dl> <dt>   PageRenderSupport     </dt> <dd>Provides {@link
-     * org.apache.tapestry.PageRenderSupport}</dd> <dt>ZoneSetup</dt> <dd>Provides {@link ZoneSetup}</dd>
-     * <dt>Heartbeat</dt> <dd>Provides {@link org.apache.tapestry.services.Heartbeat}</dd>
-     * <dt>DefaultValidationDecorator</dt> <dd>Provides {@link org.apache.tapestry.ValidationDecorator} (as an instance
-     * of {@link org.apache.tapestry.internal.DefaultValidationDecorator})</dd> </dl>
+     * org.apache.tapestry.PageRenderSupport}</dd> <dt>ClientBehaviorSupport</dt> <dd>Provides {@link
+     * org.apache.tapestry.internal.services.ClientBehaviorSupport}</dd> <dt>Heartbeat</dt> <dd>Provides {@link
+     * org.apache.tapestry.services.Heartbeat}</dd> <dt>DefaultValidationDecorator</dt> <dd>Provides {@link
+     * org.apache.tapestry.ValidationDecorator} (as an instance of {@link org.apache.tapestry.internal.DefaultValidationDecorator})</dd>
+     * </dl>
      */
     public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration,
 
-                                                final AjaxUIDManager ajaxUIDManager,
-
                                                 @Path("${tapestry.field-error-marker}")
                                                 final Asset fieldErrorIcon,
 
@@ -1452,7 +1458,9 @@
         {
             public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
             {
-                String namespace = ":" + ajaxUIDManager.getAjaxUID();
+                String uid = Long.toHexString(System.currentTimeMillis());
+
+                String namespace = ":" + uid;
 
                 PartialRenderPageRenderSupport support = new PartialRenderPageRenderSupport(
                         namespace);
@@ -1467,21 +1475,21 @@
             }
         };
 
-        PartialMarkupRendererFilter zoneSupport = new PartialMarkupRendererFilter()
+        PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter()
         {
             public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
             {
                 PageRenderSupport pageRenderSupport = _environment.peekRequired(PageRenderSupport.class);
 
-                ZoneSetupImpl setup = new ZoneSetupImpl(pageRenderSupport);
+                ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(pageRenderSupport);
 
-                _environment.push(ZoneSetup.class, setup);
+                _environment.push(ClientBehaviorSupport.class, support);
 
                 renderer.renderMarkup(writer, reply);
 
-                _environment.pop(ZoneSetup.class);
+                _environment.pop(ClientBehaviorSupport.class);
 
-                setup.writeInitializationScript();
+                support.writeInitializationScript();
             }
         };
 
@@ -1522,7 +1530,7 @@
 
 
         configuration.add("PageRenderSupport", pageRenderSupport);
-        configuration.add("ZoneSupport", zoneSupport, "after:PageRenderSupport");
+        configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:PageRenderSupport");
         configuration.add("Heartbeat", heartbeat, "after:PageRenderSupport");
         configuration.add("DefaultValidationDecorator", defaultValidationDecorator, "after:Heartbeat");
     }
@@ -2152,14 +2160,5 @@
     {
         configuration.add("default", new DefaultNullFieldStrategy());
         configuration.add("zero", new ZeroNullFieldStrategy());
-    }
-
-    public static AjaxUIDManager buildAjaxUIDManager(LinkFactory linkFactory, ServiceResources resources)
-    {
-        AjaxUIDManagerImpl service = resources.autobuild(AjaxUIDManagerImpl.class);
-
-        linkFactory.addListener(service);
-
-        return service;
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/StringToEnumCoercion.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/StringToEnumCoercion.java?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/StringToEnumCoercion.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/StringToEnumCoercion.java Thu Feb 28 10:52:45 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -21,12 +21,11 @@
 import java.util.Map;
 
 /**
- * A {@link Coercion} for converting strings into an instance of a particular enumerated type. The
- * {@link Enum#name() name} is used as the key to identify the enum instance, in a case-insensitive
+ * A {@link org.apache.tapestry.ioc.services.Coercion} for converting strings into an instance of a particular
+ * enumerated type. The {@link Enum#name() name} is used as the key to identify the enum instance, in a case-insensitive
  * fashion.
  *
- * @param <T>
- * the type of enumeration
+ * @param <T> the type of enumeration
  */
 public final class StringToEnumCoercion<T extends Enum> implements Coercion<String, T>
 {
@@ -61,6 +60,12 @@
                     _stringToEnum.keySet()));
 
         return result;
+    }
+
+
+    public static <T extends Enum> StringToEnumCoercion<T> create(Class<T> enumClass)
+    {
+        return new StringToEnumCoercion<T>(enumClass);
     }
 
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/tapestry.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/tapestry.js?rev=632085&r1=632084&r2=632085&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/tapestry.js (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/tapestry.js Thu Feb 28 10:52:45 2008
@@ -24,6 +24,8 @@
 
     FormFragment : Class.create(),
 
+    FormInjector : Class.create(),
+
     ErrorPopup : Class.create(),
 
     // An array of ErrorPopup that have been created for fields within the page
@@ -39,11 +41,11 @@
         document.observe("dom:loaded", callback);
     },
 
-    // Find all elements marked with the "t-invisible" CSS class and hide() them, so that
-    // Prototype's visible() method operates correctly. This is invoked when the
-    // DOM is first loaded, and AGAIN whenever dynamic content is loaded via the Zone
-    // mechanism.
-
+    /** 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
+     * 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.
+     */
     onDomLoadedCallback : function()
     {
         $$(".t-invisible").each(function(element)
@@ -78,18 +80,17 @@
         });
     },
 
-    registerForm : function(form, clientValidations)
-    {
-        new Tapestry.FormEventManager(form);
-
-        this.registerValidations(form, clientValidations);
-    },
-
-    registerValidations : function(form, clientValidations)
+    registerValidation : function(clientValidations)
     {
         $H(clientValidations).each(function(pair)
         {
             var field = $(pair.key);
+
+            var form = $(field.form);
+
+            if (! form.eventManager)
+                form.eventManager = new Tapestry.FormEventManager(form);
+
             var specs = pair.value;
 
             specs.each(function(spec)
@@ -115,9 +116,22 @@
         });
     },
 
-    // Convert a form or link into a trigger of an Ajax update that
-    // updates the indicated Zone.
-
+    /**
+     * Passed the JSON content of a Tapestry partial markup response, extracts
+     * the script key (if present) and evals it, then uses the DOM loaded callback
+     * to hide invisible fields and add notifications for any form elements.
+     */
+    processScriptInReply : function(reply)
+    {
+        if (reply.script != undefined)
+            eval(reply.script);
+
+        Tapestry.onDomLoadedCallback();
+    },
+
+    /** Convert a form or link into a trigger of an Ajax update that
+     * updates the indicated Zone.
+     */
     linkZone : function(element, zoneDiv)
     {
         element = $(element);
@@ -125,20 +139,13 @@
 
         var successHandler = function(transport)
         {
-            var response = transport.responseText;
-            var reply = eval("(" + response + ")");
+            var reply = transport.responseJSON;
 
             zone.show(reply.content);
 
-            var newScript = reply.script;
-
-            if (newScript != undefined)
-                eval(newScript);
-
-            Tapestry.onDomLoadedCallback();
+            Tapestry.processScriptInReply(reply);
         };
 
-
         if (element.tagName == "FORM")
         {
             // The existing handler, if present, will be responsible for form validations, which must
@@ -207,6 +214,14 @@
         });
     },
 
+    initializeFormInjectors : function(specs)
+    {
+        $A(specs).each(function(spec)
+        {
+            new Tapestry.FormInjector(spec);
+        });
+    },
+
 
     // Links a FormFragment to a checkbox, such that changing the checkbox will hide
     // or show the FormFragment. Care should be taken to render the page with the
@@ -490,7 +505,6 @@
     initialize : function(form)
     {
         this.form = $(form);
-        this.form.eventManager = this;
 
         this.form.onsubmit = this.handleSubmit.bindAsEventListener(this);
     },
@@ -522,7 +536,6 @@
             this.form.fire("form:prepareforsubmit");
         }
 
-
         return event.result;
     }
 
@@ -775,5 +788,55 @@
     }
 };
 
-Tapestry.onDOMLoaded(Tapestry.onDomLoadedCallback);
 
+Tapestry.FormInjector.prototype = {
+
+    initialize: function(spec)
+    {
+        this.element = $(spec.element);
+        this.url = spec.url;
+        this.below = spec.below;
+
+        this.showFunc = Tapestry.ZoneEffect[spec.show] || Tapestry.ZoneEffect.highlight;
+
+        this.element.trigger = function()
+        {
+            var successHandler = function(transport)
+            {
+                var reply = transport.responseJSON;
+
+                // Clone the FormInjector element (usually a div)
+                // to create the new element, that gets inserted
+                // before or after the FormInjector element.
+
+                var newElement = new Element(this.element.tagName);
+
+                newElement.innerHTML = reply.content;
+
+                // Insert the new content before or after the existing element.
+
+                var param = { };
+                var key = this.below ? "after" : "before";
+                param[key] = newElement;
+
+                // Add the new element with the downloaded content.
+
+                this.element.insert(param);
+
+                // Add some animation
+
+                this.showFunc(newElement);
+
+                // Handle any scripting issues.
+
+                Tapestry.processScriptInReply(reply);
+            }.bind(this);
+
+            new Ajax.Request(this.url, { onSuccess : successHandler });
+
+            return false;
+        }.bind(this);
+    }
+};
+
+Tapestry.onDOMLoaded(Tapestry.onDomLoadedCallback);

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/FormInjectorDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/FormInjectorDemo.tml?rev=632085&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/FormInjectorDemo.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/FormInjectorDemo.tml Thu Feb 28 10:52:45 2008
@@ -0,0 +1,32 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+
+    <t:form>
+
+        <t:block id="newRow">
+
+            <div class="t-beaneditor-row">
+                <t:submitnotifier>
+                    <t:textfield t:id="value"/>
+                </t:submitnotifier>
+            </div>
+
+        </t:block>
+
+        <div t:id="forminjector" class="t-beaneditor-row">
+            <a href="#" id="addnewrow">Add a row</a>
+        </div>
+
+        <div class="t-beaneditor-row">
+            <input type="submit" value="Sum up the values"/>
+        </div>
+
+    </t:form>
+
+    <p>
+        Current sum:
+        <span id="sum">${sum}</span>
+    </p>
+
+
+</html>
\ No newline at end of file