You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by jk...@apache.org on 2007/08/14 20:49:39 UTC

svn commit: r565853 [1/2] - in /tapestry/tapestry4/trunk/tapestry-framework/src: java/org/apache/tapestry/ java/org/apache/tapestry/form/ java/org/apache/tapestry/script/ java/org/apache/tapestry/services/ java/org/apache/tapestry/services/impl/ java/o...

Author: jkuhnert
Date: Tue Aug 14 11:49:37 2007
New Revision: 565853

URL: http://svn.apache.org/viewvc?view=rev&rev=565853
Log:
Fixes TAPESTRY-1705.  Added support for appending initialization scripts to the very end of the initialization script block.

Modified:
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/FormBehavior.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IScriptProcessor.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/PageRenderSupport.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/form/FormSupportImpl.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/script/ScriptSessionImpl.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/ResponseBuilder.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DefaultResponseBuilder.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DojoAjaxResponseBuilder.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/JSONResponseBuilder.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/PrototypeResponseBuilder.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/PageRenderSupportImpl.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/version.properties
    tapestry/tapestry4/trunk/tapestry-framework/src/js/tapestry/form.js
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/form/FormSupportTest.java
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/junit/script/MockScriptProcessor.java

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/FormBehavior.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/FormBehavior.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/FormBehavior.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/FormBehavior.java Tue Aug 14 11:49:37 2007
@@ -34,9 +34,14 @@
     /**
      * Adds an additional event handler. The type determines when the handler will be invoked,
      * {@link FormEventType#SUBMIT}is most typical.
+     *
+     * @param type
+     *          Type of event to add.
+     * @param functionName
+     *          Name of the javascript function being added.
      * 
      * @deprecated Wiring of form event handlers is now managed on the client side. This method may
-     *             be removed in a future release of Tapestry.
+     *             be removed in Tapestry 4.1.2.
      */
     void addEventHandler(FormEventType type, String functionName);
 
@@ -47,6 +52,11 @@
      * <p>
      * It is acceptible to add multiple hidden fields with the same name. They will be written in
      * the order they are received.
+     *
+     * @param name
+     *          The name of the hidden input.
+     * @param value
+     *          The value to store in the hidden field.
      */
 
     void addHiddenValue(String name, String value);
@@ -58,7 +68,13 @@
      * <p>
      * It is acceptible to add multiple hidden fields with the same name. They will be written in
      * the order they are received.
-     * 
+     *
+     * @param name
+     *          The name of the hidden input.
+     * @param id
+     *          The id of the hidden input - should almost always be the same as the name.
+     * @param value
+     *          The value to store in the hidden field.
      * @since 3.0
      */
 
@@ -70,9 +86,16 @@
      * <p>
      * Simply invokes {@link #getElementId(IFormComponent, String)}with the component's id.
      * <p>
-     * Note: yes, some confusion on naming here. This is the form element id, which should be (for
+     *
+     * <p>Note: yes, some confusion on naming here. This is the form element id, which should be (for
      * Tapestry purposes) unique within the rendered form. The {@link IFormComponent#getClientId()}
      * is different, and should be unique within the rendered page.
+     * </p>
+     *
+     * @param component
+     *          The component to get the unique id of.
+     * @return The unique id for this component, to be used in rendering name="id" type elements.
+     *
      */
 
     String getElementId(IFormComponent component);
@@ -85,7 +108,13 @@
      * {@link org.apache.tapestry.form.ImageSubmit}) have more specific control over their names.
      * <p>
      * Invokes {@link IFormComponent#setName(String)}with the result, as well as returning it.
-     * 
+     *
+     * @param component
+     *          The component to generate an element id for.
+     * @param baseId
+     *          The basic id of the component.
+     * @return The form specific unique identifier for the given element.  May be the same as the baseId
+     *          if this is the first render of that specific component.
      * @throws StaleLinkException
      *             if, when the form itself is rewinding, the element id allocated does not match
      *             the expected id (as allocated when the form rendered). This indicates that the
@@ -98,6 +127,9 @@
     /**
      * Used internally to "peek" at what the next generated client id will be for the 
      * given component when it renders. Similar to the logic found in {@link IRequestCycle#peekUniqueId(String)}.
+     *
+     * @param component
+     *          The component to determine the next client id for.
      * 
      * @return The next possible client ID for the component.
      */
@@ -106,13 +138,17 @@
     /**
      * Returns true if the form is rewinding (meaning, the form was the subject of the request
      * cycle).
+     *
+     * @return True if the form is rewinding, false otherwise.
      */
 
     boolean isRewinding();
 
     /**
      * May be invoked by a component to force the encoding type of the form to a particular value.
-     * 
+     *
+     * @param encodingType
+     *          The encoding type to set.
      * @see org.apache.tapestry.form.Upload
      * @throws ApplicationRuntimeException
      *             if the current encoding type is not null and doesn't match the provided encoding
@@ -142,6 +178,11 @@
      * Invoked by a form control component (a field) that may have been pre-rendered. If the field
      * was pre-rendered, then the buffered output is printed into the writer and true is returned.
      * Otherwise, false is returned.
+     *
+     * @param writer
+     *          The markup writer to render with. (may be ignored during dynamic requests)
+     * @param field
+     *          The component to check for pre-rendering.
      * 
      * @return true if the field was pre-rendered and should do nothing during its render phase,
      *         false if the field should continue as normal.

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IScriptProcessor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IScriptProcessor.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IScriptProcessor.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IScriptProcessor.java Tue Aug 14 11:49:37 2007
@@ -72,7 +72,22 @@
      *          The script to add.
      */
     void addInitializationScript(IComponent target, String script);
-    
+
+    /**
+     * Works in the same way as {@link #addInitializationScript(IComponent, String)} - except this
+     * method causes the script being added to appear <em>after</em> all of the script content written out
+     * from the normal initialization script processing happens.  This is useful if you have some initialization
+     * script logic that absolutely must happen at the very end of the rest of things.
+     *
+     * @see {@link #addInitializationScript(IComponent, String)}.
+     * 
+     * @param target
+     *          The component the script is being added for.
+     * @param script
+     *          The script to add.
+     */
+    void addScriptAfterInitialization(IComponent target, String script);
+
     /**
      * Adds an external script. The processor is expected to ensure that external scripts are only
      * loaded a single time per page.

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/PageRenderSupport.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/PageRenderSupport.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/PageRenderSupport.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/PageRenderSupport.java Tue Aug 14 11:49:37 2007
@@ -30,7 +30,10 @@
      * a snippet of JavaScript expression that can be inserted into some larger block of JavaScript
      * as a function parameter, or as a property assignment. A typical return value might be
      * <code>tapestry_preload[7].src</code>.
-     * 
+     *
+     * @param url
+     *          The url to the resource to be preloaded.
+     * @return Reference to preload image url.
      * @deprecated To be removed in 4.2, use {@link #getPreloadedImageReference(IComponent, String)} 
      *              or {@link #getPreloadedImageReference(IComponent, IAsset)} instead.
      */
@@ -55,8 +58,11 @@
      * as a function parameter, or as a property assignment. A typical return value might be
      * <code>tapestry_preload[7].src</code>.
      * 
-     * @param target The component url being loaded for
-     * @param url The asset URL to preload
+     * @param target
+     *          The component url being loaded for.
+     * @param url
+     *          The asset URL to preload.
+     * @return Reference to preload image url.
      */
     
     String getPreloadedImageReference(IComponent target, String url);

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/form/FormSupportImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/form/FormSupportImpl.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/form/FormSupportImpl.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/form/FormSupportImpl.java Tue Aug 14 11:49:37 2007
@@ -31,7 +31,7 @@
 
 /**
  * Encapsulates most of the behavior of a Form component.
- * 
+ *
  */
 public class FormSupportImpl implements FormSupport
 {
@@ -57,14 +57,14 @@
      */
 
     public static final String SUBMIT_MODE = "submitmode";
-    
+
     /**
      * Attribute set to true when a field has been focused; used to prevent conflicting JavaScript
      * for field focusing from being emitted.
      */
 
     public static final String FIELD_FOCUS_ATTRIBUTE = "org.apache.tapestry.field-focused";
-    
+
     private static final Set _standardReservedIds;
 
     static
@@ -76,7 +76,7 @@
         set.add(RESERVED_FORM_IDS);
         set.add(SUBMIT_MODE);
         set.add(FormConstants.SUBMIT_NAME_PARAMETER);
-        
+
         _standardReservedIds = Collections.unmodifiableSet(set);
     }
 
@@ -93,9 +93,9 @@
     }
 
     protected final IRequestCycle _cycle;
-    
+
     protected final IdAllocator _elementIdAllocator = new IdAllocator();
-    
+
     /**
      * Used when rewinding the form to figure to match allocated ids (allocated during the rewind)
      * against expected ids (allocated in the previous request cycle, when the form was rendered).
@@ -144,12 +144,12 @@
      * Client side validation is built up using a json object syntax structure
      */
     private final JSONObject _profile;
-    
+
     /**
      * Used to detect whether or not a form component has been updated and will require form sync on ajax requests
      */
     private boolean _fieldUpdating;
-    
+
     public FormSupportImpl(IMarkupWriter writer, IRequestCycle cycle, IForm form)
     {
         Defense.notNull(writer, "writer");
@@ -160,17 +160,17 @@
         _cycle = cycle;
         _form = form;
         _delegate = form.getDelegate();
-        
+
         _rewinding = cycle.isRewound(form);
         _allocatedIdIndex = 0;
-        
+
         _pageRenderSupport = TapestryUtils.getOptionalPageRenderSupport(cycle);
         _profile = new JSONObject();
     }
 
     /**
      * Alternate constructor used for testing only.
-     * 
+     *
      * @param cycle
      *          The current cycle.
      */
@@ -192,7 +192,7 @@
     {
         return _form;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -225,7 +225,7 @@
      * component whose id is 'page'. A certain number of ids are always reserved, and we find any
      * additional ids beyond that set.
      */
-    
+
     private void addHiddenFieldsForLinkParameters(ILink link)
     {
         String[] names = link.getParameterNames();
@@ -258,14 +258,14 @@
                 sep = ",";
                 hasExtra = true;
             }
-            
+
             addHiddenFieldsForLinkParameter(link, name);
         }
-        
+
         if (hasExtra)
             addHiddenValue(RESERVED_FORM_IDS, extraIds.toString());
     }
-    
+
     public void addHiddenValue(String name, String value)
     {
         _hiddenValues.add(new HiddenFieldData(name, value));
@@ -364,7 +364,7 @@
     {
         return getElementId(component, component.getSpecifiedId());
     }
-    
+
     /**
      * Constructs a unique identifier (within the Form). The identifier consists of the component's
      * id, with an index number added to ensure uniqueness.
@@ -377,28 +377,28 @@
     public String getElementId(IFormComponent component, String baseId)
     {
         // $ is not a valid character in an XML/XHTML id, so convert it to an underscore.
-        
+
         String filteredId = TapestryUtils.convertTapestryIdToNMToken(baseId);
-        
+
         String result = _elementIdAllocator.allocateId(filteredId);
-        
+
         if (_rewinding)
         {
             if (_allocatedIdIndex >= _allocatedIds.size())
             {
-                throw new StaleLinkException(FormMessages.formTooManyIds(_form, _allocatedIds
-                        .size(), component), component);
+                throw new StaleLinkException(FormMessages.formTooManyIds(_form, _allocatedIds.size(),
+                                                                         component), component);
             }
-            
+
             String expected = (String) _allocatedIds.get(_allocatedIdIndex);
-            
+
             if (!result.equals(expected))
                 throw new StaleLinkException(FormMessages.formIdMismatch(
-                        _form,
-                        _allocatedIdIndex,
-                        expected,
-                        result,
-                        component), component);
+                  _form,
+                  _allocatedIdIndex,
+                  expected,
+                  result,
+                  component), component);
         }
         else
         {
@@ -406,11 +406,10 @@
         }
 
         _allocatedIdIndex++;
-        
+
         component.setName(result);
-        
         component.setClientId(result);
-        
+
         return result;
     }
 
@@ -425,7 +424,7 @@
 
         return _elementIdAllocator.peekNextId(id);
     }
-    
+
     public boolean isRewinding()
     {
         return _rewinding;
@@ -442,7 +441,7 @@
      * Converts a string passed as a parameter (and containing a comma separated list of ids) back
      * into the allocateIds property. In addition, return the state of the ID allocater back to
      * where it was at the start of the render.
-     * 
+     *
      * @see #buildAllocatedIdList()
      * @since 3.0
      */
@@ -450,9 +449,9 @@
     private void reinitializeIdAllocatorForRewind()
     {
         String allocatedFormIds = _cycle.getParameter(FORM_IDS);
-        
+
         String[] ids = TapestryUtils.split(allocatedFormIds);
-        
+
         for (int i = 0; i < ids.length; i++)
             _allocatedIds.add(ids[i]);
 
@@ -468,17 +467,17 @@
         for (int i = 0; i < ids.length; i++)
             _elementIdAllocator.allocateId(ids[i]);
     }
-    
+
     int convertSeedToId(String input)
     {
         int index = input.lastIndexOf("_");
-        
+
         if (index < 0)
             throw new ApplicationRuntimeException("Unable to convert seedId of " + input + " to integer.");
-        
+
         return Integer.parseInt(input.substring(index, input.length()));
     }
-    
+
     public void render(String method, IRender informalParametersRenderer, ILink link, String scheme, Integer port)
     {
         String formId = _form.getName();
@@ -494,25 +493,26 @@
         // client-side JavaScript forces an update.
 
         addHiddenValue(SUBMIT_MODE, null);
-        
+
         // And another for the name of the component that
         // triggered the submit.
 
         addHiddenValue(FormConstants.SUBMIT_NAME_PARAMETER, null);
-        
+
         IMarkupWriter nested = _writer.getNestedWriter();
 
         _form.renderBody(nested, _cycle);
 
         runDeferredRunnables();
-        
+
         int portI = (port == null) ? 0 : port.intValue();
-        
+
         writeTag(_writer, method, link.getURL(scheme, null, portI, null, false));
-        
+
         // For XHTML compatibility
+
         _writer.attribute("id", formId);
-        
+
         if (_encodingType != null)
             _writer.attribute("enctype", _encodingType);
 
@@ -527,49 +527,55 @@
         _writer.println();
 
         writeHiddenFields();
-        
+
         // Close the nested writer, inserting its contents.
-        
+
         nested.close();
-        
+
         // Close the <form> tag.
-        
+
         _writer.end();
-        
+
         String fieldId = _delegate.getFocusField();
-        
+
         if (_pageRenderSupport == null)
             return;
-        
+
         // If the form doesn't support focus, or the focus has already been set by a different form,
         // then do nothing.
-        
-        if (!_cycle.isFocusDisabled() && fieldId != null && _form.getFocus() 
-                && _cycle.getAttribute(FIELD_FOCUS_ATTRIBUTE) == null)
+
+        if (!_cycle.isFocusDisabled() && fieldId != null && _form.getFocus()
+            && _cycle.getAttribute(FIELD_FOCUS_ATTRIBUTE) == null)
         {
-            _pageRenderSupport.addInitializationScript(_form, "dojo.require(\"tapestry.form\");tapestry.form.focusField('" + fieldId + "');");
+            _pageRenderSupport.addInitializationScript(_form, "dojo.require(\"tapestry.form\");");
+
+            // needs to happen last to avoid dialog issues in ie - TAPESTRY-1705
+            _pageRenderSupport.addScriptAfterInitialization(_form, "tapestry.form.focusField('" + fieldId + "');");
+
             _cycle.setAttribute(FIELD_FOCUS_ATTRIBUTE, Boolean.TRUE);
         }
-        
+
         // register the validation profile with client side form manager
-        
+
         if (_form.isClientValidationEnabled())
         {
             IPage page = _form.getPage();
 
             // only include dojo widget layer if it's not already been included
 
-            if (!page.hasWidgets()) {
+            if (!page.hasWidgets())
+            {
                 IAsset clientScript = _form.getAsset("clientValidationScript");
-                if (clientScript != null){
 
+                if (clientScript != null)
+                {
                     _pageRenderSupport.addExternalScript(_form, clientScript.getResourceLocation());
                 }
             }
-            
+
             _pageRenderSupport.addInitializationScript(_form, "dojo.require(\"tapestry.form\");tapestry.form.clearProfiles('"
-                    + formId + "'); tapestry.form.registerProfile('" + formId + "'," 
-                    + _profile.toString() + ");");
+                                                              + formId + "'); tapestry.form.registerProfile('" + formId + "',"
+                                                              + _profile.toString() + ");");
         }
     }
 
@@ -584,77 +590,77 @@
     {
         if (_pageRenderSupport == null)
             return;
-        
+
         StringBuffer str = new StringBuffer("dojo.require(\"tapestry.form\");");
         str.append("tapestry.form.registerForm(\"").append(formId).append("\"");
-        
-        if (_form.isAsync()) {
-            
+
+        if (_form.isAsync())
+        {
             str.append(", true");
-            
-            if (_form.isJson()) {
+
+            if (_form.isJson())
+            {
                 str.append(", true");
             }
         }
-        
+
         str.append(");");
-        
+
         _pageRenderSupport.addInitializationScript(_form, str.toString());
     }
-    
+
     public String rewind()
     {
         _form.getDelegate().clear();
 
         String mode = _cycle.getParameter(SUBMIT_MODE);
-        
+
         // On a cancel, don't bother rendering the body or anything else at all.
-        
+
         if (FormConstants.SUBMIT_CANCEL.equals(mode))
             return mode;
-        
+
         reinitializeIdAllocatorForRewind();
-        
+
         _form.renderBody(_writer, _cycle);
-        
+
         // New, handles cases where an eventlistener
         // causes a form submission.
-        
+
         BrowserEvent event = new BrowserEvent(_cycle);
-        
+
         _form.getEventInvoker().invokeFormListeners(this, _cycle, event);
 
         int expected = _allocatedIds.size();
-        
+
         // The other case, _allocatedIdIndex > expected, is
         // checked for inside getElementId(). Remember that
         // _allocatedIdIndex is incremented after allocating.
-        
+
         if (_allocatedIdIndex < expected)
         {
             String nextExpectedId = (String) _allocatedIds.get(_allocatedIdIndex);
-            
-            throw new StaleLinkException(FormMessages.formTooFewIds(_form, expected
-                    - _allocatedIdIndex, nextExpectedId), _form);
+
+            throw new StaleLinkException(FormMessages.formTooFewIds(_form, expected - _allocatedIdIndex, nextExpectedId), _form);
         }
-        
-        runDeferredRunnables();
 
-        if (_submitModes.contains(mode)) {
+        runDeferredRunnables();
 
+        if (_submitModes.contains(mode))
+        {
             // clear errors during refresh
-            
-            if (FormConstants.SUBMIT_REFRESH.equals(mode)) {
 
+            if (FormConstants.SUBMIT_REFRESH.equals(mode))
+            {
                 _form.getDelegate().clearErrors();
             }
 
             return mode;
         }
-        
+
         // Either something wacky on the client side, or a client without
         // javascript enabled.
-        
+
         return FormConstants.SUBMIT_NORMAL;
     }
 
@@ -664,7 +670,7 @@
         while (i.hasNext())
         {
             Runnable r = (Runnable) i.next();
-            
+
             r.run();
         }
     }
@@ -674,9 +680,9 @@
 
         if (_encodingType != null && !_encodingType.equals(encodingType))
             throw new ApplicationRuntimeException(FormMessages.encodingTypeContention(
-                    _form,
-                    _encodingType,
-                    encodingType), _form, null, null);
+              _form,
+              _encodingType,
+              encodingType), _form, null, null);
 
         _encodingType = encodingType;
     }
@@ -689,10 +695,10 @@
         writer.beginEmpty("input");
         writer.attribute("type", "hidden");
         writer.attribute("name", name);
-        
+
         if (HiveMind.isNonBlank(id))
             writer.attribute("id", id);
-        
+
         writer.attribute("value", value == null ? "" : value);
         writer.println();
     }
@@ -703,57 +709,56 @@
      * {@link #writeHiddenFieldList(IMarkupWriter)}. Overriden by
      * {@link org.apache.tapestry.wml.GoFormSupportImpl}.
      */
-    
+
     protected void writeHiddenFields()
     {
         IMarkupWriter writer = getHiddenFieldWriter();
-        
+
         writer.begin("div");
         writer.attribute("style", "display:none;");
         writer.attribute("id", _form.getName() + "hidden");
-        
+
         writeHiddenFieldList(writer);
-        
+
         writer.end();
     }
-    
+
     /**
      * Writes out all hidden values previously added by
      * {@link #addHiddenValue(String, String, String)}, plus the allocated id list.
      */
-    
+
     protected void writeHiddenFieldList(IMarkupWriter writer)
     {
         writeHiddenField(writer, FORM_IDS, null, buildAllocatedIdList());
-        
+
         Iterator i = _hiddenValues.iterator();
         while (i.hasNext())
         {
             HiddenFieldData data = (HiddenFieldData) i.next();
-            
+
             writeHiddenField(writer, data.getName(), data.getId(), data.getValue());
         }
     }
-    
+
     /**
      * Determines if a hidden field change has occurred, which would require
      * that we write hidden form fields using the {@link ResponseBuilder} 
      * writer.
-     * 
+     *
      * @return The default {@link IMarkupWriter} if not doing a managed ajax/json
      *          response, else whatever is returned from {@link ResponseBuilder}.
      */
     protected IMarkupWriter getHiddenFieldWriter()
     {
-        if (_cycle.getResponseBuilder().contains(_form) 
-                || (!_fieldUpdating || !_cycle.getResponseBuilder().isDynamic()) ) {
+        if (_cycle.getResponseBuilder().contains(_form)
+            || (!_fieldUpdating || !_cycle.getResponseBuilder().isDynamic()) )
             return _writer;
-        }
-        
-        return _cycle.getResponseBuilder().getWriter(_form.getName() + "hidden", 
-                ResponseBuilder.ELEMENT_TYPE);
+
+        return _cycle.getResponseBuilder().getWriter(_form.getName() + "hidden",
+                                                     ResponseBuilder.ELEMENT_TYPE);
     }
-    
+
     private void addHiddenFieldsForLinkParameter(ILink link, String parameterName)
     {
         String[] values = link.getParameterValues(parameterName);
@@ -764,9 +769,7 @@
             return;
 
         for (int i = 0; i < values.length; i++)
-        {
             addHiddenValue(parameterName, values[i]);
-        }
     }
 
     protected void writeTag(IMarkupWriter writer, String method, String url)
@@ -784,19 +787,20 @@
         String key = field.getExtendedId();
 
         if (_prerenderMap.containsKey(key))
-            throw new ApplicationRuntimeException(FormMessages.fieldAlreadyPrerendered(field), field, location, null);
-        
+            throw new ApplicationRuntimeException(FormMessages.fieldAlreadyPrerendered(field),
+                                                  field, location, null);
+
         NestedMarkupWriter nested = writer.getNestedWriter();
-        
+
         TapestryUtils.storePrerender(_cycle, field);
-        
+
         _cycle.getResponseBuilder().render(nested, field, _cycle);
-        
+
         TapestryUtils.removePrerender(_cycle);
-        
+
         _prerenderMap.put(key, nested.getBuffer());
     }
-    
+
     public boolean wasPrerendered(IMarkupWriter writer, IComponent field)
     {
         String key = field.getExtendedId();
@@ -833,7 +837,7 @@
         _delegate.registerForFocus(field, priority);
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public JSONObject getProfile()
@@ -841,15 +845,15 @@
         return _profile;
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public boolean isFormFieldUpdating()
     {
         return _fieldUpdating;
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void setFormFieldUpdating(boolean value)

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/script/ScriptSessionImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/script/ScriptSessionImpl.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/script/ScriptSessionImpl.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/script/ScriptSessionImpl.java Tue Aug 14 11:49:37 2007
@@ -14,8 +14,6 @@
 
 package org.apache.tapestry.script;
 
-import java.util.Map;
-
 import org.apache.hivemind.Resource;
 import org.apache.tapestry.IComponent;
 import org.apache.tapestry.IRequestCycle;
@@ -23,13 +21,15 @@
 import org.apache.tapestry.coerce.ValueConverter;
 import org.apache.tapestry.services.ExpressionEvaluator;
 
+import java.util.Map;
+
 /**
  * The result of executing a script, the session is used during the parsing
  * process as well. Following
  * {@link org.apache.tapestry.IScript#execute(IComponent, org.apache.tapestry.IRequestCycle, org.apache.tapestry.IScriptProcessor, java.util.Map)},
  * the session provides access to output symbols as well as the body and
  * initialization blocks created by the script tokens.
- * 
+ *
  * @author Howard Lewis Ship
  * @since 0.2.9
  */
@@ -52,11 +52,11 @@
     private ValueConverter _valueConverter;
 
     private IComponent _component;
-    
+
     public ScriptSessionImpl(Resource scriptTemplateResource,
-            IRequestCycle cycle, IScriptProcessor processor,
-            ExpressionEvaluator evaluator, ValueConverter valueConverter,
-            Map symbols)
+                             IRequestCycle cycle, IScriptProcessor processor,
+                             ExpressionEvaluator evaluator, ValueConverter valueConverter,
+                             Map symbols)
     {
         _scriptTemplateResource = scriptTemplateResource;
         _cycle = cycle;
@@ -65,12 +65,12 @@
         _evaluator = evaluator;
         _valueConverter = valueConverter;
     }
-    
+
     public ScriptSessionImpl(Resource scriptTemplateResource,
-            IComponent component, 
-            IRequestCycle cycle, IScriptProcessor processor,
-            ExpressionEvaluator evaluator, ValueConverter valueConverter,
-            Map symbols)
+                             IComponent component,
+                             IRequestCycle cycle, IScriptProcessor processor,
+                             ExpressionEvaluator evaluator, ValueConverter valueConverter,
+                             Map symbols)
     {
         _scriptTemplateResource = scriptTemplateResource;
         _component = component;
@@ -112,7 +112,7 @@
     {
         addBodyScript(_component, script);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -142,12 +142,12 @@
         if (_processor.isBodyScriptAllowed(target))
             _processor.addBodyScript(target, script);
     }
-    
+
     public void addExternalScript(Resource resource)
     {
         addExternalScript(_component, resource);
     }
-    
+
     public void addExternalScript(IComponent target, Resource resource)
     {
         if (_processor.isExternalScriptAllowed(target))
@@ -163,6 +163,12 @@
     {
         if (_processor.isInitializationScriptAllowed(target))
             _processor.addInitializationScript(target, script);
+    }
+
+    public void addScriptAfterInitialization(IComponent target, String script)
+    {
+        if (_processor.isInitializationScriptAllowed(target))
+            _processor.addScriptAfterInitialization(target, script);
     }
 
     public String getUniqueString(String baseValue)

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/ResponseBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/ResponseBuilder.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/ResponseBuilder.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/ResponseBuilder.java Tue Aug 14 11:49:37 2007
@@ -46,7 +46,7 @@
  * @since 4.1
  */
 public interface ResponseBuilder extends PageRenderSupport {
-    
+
     /**
      * Inside a {@link org.apache.tapestry.util.ContentType}, the output encoding is called
      * "charset".
@@ -67,123 +67,125 @@
      * The response exception type.
      */
     String EXCEPTION_TYPE = "exception";
-    
+
     /**
      * The response element type denoting a brand new page render.
      */
     String PAGE_TYPE = "page";
-    
+
     String SCRIPT_TYPE = "script";
-    
+
     String BODY_SCRIPT = "bodyscript";
-    
+
     String INCLUDE_SCRIPT = "includescript";
-    
+
     String INITIALIZATION_SCRIPT = "initializationscript";
-    
+
     /**
      * Implementors that manage content writes dynamically (ie {@link DojoAjaxResponseBuilder}) should
      * return true to denote that dynamic behaviour is on for a particular response.
-     * 
+     *
      * @return Whether or not request is dynamic.
      */
     boolean isDynamic();
-    
+
     /**
      * Causes the output stream to be flushed, used primarily in concert with {@link IRequestCycle} to sync
      * up flushing of headers to the browser once any page changes have been committed.
-     * 
-     * @throws IOException
+     *
+     * @throws IOException During io error.
      */
     void flush()
-    throws IOException;
-    
+      throws IOException;
+
     /**
      * Renders the response to a client. Handles transitioning logic
      * for setting up page and associated components for response.
-     * 
+     *
      * @param cycle
      *          The main request cycle object for this request.
+     *
+     * @throws IOException During io error.
      */
-    
+
     void renderResponse(IRequestCycle cycle)
-    throws IOException;
-    
+      throws IOException;
+
     /**
      * Invoked to render a renderable object. Performs any necessary
      * under the hood type logic involving ajax/json/normal responses, where
      * needed.
-     * 
-     * @param writer 
+     *
+     * @param writer
      *          The markup writer to use, this may be ignored or swapped
      *          out for a different writer depending on the implementation being used.
      * @param render The renderable object to render
      * @param cycle Render request cycle
      */
-    
+
     void render(IMarkupWriter writer, IRender render, IRequestCycle cycle);
-    
+
     /**
      * If the component identified by the specified id isn't already set to
      * be updated, will add it to the response for updating. (Only applicable
      * in dynamic responses such as XHR/JSON ).
-     * 
+     *
      * @param id
      *          The {@link IComponent} id to update.
      */
     void updateComponent(String id);
-    
+
     /**
      * Checks if the rendered response contains a particular component. Contains
      * can mean many things. In the instance of a dynamic response it could potentially
      * mean a component explicitly set to be updated - or a component that has a containing
      * component explicitly set to be updated.
-     * 
+     *
      * @param target The component to check containment of.
      * @return True if response contains the specified component, false otherwise.
      */
     boolean contains(IComponent target);
-    
+
     /**
      * Similar to {@link #contains(IComponent)}, but only returns true if the component
      * has been marked for update directly via an <code>updateComponents</code> property 
      * or by calling {@link ResponseBuilder#updateComponent(String)} directly. 
-     * 
+     *
      * <p>
      * <b>IMPORTANT!:</b> This will not return true for components contained by a component
      *  marked for update. If you want that kind of behaviour use {@link #contains(IComponent)}. 
      * </p>
-     * 
+     *
      * @param target The component to check.
      * @return True if the component as listed as one to be updated, false otherwise.
      */
     boolean explicitlyContains(IComponent target);
-    
+
     /**
      * Invoked by components that know "when" the method should be called. Causes all queued up
      * body related javascript data to be written out to the response.
-     * 
+     *
      * @param writer
      *          The writer to use . (may / may not be ignored depending on the response type)
      * @param cycle
      *          Associated request.
      */
     void writeBodyScript(IMarkupWriter writer, IRequestCycle cycle);
-    
+
     /**
      * Invoked by components that know "when" the method should be called. Causes all queued up
      * initialization related javascript data to be written out to the response.
-     * 
+     *
      * @param writer
      *          The writer to use . (may / may not be ignored depending on the response type)
      */
     void writeInitializationScript(IMarkupWriter writer);
-    
+
     /**
      * Invoked by {@link PageRenderSupport} to write external js package
      * includes. This method will be invoked for each external script requesting
      * inclusion in the response.
-     * 
+     *
      * These will typically be written out as 
      * <code>
      * <script type="text/javascript" src="url"></script>
@@ -196,9 +198,9 @@
      *          The absolute url to the .js package to be included.
      * @param cycle
      *          The associated request.
-     */    
+     */
     void writeExternalScript(IMarkupWriter writer, String url, IRequestCycle cycle);
-    
+
     /**
      * Marks the beginning of the core body script.
      *
@@ -209,7 +211,7 @@
      *          The associated request.
      */
     void beginBodyScript(IMarkupWriter writer, IRequestCycle cycle);
-    
+
     /**
      * Intended to be written within the confines of the body script, should
      * be invoked once just after {@link #beginBodyScript(IMarkupWriter, IRequestCycle)} is called
@@ -222,13 +224,13 @@
      *          out for a different writer depending on the implementation being used.
      * @param script
      *          The non null value of the script images to include. 
-     * @param preloadName 
+     * @param preloadName
      *          The global variable name to give to the preloaded images array.
      * @param cycle
      *          The associated request.
      */
     void writeImageInitializations(IMarkupWriter writer, String script, String preloadName, IRequestCycle cycle);
-    
+
     /**
      * Called after {@link #beginBodyScript(IMarkupWriter, IRequestCycle)} to write the containing
      * body script. This method may not be called at all if there is no js body 
@@ -243,7 +245,7 @@
      *          The associated request.
      */
     void writeBodyScript(IMarkupWriter writer, String script, IRequestCycle cycle);
-    
+
     /**
      * Marks the end of the body block being called. This method will 
      * always be called if {@link #beginBodyScript(IMarkupWriter, IRequestCycle)} was previously
@@ -256,13 +258,13 @@
      *          The associated request.
      */
     void endBodyScript(IMarkupWriter writer, IRequestCycle cycle);
-    
+
     /**
      * Writes any javascript that should only execute after all other items
      * on a page have completed rendering. This is typically implemented via
      * wrapping the executing of the code to some sort of <code>window.onload</code> 
      * event, but will vary depending on the implementation of the builder being used.
-     * 
+     *
      * This method will ~only~ be called if there is any queued intialization script 
      * to write.
      *
@@ -273,23 +275,23 @@
      *          The initialzation script to write.
      */
     void writeInitializationScript(IMarkupWriter writer, String script);
-    
+
     /**
      * Returns the IMarkupWriter associated with this response, it may or may
      * not be a NullWriter instance depending on the response type or stage 
      * of the render cycle. (specifically during rewind)
-     * 
+     *
      * @return A validly writable markup writer, even if the content is sometimes
      * ignored.
      */
-    
+
     IMarkupWriter getWriter();
-    
+
     /**
      * Gets a write that will output its content in a <code>response</code>
      * element with the given id and type. 
-     * 
-     * @param id 
+     *
+     * @param id
      *          The response element id to give writer.
      * @param type
      *          Optional - If specified will give the response element a type
@@ -297,17 +299,17 @@
      * @return A valid {@link IMarkupWriter} instance to write content to.
      */
     IMarkupWriter getWriter(String id, String type);
-    
+
     /**
      * Determines if the specified component should have any asset image URL
      * references embedded in the response.
-     * 
+     *
      * @param target
      *          The component to allow/disallow image initialization script content from.
      * @return True if the component script should be allowed.
      */
     boolean isImageInitializationAllowed(IComponent target);
-    
+
     /**
      * Adds a status message to the current response.
      *

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DefaultResponseBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DefaultResponseBuilder.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DefaultResponseBuilder.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DefaultResponseBuilder.java Tue Aug 14 11:49:37 2007
@@ -31,42 +31,42 @@
 
 /**
  * Manages normal html responses for tapestry request/response cycles.
- * 
+ *
  * @author jkuhnert
  */
 public class DefaultResponseBuilder implements ResponseBuilder
-{   
+{
     private final AssetFactory _assetFactory;
-    
+
     private final String _namespace;
-    
+
     private PageRenderSupportImpl _prs;
-    
+
     private RequestLocaleManager _localeManager;
-    
+
     private MarkupWriterSource _markupWriterSource;
 
     private WebResponse _webResponse;
-    
+
     /** Default writer for rendering html output. */
     private IMarkupWriter _writer;
-    
+
     private boolean _closeWriter = true;
-    
+
     /**
      * Portlet constructor.
-     * 
+     *
      * @param writer
      */
-    public DefaultResponseBuilder(IMarkupWriter writer, 
-            AssetFactory assetFactory, String namespace, boolean closeWriter)
+    public DefaultResponseBuilder(IMarkupWriter writer,
+                                  AssetFactory assetFactory, String namespace, boolean closeWriter)
     {
         _writer = writer;
         _assetFactory = assetFactory;
         _namespace = namespace;
         _closeWriter = closeWriter;
     }
-    
+
     /**
      * Used in testing only.
      * @param writer
@@ -77,58 +77,57 @@
         _assetFactory = null;
         _namespace = null;
     }
-    
+
     /**
      * Creates a new response builder with the required services it needs
      * to render the response when {@link #renderResponse(IRequestCycle)} is called.
-     * 
-     * @param localeManager 
+     *
+     * @param localeManager
      *          Used to set the locale on the response.
      * @param markupWriterSource
      *          Creates IMarkupWriter instance to be used.
      * @param webResponse
      *          Web response for output stream.
      */
-    public DefaultResponseBuilder(RequestLocaleManager localeManager, 
-            MarkupWriterSource markupWriterSource, WebResponse webResponse,
-            AssetFactory assetFactory, String namespace)
+    public DefaultResponseBuilder(RequestLocaleManager localeManager,
+                                  MarkupWriterSource markupWriterSource, WebResponse webResponse,
+                                  AssetFactory assetFactory, String namespace)
     {
         Defense.notNull(assetFactory, "assetService");
-        
+
         _localeManager = localeManager;
         _markupWriterSource = markupWriterSource;
         _webResponse = webResponse;
-        
+
         // Used by PageRenderSupport
-        
+
         _assetFactory = assetFactory;
         _namespace = namespace;
     }
-    
+
     /**
-     * 
+     *
      * {@inheritDoc}
      */
     public boolean isDynamic()
     {
         return false;
     }
-    
+
     /**
-     * 
+     *
      * {@inheritDoc}
      */
     public void renderResponse(IRequestCycle cycle)
-    throws IOException
+      throws IOException
     {
-        if (_writer == null) {
-            
+        if (_writer == null)
+        {
             _localeManager.persistLocale();
-            
+
             IPage page = cycle.getPage();
 
             ContentType contentType = page.getResponseContentType();
-
             String encoding = contentType.getParameter(ENCODING_KEY);
 
             if (encoding == null)
@@ -137,39 +136,39 @@
 
                 contentType.setParameter(ENCODING_KEY, encoding);
             }
-            
+
             PrintWriter printWriter = _webResponse.getPrintWriter(contentType);
-            
+
             _writer = _markupWriterSource.newMarkupWriter(printWriter, contentType);
         }
-        
+
         // render response
-        
+
         _prs = new PageRenderSupportImpl(_assetFactory, _namespace, cycle.getPage().getLocation(), this);
-        
+
         TapestryUtils.storePageRenderSupport(cycle, _prs);
-        
+
         cycle.renderPage(this);
-        
+
         TapestryUtils.removePageRenderSupport(cycle);
-        
+
         flush();
 
         if (_closeWriter)
             _writer.close();
     }
-    
+
     public void flush()
-    throws IOException
+      throws IOException
     {
         // Important - causes any cookies stored to properly be written out before the
         // rest of the response starts being written - see TAPESTRY-825
 
         _writer.flush();
     }
-    
+
     /**
-     * 
+     *
      * {@inheritDoc}
      */
     public void render(IMarkupWriter writer, IRender render, IRequestCycle cycle)
@@ -179,22 +178,22 @@
         else
             render.render(writer, cycle);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void updateComponent(String id)
     {
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public boolean contains(IComponent target)
     {
         return false;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -203,29 +202,29 @@
         return false;
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public IMarkupWriter getWriter()
     {
         if (_writer == null)
             return NullWriter.getSharedInstance();
-        
+
         return _writer;
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public IMarkupWriter getWriter(String id, String type)
     {
         if (_writer == null)
             return NullWriter.getSharedInstance();
-        
+
         return _writer;
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public boolean isBodyScriptAllowed(IComponent target)
@@ -233,7 +232,7 @@
         return true;
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public boolean isExternalScriptAllowed(IComponent target)
@@ -241,14 +240,14 @@
         return true;
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public boolean isInitializationScriptAllowed(IComponent target)
     {
         return true;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -256,7 +255,7 @@
     {
         return true;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -264,7 +263,7 @@
     {
         return _prs.getPreloadedImageReference(target, source);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -296,7 +295,7 @@
     {
         _prs.addBodyScript(script);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -329,6 +328,11 @@
         _prs.addInitializationScript(script);
     }
 
+    public void addScriptAfterInitialization(IComponent target, String script)
+    {
+        _prs.addScriptAfterInitialization(target, script);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -336,7 +340,7 @@
     {
         return _prs.getUniqueString(baseValue);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -344,7 +348,7 @@
     {
         _prs.writeBodyScript(writer, cycle);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -352,8 +356,8 @@
     {
         _prs.writeInitializationScript(writer);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void beginBodyScript(IMarkupWriter writer, IRequestCycle cycle)
@@ -363,8 +367,8 @@
         writer.printRaw("<!--");
         writer.println();
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void endBodyScript(IMarkupWriter writer, IRequestCycle cycle)
@@ -374,7 +378,7 @@
         writer.end();
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public void writeBodyScript(IMarkupWriter writer, String script, IRequestCycle cycle)
@@ -382,37 +386,37 @@
         writer.printRaw(script);
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public void writeExternalScript(IMarkupWriter writer, String url, IRequestCycle cycle)
-    {        
+    {
         writer.begin("script");
         writer.attribute("type", "text/javascript");
         writer.attribute("src", url);
         writer.end();
         writer.println();
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void writeImageInitializations(IMarkupWriter writer, String script, String preloadName, IRequestCycle cycle)
     {
-        
+
         writer.println();
         writer.printRaw("dojo.addOnLoad(function(e) {\n");
-        
+
         writer.printRaw(preloadName + " = [];\n");
         writer.printRaw("if (document.images)\n");
         writer.printRaw("{\n");
         writer.printRaw(script);
         writer.printRaw("}\n");
-        
+
         writer.printRaw("});");
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void writeInitializationScript(IMarkupWriter writer, String script)
@@ -420,13 +424,13 @@
         writer.begin("script");
         writer.attribute("type", "text/javascript");
         writer.printRaw("<!--\n");
-        
+
         writer.printRaw("dojo.addOnLoad(function(e) {\n");
-        
+
         writer.printRaw(script);
-        
+
         writer.printRaw("});");
-        
+
         writer.printRaw("\n// -->");
         writer.end();
     }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DojoAjaxResponseBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DojoAjaxResponseBuilder.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DojoAjaxResponseBuilder.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/DojoAjaxResponseBuilder.java Tue Aug 14 11:49:37 2007
@@ -13,27 +13,11 @@
 // limitations under the License.
 package org.apache.tapestry.services.impl;
 
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hivemind.Resource;
 import org.apache.hivemind.util.Defense;
-import org.apache.tapestry.IAsset;
-import org.apache.tapestry.IComponent;
-import org.apache.tapestry.IForm;
-import org.apache.tapestry.IMarkupWriter;
-import org.apache.tapestry.IPage;
-import org.apache.tapestry.IRender;
-import org.apache.tapestry.IRequestCycle;
-import org.apache.tapestry.NestedMarkupWriter;
-import org.apache.tapestry.TapestryUtils;
+import org.apache.tapestry.*;
 import org.apache.tapestry.asset.AssetFactory;
 import org.apache.tapestry.engine.IEngineService;
 import org.apache.tapestry.engine.NullWriter;
@@ -47,35 +31,39 @@
 import org.apache.tapestry.util.ScriptUtils;
 import org.apache.tapestry.web.WebResponse;
 
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.*;
+
 
 /**
  * Main class that handles dojo based ajax responses. These responses are wrapped
  * by an xml document format that segments off invididual component/javascript response
  * types into easy to manage xml elements that can then be interpreted and managed by 
  * running client-side javascript.
- * 
+ *
  */
 public class DojoAjaxResponseBuilder implements ResponseBuilder
 {
     private static final Log _log = LogFactory.getLog(DojoAjaxResponseBuilder.class);
 
-	private static final String NEWLINE = System.getProperty("line.separator");
-    
+    private static final String NEWLINE = System.getProperty("line.separator");
+
     private final AssetFactory _assetFactory;
-    
+
     private final String _namespace;
-    
+
     private PageRenderSupportImpl _prs;
-    
+
     // used to create IMarkupWriter
     private RequestLocaleManager _localeManager;
     private MarkupWriterSource _markupWriterSource;
     private WebResponse _response;
-    
+
     private List _errorPages;
-    
+
     private ContentType _contentType;
-    
+
     // our response writer
     private IMarkupWriter _writer;
     // Parts that will be updated.
@@ -84,17 +72,17 @@
     private Map _writers = new HashMap();
     // List of status messages.
     private List _statusMessages;
-    
+
     private IRequestCycle _cycle;
-    
+
     private IEngineService _pageService;
-    
+
     /**
      * Keeps track of renders involving a whole page response, such 
      * as exception pages or pages activated via {@link IRequestCycle#activate(IPage)}.
      */
     private boolean _pageRender = false;
-    
+
     /**
      * Used to keep track of whether or not the appropriate xml response start
      * block has been started.
@@ -154,7 +142,7 @@
      *
      * @param cycle
      *          The current request.
-     * @param localeManager 
+     * @param localeManager
      *          Used to set the locale on the response.
      * @param markupWriterSource
      *          Creates IJSONWriter instance to be used.
@@ -169,42 +157,42 @@
      * @param pageService
      *          {@link org.apache.tapestry.engine.PageService} used to generate page urls.
      */
-    public DojoAjaxResponseBuilder(IRequestCycle cycle, 
-            RequestLocaleManager localeManager, 
-            MarkupWriterSource markupWriterSource,
-            WebResponse webResponse, List errorPages, 
-            AssetFactory assetFactory, String namespace, IEngineService pageService)
+    public DojoAjaxResponseBuilder(IRequestCycle cycle,
+                                   RequestLocaleManager localeManager,
+                                   MarkupWriterSource markupWriterSource,
+                                   WebResponse webResponse, List errorPages,
+                                   AssetFactory assetFactory, String namespace, IEngineService pageService)
     {
         Defense.notNull(cycle, "cycle");
         Defense.notNull(assetFactory, "assetService");
-        
+
         _cycle = cycle;
         _localeManager = localeManager;
         _markupWriterSource = markupWriterSource;
         _response = webResponse;
         _errorPages = errorPages;
         _pageService = pageService;
-        
+
         // Used by PageRenderSupport
-        
+
         _assetFactory = assetFactory;
         _namespace = namespace;
     }
-    
+
     /**
-     * 
+     *
      * {@inheritDoc}
      */
     public boolean isDynamic()
     {
         return true;
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void renderResponse(IRequestCycle cycle)
-        throws IOException
+      throws IOException
     {
         // if response was already started
 
@@ -212,65 +200,66 @@
         {
             // clear out any previous input
             clearPartialWriters();
-            
+
             cycle.renderPage(this);
 
             TapestryUtils.removePageRenderSupport(cycle);
+            
             endResponse();
 
             _writer.close();
-            
+
             return;
         }
-        
+
         _localeManager.persistLocale();
         _contentType = new ContentType(CONTENT_TYPE + ";charset=" + cycle.getInfrastructure().getOutputEncoding());
-        
+
         String encoding = _contentType.getParameter(ENCODING_KEY);
-        
+
         if (encoding == null)
         {
             encoding = cycle.getEngine().getOutputEncoding();
-            
+
             _contentType.setParameter(ENCODING_KEY, encoding);
         }
-        
+
         if (_writer == null)
         {
             parseParameters(cycle);
-            
+
             PrintWriter printWriter = _response.getPrintWriter(_contentType);
             _writer = _markupWriterSource.newMarkupWriter(printWriter, _contentType);
         }
-        
+
         // render response
-        
+
         _prs = new PageRenderSupportImpl(_assetFactory, _namespace, cycle.getPage().getLocation(), this);
-        
+
         TapestryUtils.storePageRenderSupport(cycle, _prs);
-        
+
         cycle.renderPage(this);
-        
+
         TapestryUtils.removePageRenderSupport(cycle);
 
         endResponse();
-        
+
         _writer.close();
     }
-    
+
     public void flush()
-    throws IOException
+      throws IOException
     {
         // Important - causes any cookies stored to properly be written out before the
         // rest of the response starts being written - see TAPESTRY-825
-        
+
         _writer.flush();
-        
+
         if (!_responseStarted)
             beginResponse();
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void updateComponent(String id)
@@ -278,76 +267,76 @@
         if (!_parts.contains(id))
             _parts.add(id);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public IMarkupWriter getWriter()
     {
         return _writer;
     }
-    
+
     void setWriter(IMarkupWriter writer)
     {
         _writer = writer;
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public boolean isBodyScriptAllowed(IComponent target)
     {
         if (_pageRender)
             return true;
-        
-        if (target != null 
-                && IPage.class.isInstance(target)
-                || (IForm.class.isInstance(target)
+
+        if (target != null
+            && IPage.class.isInstance(target)
+            || (IForm.class.isInstance(target)
                 && ((IForm)target).isFormFieldUpdating()))
             return true;
-        
+
         return contains(target);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public boolean isExternalScriptAllowed(IComponent target)
     {
         if (_pageRender)
             return true;
-        
-        if (target != null 
-                && IPage.class.isInstance(target)
-                || (IForm.class.isInstance(target)
+
+        if (target != null
+            && IPage.class.isInstance(target)
+            || (IForm.class.isInstance(target)
                 && ((IForm)target).isFormFieldUpdating()))
             return true;
-        
+
         return contains(target);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public boolean isInitializationScriptAllowed(IComponent target)
     {
-        if (_log.isDebugEnabled()) {
-            
+        if (_log.isDebugEnabled())
+        {
             _log.debug("isInitializationScriptAllowed(" + target + ") contains?: " + contains(target) + " _pageRender: " + _pageRender);
         }
-        
+
         if (_pageRender)
             return true;
-        
-        if (target != null 
-                && IPage.class.isInstance(target)
-                || (IForm.class.isInstance(target)
+
+        if (target != null
+            && IPage.class.isInstance(target)
+            || (IForm.class.isInstance(target)
                 && ((IForm)target).isFormFieldUpdating()))
             return true;
-        
+
         return contains(target);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -355,16 +344,16 @@
     {
         if (_pageRender)
             return true;
-        
-        if (target != null 
-                && IPage.class.isInstance(target)
-                || (IForm.class.isInstance(target)
+
+        if (target != null
+            && IPage.class.isInstance(target)
+            || (IForm.class.isInstance(target)
                 && ((IForm)target).isFormFieldUpdating()))
             return true;
-        
+
         return contains(target);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -372,7 +361,7 @@
     {
         return _prs.getPreloadedImageReference(target, source);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -404,7 +393,7 @@
     {
         _prs.addBodyScript(script);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -437,6 +426,11 @@
         _prs.addInitializationScript(script);
     }
 
+    public void addScriptAfterInitialization(IComponent target, String script)
+    {
+        _prs.addScriptAfterInitialization(target, script);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -444,7 +438,7 @@
     {
         return _prs.getUniqueString(baseValue);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -452,7 +446,7 @@
     {
         _prs.writeBodyScript(writer, cycle);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -460,90 +454,90 @@
     {
         _prs.writeInitializationScript(writer);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void beginBodyScript(IMarkupWriter normalWriter, IRequestCycle cycle)
     {
         IMarkupWriter writer = getWriter(ResponseBuilder.BODY_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
-        
+
         writer.begin("script");
         writer.printRaw(NEWLINE + "//<![CDATA[" + NEWLINE);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void endBodyScript(IMarkupWriter normalWriter, IRequestCycle cycle)
     {
         IMarkupWriter writer = getWriter(ResponseBuilder.BODY_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
-        
+
         writer.printRaw(NEWLINE + "//]]>" + NEWLINE);
         writer.end();
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void writeBodyScript(IMarkupWriter normalWriter, String script, IRequestCycle cycle)
     {
         IMarkupWriter writer = getWriter(ResponseBuilder.BODY_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
-        
+
         writer.printRaw(script);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void writeExternalScript(IMarkupWriter normalWriter, String url, IRequestCycle cycle)
     {
         IMarkupWriter writer = getWriter(ResponseBuilder.INCLUDE_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
-        
+
         // causes asset includes to be loaded dynamically into document head
         writer.beginEmpty("include");
         writer.attribute("url", url);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void writeImageInitializations(IMarkupWriter normalWriter, String script, String preloadName, IRequestCycle cycle)
     {
         IMarkupWriter writer = getWriter(ResponseBuilder.BODY_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
-        
+
         writer.printRaw(NEWLINE + preloadName + " = [];" + NEWLINE);
         writer.printRaw("if (document.images) {" + NEWLINE);
-        
+
         writer.printRaw(script);
-        
+
         writer.printRaw("}" + NEWLINE);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void writeInitializationScript(IMarkupWriter normalWriter, String script)
     {
         IMarkupWriter writer = getWriter(ResponseBuilder.INITIALIZATION_SCRIPT, ResponseBuilder.SCRIPT_TYPE);
-        
+
         writer.begin("script");
-        
+
         // return is in XML so must escape any potentially non-xml compliant content
         writer.printRaw(NEWLINE + "//<![CDATA[" + NEWLINE);
-        
+
         writer.printRaw(script);
-        
+
         writer.printRaw(NEWLINE + "//]]>" + NEWLINE);
-        
+
         writer.end();
     }
-        
+
     public void addStatus(IMarkupWriter normalWriter, String text)
     {
         addStatusMessage(normalWriter, "info", text);
-    }  
-    
+    }
+
     /**
      * Adds a status message to the current response. This implementation keeps track
      * of all messages and appends them to the XHR response. On the client side, 
@@ -565,24 +559,24 @@
         {
             _statusMessages = new ArrayList();
         }
-        
+
         _statusMessages.add(category);
-        _statusMessages.add(text);        
+        _statusMessages.add(text);
     }
-    
+
     void writeStatusMessages() {
 
         for (int i=0; i < _statusMessages.size(); i+=2)
         {
             IMarkupWriter writer = getWriter((String) _statusMessages.get(i), "status");
 
-            writer.printRaw((String) _statusMessages.get(i+1));                
+            writer.printRaw((String) _statusMessages.get(i+1));
         }
-        
-        _statusMessages = null;            
+
+        _statusMessages = null;
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void render(IMarkupWriter writer, IRender render, IRequestCycle cycle)
@@ -596,99 +590,100 @@
         }
 
         // check for page exception renders and write content to writer so client can display them
-        
+
         if (IPage.class.isInstance(render))
-        {    
+        {
             IPage page = (IPage)render;
             String errorPage = getErrorPage(page.getPageName());
-            
+
             if (errorPage != null)
-            {    
+            {
                 _pageRender = true;
-                
+
                 clearPartialWriters();
                 render.render(getWriter(errorPage, EXCEPTION_TYPE), cycle);
                 return;
             }
-            
+
             // If a page other than the active page originally requested is rendered
             // it means someone activated a new page, so we need to tell the client to handle
             // this appropriately. (usually by replacing the current dom with whatever this renders)
-            
+
             if (_cycle.getParameter(ServiceConstants.PAGE) != null
-                    && !page.getPageName().equals(_cycle.getParameter(ServiceConstants.PAGE)))
-            {    
+                && !page.getPageName().equals(_cycle.getParameter(ServiceConstants.PAGE)))
+            {
                 IMarkupWriter urlwriter = _writer.getNestedWriter();
-                
+
                 urlwriter.begin("response");
                 urlwriter.attribute("type", PAGE_TYPE);
                 urlwriter.attribute("url", _pageService.getLink(true, page.getPageName()).getAbsoluteURL());
-                
+
                 _writers.put(PAGE_TYPE, urlwriter);
                 return;
             }
         }
-        
+
         if (IComponent.class.isInstance(render)
-                && contains((IComponent)render, ((IComponent)render).peekClientId()))
+            && contains((IComponent)render, ((IComponent)render).peekClientId()))
         {
             render.render(getComponentWriter( ((IComponent)render).peekClientId() ), cycle);
             return;
         }
-        
+
         // Nothing else found, throw out response
-        
+
         render.render(NullWriter.getSharedInstance(), cycle);
     }
-    
+
     private String getErrorPage(String pageName)
     {
-        for (int i=0; i < _errorPages.size(); i++) {
+        for (int i=0; i < _errorPages.size(); i++)
+        {
             String page = (String)_errorPages.get(i);
 
             if (pageName.indexOf(page) > -1)
                 return page;
         }
-        
+
         return null;
     }
-    
+
     IMarkupWriter getComponentWriter(String id)
     {
         return getWriter(id, ELEMENT_TYPE);
     }
-    
+
     /**
-     * 
+     *
      * {@inheritDoc}
      */
     public IMarkupWriter getWriter(String id, String type)
     {
         Defense.notNull(id, "id can't be null");
-        
+
         if (!_responseStarted)
             beginResponse();
-        
+
         IMarkupWriter w = (IMarkupWriter)_writers.get(id);
-        if (w != null) 
+        if (w != null)
             return w;
-        
+
         // Make component write to a "nested" writer
         // so that element begin/ends don't conflict
         // with xml element response begin/ends. This is very
         // important.
-        
+
         IMarkupWriter nestedWriter = _writer.getNestedWriter();
         nestedWriter.begin("response");
         nestedWriter.attribute("id", id);
         if (type != null)
             nestedWriter.attribute("type", type);
-        
+
         _writers.put(id, nestedWriter);
-        
+
         return nestedWriter;
     }
-    
+
     /**
      * Called to start an ajax response. Writes xml doctype and starts
      * the <code>ajax-response</code> element that will contain all of
@@ -697,13 +692,14 @@
     void beginResponse()
     {
         _responseStarted = true;
-        
+
         _writer.printRaw("<?xml version=\"1.0\" encoding=\"" + _cycle.getInfrastructure().getOutputEncoding() + "\"?>");
         _writer.printRaw("<!DOCTYPE html "
-                + "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
-                + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [" + NEWLINE
-                + "<!ENTITY nbsp '&#160;'>" + NEWLINE
-                + "]>" + NEWLINE);
+                         + "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
+                         + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [" + NEWLINE
+                         + "<!ENTITY nbsp '&#160;'>" + NEWLINE
+                         + "]>" + NEWLINE);
+        
         _writer.printRaw("<ajax-response>");
     }
 
@@ -727,43 +723,43 @@
         {
             beginResponse();
         }
-        
+
         // write out captured content
-        
-        if (_statusMessages != null)        
+
+        if (_statusMessages != null)
             writeStatusMessages();
-        
+
         Iterator keys = _writers.keySet().iterator();
-        String buffer = null;
-        
-        while (keys.hasNext()) {
-            
+        String buffer;
+
+        while (keys.hasNext())
+        {
             String key = (String)keys.next();
             NestedMarkupWriter nw = (NestedMarkupWriter)_writers.get(key);
-                        
+
             buffer = nw.getBuffer();
-            
-            if (_log.isDebugEnabled()) {
-                
+
+            if (_log.isDebugEnabled())
+            {
                 _log.debug("Ajax markup buffer for key <" + key + " contains: " + buffer);
             }
-            
+
             if (!isScriptWriter(key))
                 _writer.printRaw(ScriptUtils.ensureValidScriptTags(buffer));
             else
                 _writer.printRaw(buffer);
         }
-        
+
         // end response
-        
+
         _writer.printRaw("</ajax-response>");
         _writer.flush();
     }
-    
+
     /**
      * Determines if the specified markup writer key is one of
      * the pre-defined script keys from ResponseBuilder.
-     * 
+     *
      * @param key
      *          The key to check.
      * @return True, if key is one of the ResponseBuilder keys. 
@@ -771,35 +767,35 @@
      */
     boolean isScriptWriter(String key)
     {
-        if (key == null) 
+        if (key == null)
             return false;
-        
+
         if (ResponseBuilder.BODY_SCRIPT.equals(key)
-                || ResponseBuilder.INCLUDE_SCRIPT.equals(key)
-                || ResponseBuilder.INITIALIZATION_SCRIPT.equals(key))
+            || ResponseBuilder.INCLUDE_SCRIPT.equals(key)
+            || ResponseBuilder.INITIALIZATION_SCRIPT.equals(key))
             return true;
-        
+
         return false;
     }
-    
+
     /**
      * Grabs the incoming parameters needed for json responses, most notable the
      * {@link ServiceConstants#UPDATE_PARTS} parameter.
-     * 
+     *
      * @param cycle
      *            The request cycle to parse from
      */
     void parseParameters(IRequestCycle cycle)
     {
         Object[] updateParts = cycle.getParameters(ServiceConstants.UPDATE_PARTS);
-        
+
         if (updateParts == null)
             return;
-        
+
         for(int i = 0; i < updateParts.length; i++)
             _parts.add(updateParts[i].toString());
     }
-    
+
     /**
      * Determines if the specified component is contained in the 
      * responses requested update parts.
@@ -809,32 +805,32 @@
      */
     public boolean contains(IComponent target)
     {
-        if (target == null) 
+        if (target == null)
             return false;
-        
+
         String id = target.getClientId();
-        
+
         return contains(target, id);
     }
-    
+
     boolean contains(IComponent target, String id)
     {
         if (_parts.contains(id))
             return true;
-        
+
         Iterator it = _cycle.renderStackIterator();
-        while (it.hasNext()) {
-            
+        while (it.hasNext())
+        {
             IComponent comp = (IComponent)it.next();
             String compId = comp.getClientId();
-            
+
             if (comp != target && _parts.contains(compId))
                 return true;
         }
-        
+
         return false;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -842,7 +838,7 @@
     {
         if (target == null)
             return false;
-        
+
         return _parts.contains(target.getId());
     }
 }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/JSONResponseBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/JSONResponseBuilder.java?view=diff&rev=565853&r1=565852&r2=565853
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/JSONResponseBuilder.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/JSONResponseBuilder.java Tue Aug 14 11:49:37 2007
@@ -36,168 +36,168 @@
 
 /**
  * Class that implements JSON responses in tapestry.
- * 
+ *
  * @see <a href="http://json.org">json.org</a>
  * @author jkuhnert
  */
 public class JSONResponseBuilder implements ResponseBuilder
-{   
+{
     /** Writer that creates JSON output response. */
     protected IJSONWriter _writer;
     /** Passed in to bypass normal rendering. */
     protected IMarkupWriter _nullWriter = NullWriter.getSharedInstance();
-    
+
     /** Parts that will be updated. */
     protected List _parts = new ArrayList();
-    
+
     protected RequestLocaleManager _localeManager;
     protected MarkupWriterSource _markupWriterSource;
-    
+
     private WebResponse _response;
-    
+
     private ContentType _contentType;
-    
+
     private final AssetFactory _assetFactory;
-    
+
     private final String _namespace;
-    
+
     private PageRenderSupportImpl _prs;
-    
+
     private IRequestCycle _cycle;
-    
+
     /**
      * Creates a new response builder with the required services it needs
      * to render the response when {@link #renderResponse(IRequestCycle)} is called.
-     * 
-     * @param localeManager 
+     *
+     * @param localeManager
      *          Used to set the locale on the response.
      * @param markupWriterSource
      *          Creates IJSONWriter instance to be used.
      * @param webResponse
      *          Web response for output stream.
      */
-    public JSONResponseBuilder(IRequestCycle cycle, RequestLocaleManager localeManager, 
-            MarkupWriterSource markupWriterSource,
-            WebResponse webResponse, WebRequest request, AssetFactory assetFactory, String namespace)
+    public JSONResponseBuilder(IRequestCycle cycle, RequestLocaleManager localeManager,
+                               MarkupWriterSource markupWriterSource,
+                               WebResponse webResponse, WebRequest request, AssetFactory assetFactory, String namespace)
     {
         Defense.notNull(cycle, "cycle");
-        
+
         _cycle = cycle;
         _localeManager = localeManager;
         _markupWriterSource = markupWriterSource;
         _response = webResponse;
-        
+
         // Used by PageRenderSupport
-        
+
         _assetFactory = assetFactory;
         _namespace = namespace;
     }
-    
+
     /**
-     * 
+     *
      * {@inheritDoc}
      */
     public boolean isDynamic()
     {
         return true;
     }
-    
+
     /**
      * {@inheritDoc}
      */
     public void renderResponse(IRequestCycle cycle)
-    throws IOException
+      throws IOException
     {
         _localeManager.persistLocale();
-        
+
         IPage page = cycle.getPage();
-        
+
         _contentType = page.getResponseContentType();
-        
+
         String encoding = _contentType.getParameter(ENCODING_KEY);
-        
+
         if (encoding == null)
         {
             encoding = cycle.getEngine().getOutputEncoding();
-            
+
             _contentType.setParameter(ENCODING_KEY, encoding);
         }
-        
-        if (_writer == null) {
-            
+
+        if (_writer == null)
+        {
             parseParameters(cycle);
 
             PrintWriter printWriter = _response.getPrintWriter(_contentType);
-            
+
             _writer = _markupWriterSource.newJSONWriter(printWriter, _contentType);
         }
-        
+
         // render response
-        
+
         _prs = new PageRenderSupportImpl(_assetFactory, _namespace, cycle.getPage().getLocation(), this);
-        
+
         TapestryUtils.storePageRenderSupport(cycle, _prs);
-        
+
         cycle.renderPage(this);
-        
+
         TapestryUtils.removePageRenderSupport(cycle);
-        
+
         flush();
 
         _writer.close();
     }
-    
+
     public void flush()
-    throws IOException
+      throws IOException
     {
         // Important - causes any cookies stored to properly be written out before the
         // rest of the response starts being written - see TAPESTRY-825
-        
+
         _writer.flush();
     }
-    
+
     /**
      * Grabs the incoming parameters needed for json responses, most notable the
      * {@link ServiceConstants#UPDATE_PARTS} parameter.
-     * 
+     *
      * @param cycle
      *            The request cycle to parse from
      */
     protected void parseParameters(IRequestCycle cycle)
     {
         Object[] updateParts = cycle.getParameters(ServiceConstants.UPDATE_PARTS);
-        
+
         if (updateParts == null)
             return;
-        
+
         for(int i = 0; i < updateParts.length; i++)
             _parts.add(updateParts[i].toString());
     }
-    
+
     /**
      * {@inheritDoc}
      */
     public void render(IMarkupWriter writer, IRender render, IRequestCycle cycle)
     {
         if (IJSONRender.class.isInstance(render)
-                && IComponent.class.isInstance(render))
+            && IComponent.class.isInstance(render))
         {
             IJSONRender json = (IJSONRender) render;
             IComponent component = (IComponent) render;
-            
+
             if (!contains(component, component.peekClientId()))
             {
                 render.render(_nullWriter, cycle);
                 return;
             }
-            
+
             json.renderComponent(_writer, cycle);
         }
-        
+
         render.render(_nullWriter, cycle);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void updateComponent(String id)
@@ -205,7 +205,7 @@
         if (!_parts.contains(id))
             _parts.add(id);
     }
-    
+
     /**
      * Determines if the specified component is contained in the 
      * responses requested update parts.
@@ -215,32 +215,32 @@
      */
     public boolean contains(IComponent target)
     {
-        if (target == null) 
+        if (target == null)
             return false;
-        
+
         String id = target.getClientId();
-        
+
         return contains(target, id);
     }
-    
+
     boolean contains(IComponent target, String id)
     {
         if (_parts.contains(id))
             return true;
-        
+
         Iterator it = _cycle.renderStackIterator();
-        while (it.hasNext()) {
-            
+        while (it.hasNext())
+        {
             IComponent comp = (IComponent)it.next();
             String compId = comp.getClientId();
-            
+
             if (comp != target && _parts.contains(compId))
                 return true;
         }
-        
+
         return false;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -248,10 +248,10 @@
     {
         if (target == null)
             return false;
-        
+
         return _parts.contains(target.getId());
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -259,16 +259,16 @@
     {
         return _nullWriter;
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public IMarkupWriter getWriter(String id, String type)
     {
         return _nullWriter;
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public boolean isBodyScriptAllowed(IComponent target)
@@ -276,7 +276,7 @@
         return false;
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public boolean isExternalScriptAllowed(IComponent target)
@@ -284,14 +284,14 @@
         return false;
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public boolean isInitializationScriptAllowed(IComponent target)
     {
         return false;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -299,7 +299,7 @@
     {
         return false;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -307,7 +307,7 @@
     {
         return _prs.getPreloadedImageReference(target, source);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -339,7 +339,7 @@
     {
         _prs.addBodyScript(script);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -372,6 +372,11 @@
         _prs.addInitializationScript(script);
     }
 
+    public void addScriptAfterInitialization(IComponent target, String script)
+    {
+        _prs.addScriptAfterInitialization(target, script);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -379,7 +384,7 @@
     {
         return _prs.getUniqueString(baseValue);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -387,7 +392,7 @@
     {
         _prs.writeBodyScript(writer, cycle);
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -395,8 +400,8 @@
     {
         _prs.writeInitializationScript(writer);
     }
-    
-    /** 
+
+    /**
      * {@inheritDoc}
      */
     public void beginBodyScript(IMarkupWriter writer, IRequestCycle cycle)
@@ -404,7 +409,7 @@
         // does nothing
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public void endBodyScript(IMarkupWriter writer, IRequestCycle cycle)
@@ -412,7 +417,7 @@
         // does nothing
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public void writeBodyScript(IMarkupWriter writer, String script, IRequestCycle cycle)
@@ -420,7 +425,7 @@
         // does nothing
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public void writeExternalScript(IMarkupWriter normalWriter, String url, IRequestCycle cycle)
@@ -428,7 +433,7 @@
         // does nothing
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public void writeImageInitializations(IMarkupWriter writer, String script, String preloadName, IRequestCycle cycle)
@@ -436,7 +441,7 @@
         // does nothing
     }
 
-    /** 
+    /**
      * {@inheritDoc}
      */
     public void writeInitializationScript(IMarkupWriter writer, String script)