You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2008/04/15 18:50:29 UTC

svn commit: r648325 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/corelib/mixins/ main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/services/ main/resources/o...

Author: hlship
Date: Tue Apr 15 09:50:14 2008
New Revision: 648325

URL: http://svn.apache.org/viewvc?rev=648325&view=rev
Log:
TAPESTRY-2334: Optimize JavaScript generation for FormInjector

Removed:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PartialRenderPageRenderSupport.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PartialRenderPageRenderSupportTest.java
Modified:
    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/mixins/TriggerFragment.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilder.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImpl.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/services/TapestryModule.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/internal/services/ClientBehaviorSupportImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImplTest.java

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=648325&r1=648324&r2=648325&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 Tue Apr 15 09:50:14 2008
@@ -15,6 +15,8 @@
 package org.apache.tapestry;
 
 import org.apache.tapestry.ioc.services.SymbolSource;
+import org.apache.tapestry.json.JSONArray;
+import org.apache.tapestry.json.JSONObject;
 import org.apache.tapestry.services.AssetSource;
 
 /**
@@ -79,4 +81,31 @@
      * @param arguments additional arguments formatted to form the final script
      */
     void addScript(String format, Object... arguments);
+
+    /**
+     * Add an initialization call.
+     *
+     * @param functionName  the name of the function (on the client-side Tapestry.Initializer object) to invoke.
+     * @param parameterList list of parameters for the method invocation.
+     * @see #addScript(String, Object[])
+     */
+    void addInit(String functionName, JSONArray parameterList);
+
+    /**
+     * Alternate version of {@link #addInit(String, org.apache.tapestry.json.JSONArray)} where just a single object is
+     * passed.
+     *
+     * @param functionName the name of the function (on the client-side Tapestry object) to invoke.
+     * @param parameter    the object to pass to the function
+     */
+    void addInit(String functionName, JSONObject parameter);
+
+    /**
+     * Alternate version of {@link #addInit(String, org.apache.tapestry.json.JSONArray)} where just a single string is
+     * passed.
+     *
+     * @param functionName the name of the function (on the client-side Tapestry object) to invoke.
+     * @param parameter    the single string to pass to the function
+     */
+    void addInit(String functionName, String parameter);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/TriggerFragment.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/TriggerFragment.java?rev=648325&r1=648324&r2=648325&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/TriggerFragment.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/mixins/TriggerFragment.java Tue Apr 15 09:50:14 2008
@@ -20,6 +20,7 @@
 import org.apache.tapestry.annotations.Environmental;
 import org.apache.tapestry.annotations.InjectContainer;
 import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.json.JSONArray;
 import org.apache.tapestry.services.Heartbeat;
 
 /**
@@ -51,9 +52,11 @@
         {
             public void run()
             {
-                _pageRenderSupport.addScript("Tapestry.linkTriggerToFormFragment('%s', '%s');",
-                                             _container.getClientId(),
-                                             _fragment.getClientId());
+                JSONArray spec = new JSONArray();
+                spec.put(_container.getClientId());
+                spec.put(_fragment.getClientId());
+
+                _pageRenderSupport.addInit("linkTriggerToFormFragment", spec);
             }
         };
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java?rev=648325&r1=648324&r2=648325&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImpl.java Tue Apr 15 09:50:14 2008
@@ -18,27 +18,16 @@
 import org.apache.tapestry.Link;
 import org.apache.tapestry.PageRenderSupport;
 import org.apache.tapestry.corelib.data.InsertPosition;
+import org.apache.tapestry.ioc.internal.util.Defense;
 import org.apache.tapestry.json.JSONArray;
 import org.apache.tapestry.json.JSONObject;
 
 public class ClientBehaviorSupportImpl implements ClientBehaviorSupport
 {
-    static final String ZONE_INITIALIZER_STRING = "Tapestry.initializeZones(%s, %s);";
-
     private final PageRenderSupport _pageRenderSupport;
 
-    private final JSONArray _zones = new JSONArray();
-
-    private final JSONArray _links = new JSONArray();
-
-    private final JSONArray _subForms = new JSONArray();
-
-    private final JSONArray _injectors = new JSONArray();
-
     private final JSONObject _validations = new JSONObject();
 
-    private boolean _zonesDirty;
-
     public ClientBehaviorSupportImpl(PageRenderSupport pageRenderSupport)
     {
         _pageRenderSupport = pageRenderSupport;
@@ -47,16 +36,29 @@
     public void addZone(String clientId, String showFunctionName, String updateFunctionName)
     {
         JSONObject spec = new JSONObject();
-        spec.put("div", clientId);
 
         addFunction(spec, "show", showFunctionName);
         addFunction(spec, "update", updateFunctionName);
 
-        _zones.put(spec);
+        addElementInit("zone", clientId, spec);
+    }
+
+    private void addElementInit(String functionName, String clientId, JSONObject spec)
+    {
+        Defense.notBlank(clientId, "clientId");
+
+        if (spec.length() == 0)
+        {
+            _pageRenderSupport.addInit(functionName, clientId);
+            return;
+        }
 
-        _zonesDirty = true;
+        spec.put("element", clientId);
+
+        _pageRenderSupport.addInit(functionName, spec);
     }
 
+
     private void addFunction(JSONObject spec, String key, String showFunctionName)
     {
         if (showFunctionName != null) spec.put(key, showFunctionName.toLowerCase());
@@ -68,21 +70,17 @@
         spec.put(linkId);
         spec.put(elementId);
 
-        _links.put(spec);
-
-        _zonesDirty = true;
-
+        _pageRenderSupport.addInit("linkZone", spec);
     }
 
     public void addFormFragment(String clientId, String showFunctionName, String hideFunctionName)
     {
         JSONObject spec = new JSONObject();
-        spec.put("element", clientId);
 
         addFunction(spec, "show", showFunctionName);
         addFunction(spec, "hide", hideFunctionName);
 
-        _subForms.put(spec);
+        addElementInit("formFragment", clientId, spec);
     }
 
     public void addFormInjector(String clientId, Link link, InsertPosition insertPosition, String showFunctionName)
@@ -97,9 +95,12 @@
 
         addFunction(spec, "show", showFunctionName);
 
-        _injectors.put(spec);
+        // Always has at least two properties.
+
+        _pageRenderSupport.addInit("formInjector", spec);
     }
 
+
     public void addValidation(Field field, String validationName, String message, Object constraint)
     {
         String fieldId = field.getClientId();
@@ -123,17 +124,13 @@
         specs.put(thisSpec);
     }
 
-    public void writeInitializationScript()
+    /**
+     * Invoked at the end of rendering to commit (to the {@link org.apache.tapestry.PageRenderSupport}) any accumulated
+     * validations.
+     */
+    public void commit()
     {
         if (_validations.length() > 0)
-            _pageRenderSupport.addScript("Tapestry.registerValidation(%s);", _validations);
-
-        if (_subForms.length() > 0)
-            _pageRenderSupport.addScript("Tapestry.initializeFormFragments(%s);", _subForms);
-
-        if (_injectors.length() > 0)
-            _pageRenderSupport.addScript("Tapestry.initializeFormInjectors(%s);", _injectors);
-
-        if (_zonesDirty) _pageRenderSupport.addScript(ZONE_INITIALIZER_STRING, _zones, _links);
+            _pageRenderSupport.addScript("Tapestry.initValidations(%s);", _validations);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilder.java?rev=648325&r1=648324&r2=648325&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilder.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilder.java Tue Apr 15 09:50:14 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.
@@ -14,8 +14,6 @@
 
 package org.apache.tapestry.internal.services;
 
-import org.apache.tapestry.dom.Document;
-
 /**
  * Responsible for injecting script and style links into the <head> element of the rendered HTML document.
  */
@@ -41,12 +39,4 @@
      * @param script statement to add to the block (a newline will be appended as well)
      */
     void addScript(String script);
-
-    /**
-     * Updates the supplied Document, locating the html/body element and adding script links (to the top) and a script
-     * block (to the end).
-     *
-     * @param document to be updated
-     */
-    void updateDocument(Document document);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImpl.java?rev=648325&r1=648324&r2=648325&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImpl.java Tue Apr 15 09:50:14 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.
@@ -83,6 +83,12 @@
         _scriptBlock.append("\n");
     }
 
+    /**
+     * Updates the supplied Document, locating the html/body element and adding script links (to the top) and a script
+     * block (to the end).
+     *
+     * @param document to be updated
+     */
     public void updateDocument(Document document)
     {
         Element root = document.getRootElement();

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=648325&r1=648324&r2=648325&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 Tue Apr 15 09:50:14 2008
@@ -17,9 +17,12 @@
 import org.apache.tapestry.Asset;
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.PageRenderSupport;
+import org.apache.tapestry.ioc.internal.util.Defense;
 import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
 import org.apache.tapestry.ioc.internal.util.IdAllocator;
 import org.apache.tapestry.ioc.services.SymbolSource;
+import org.apache.tapestry.json.JSONArray;
+import org.apache.tapestry.json.JSONObject;
 import org.apache.tapestry.services.AssetSource;
 
 import static java.lang.String.format;
@@ -28,7 +31,7 @@
 
 public class PageRenderSupportImpl implements PageRenderSupport
 {
-    private final IdAllocator _idAllocator = new IdAllocator();
+    private final IdAllocator _idAllocator;
 
     private final DocumentHeadBuilder _builder;
 
@@ -40,20 +43,40 @@
 
     private boolean _coreAssetsAdded;
 
+    private final JSONObject _init = new JSONObject();
+
     /**
      * @param builder      Used to assemble JavaScript includes and snippets
      * @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 assetSource  Used to convert classpath scripts to {@link org.apache.tapestry.Asset}s
      * @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)
     {
+        this(builder, symbolSource, assetSource, new IdAllocator(), coreScripts);
+    }
+
+    /**
+     * @param builder      Used to assemble JavaScript includes and snippets
+     * @param symbolSource Used to example symbols (in {@linkplain #addClasspathScriptLink(String...) in classpath
+     *                     scripts)
+     * @param assetSource  Used to convert classpath scripts to {@link org.apache.tapestry.Asset}s
+     * @param idAllocator  Used to allocate unique client ids during the render
+     * @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, IdAllocator idAllocator, String... coreScripts)
+
+    {
         _builder = builder;
         _symbolSource = symbolSource;
         _assetSource = assetSource;
+        _idAllocator = idAllocator;
 
         _coreScripts = Arrays.asList(coreScripts);
     }
@@ -106,6 +129,48 @@
         String script = format(format, arguments);
 
         _builder.addScript(script);
+    }
+
+    public void addInit(String functionName, JSONArray parameterList)
+    {
+        addInitFunctionInvocation(functionName, parameterList);
+    }
+
+    public void addInit(String functionName, JSONObject parameter)
+    {
+        addInitFunctionInvocation(functionName, parameter);
+    }
+
+    public void addInit(String functionName, String parameter)
+    {
+        addInitFunctionInvocation(functionName, parameter);
+    }
+
+    private void addInitFunctionInvocation(String functionName, Object parameters)
+    {
+        Defense.notBlank(functionName, "functionName");
+        Defense.notNull(parameters, "parameters");
+
+        JSONArray invocations = _init.has(functionName) ? _init.getJSONArray(functionName) : null;
+
+        if (invocations == null)
+        {
+            invocations = new JSONArray();
+            _init.put(functionName, invocations);
+        }
+
+        invocations.put(parameters);
+    }
+
+    /**
+     * Commit any outstanding changes.
+     */
+    public void commit()
+    {
+        if (_init.length() > 0)
+        {
+            addScript("Tapestry.init(%s);", _init);
+        }
     }
 
     public void addStylesheetLink(Asset stylesheet, String media)

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=648325&r1=648324&r2=648325&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 Tue Apr 15 09:50:14 2008
@@ -40,6 +40,7 @@
 import org.apache.tapestry.ioc.annotations.*;
 import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
+import org.apache.tapestry.ioc.internal.util.IdAllocator;
 import org.apache.tapestry.ioc.services.*;
 import org.apache.tapestry.ioc.util.StrategyRegistry;
 import org.apache.tapestry.ioc.util.TimeInterval;
@@ -1331,7 +1332,7 @@
         {
             public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
             {
-                DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+                DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
                 PageRenderSupportImpl support = new PageRenderSupportImpl(builder, symbolSource, assetSource,
 
@@ -1351,6 +1352,8 @@
 
                 renderer.renderMarkup(writer);
 
+                support.commit();
+
                 builder.updateDocument(writer.getDocument());
 
                 _environment.pop(PageRenderSupport.class);
@@ -1371,7 +1374,7 @@
 
                 _environment.pop(ClientBehaviorSupport.class);
 
-                clientBehaviorSupport.writeInitializationScript();
+                clientBehaviorSupport.commit();
             }
         };
 
@@ -1462,6 +1465,10 @@
                                                 @Path("${tapestry.field-error-marker}")
                                                 final Asset fieldErrorIcon,
 
+                                                final SymbolSource symbolSource,
+
+                                                final AssetSource assetSource,
+
                                                 final ValidationMessagesSource validationMessagesSource)
     {
         PartialMarkupRendererFilter pageRenderSupport = new PartialMarkupRendererFilter()
@@ -1472,16 +1479,41 @@
 
                 String namespace = ":" + uid;
 
-                PartialRenderPageRenderSupport support = new PartialRenderPageRenderSupport(
-                        namespace);
+                final StringBuilder buffer = new StringBuilder(1000);
+
+                IdAllocator idAllocator = new IdAllocator(namespace);
+
+                DocumentHeadBuilder builder = new DocumentHeadBuilder()
+                {
+                    public void addScriptLink(String scriptURL)
+                    {
+                    }
+
+                    public void addStylesheetLink(String styleURL, String media)
+                    {
+                    }
+
+                    public void addScript(String script)
+                    {
+                        buffer.append(script);
+                        buffer.append("\n");
+                    }
+                };
+
+
+                PageRenderSupportImpl support = new PageRenderSupportImpl(builder, symbolSource, assetSource,
+                                                                          idAllocator);
 
                 _environment.push(PageRenderSupport.class, support);
 
                 renderer.renderMarkup(writer, reply);
 
-                support.update(reply);
+                support.commit();
 
                 _environment.pop(PageRenderSupport.class);
+
+                if (buffer.length() > 0)
+                    reply.put("script", buffer.toString());
             }
         };
 
@@ -1499,7 +1531,7 @@
 
                 _environment.pop(ClientBehaviorSupport.class);
 
-                support.writeInitializationScript();
+                support.commit();
             }
         };
 

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=648325&r1=648324&r2=648325&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 Tue Apr 15 09:50:14 2008
@@ -32,7 +32,7 @@
 
     ErrorPopup : Class.create(),
 
-    // An array of ErrorPopup that have been created for fields within the page
+    // An array of Tapestry.ErrorPopup instances that have been created for fields within the page.
 
     errorPopups : [],
 
@@ -84,6 +84,46 @@
         });
     },
 
+    // Generalized initialize function for Tapestry, used to help minimize the amount of JavaScript
+    // for the page by removing redundancies such as repeated Object and method names. The spec
+    // is a hash whose keys are the names of methods of the Tapestry object.
+    // The value is an array of arrays.  The outer arrays represent invocations
+    // of the method.  The inner array are the parameters for each invocation.
+    // As an optimization, the inner value may not be an array but instead
+    // a single value.
+
+    init : function(spec)
+    {
+        $H(spec).each(function(pair)
+        {
+            var functionName = pair.key;
+            var initf = Tapestry.Initializer[functionName];
+
+            if (initf == undefined)
+            {
+                Tapestry.logError("Function Tapestry.Initializer.#{name}() does not exist.", { name:functionName });
+                return;
+            }
+
+            pair.value.each(function(parameterList)
+            {
+                if (! Object.isArray(parameterList))
+                {
+                    parameterList = [parameterList];
+                }
+
+                initf.apply(this, parameterList);
+            });
+        });
+    },
+
+    logError : function (message, substitutions)
+    {
+        var formatted = message.interpolate(substitutions);
+
+        window.alert(formatted);
+    },
+
     getFormEventManager : function(form)
     {
         form = $(form);
@@ -96,7 +136,7 @@
         return manager;
     },
 
-    registerValidation : function(clientValidations)
+    initValidations : function(clientValidations)
     {
         $H(clientValidations).each(function(pair)
         {
@@ -119,9 +159,8 @@
 
                 if (vfunc == undefined)
                 {
-                    var errorMessage = "Function Tapestry.Validator.#{name}() does not exist for field '#{fieldName}'.".interpolate({name:name, fieldName:pair.key});
-
-                    window.alert(errorMessage);
+                    Tapestry.logError("Function Tapestry.Validator.#{name}() does not exist for field '#{fieldName}'.", {name:name, fieldName:pair.key});
+                    return;
                 }
 
                 vfunc.call(this, field, message, constraint);
@@ -142,7 +181,35 @@
         Tapestry.onDomLoadedCallback();
     },
 
-    /** Convert a form or link into a trigger of an Ajax update that
+    // Adds a validator for a field.  A FieldEventManager is added, if necessary.
+    // The validator will be called only for non-blank values, unless acceptBlank is
+    // true (in most cases, acceptBlank is flase). The validator is a function
+    // that accepts the current field value as its first parameter, and a
+    // Tapestry.FormEvent as its second.  It can invoke recordError() on the event
+    // if the input is not valid.
+
+    addValidator : function(field, acceptBlank, validator)
+    {
+        this.getFieldEventManager(field).addValidator(acceptBlank, validator);
+    },
+
+    getFieldEventManager : function(field)
+    {
+        field = $(field);
+
+        var manager = field.fieldEventManager;
+
+        if (manager == undefined) manager = new Tapestry.FieldEventManager(field);
+
+        return manager;
+    }
+};
+
+/** Container of functions that may be invoked by the Tapestry.init() function. */
+Tapestry.Initializer = {
+
+    /**
+     * Convert a form or link into a trigger of an Ajax update that
      * updates the indicated Zone.
      */
     linkZone : function(element, zoneDiv)
@@ -198,44 +265,21 @@
         element.onclick = handler;
     },
 
-    // Allows many Tapestry.Zone instances, and calls to Tapestry.linkZone(), to be
-    // combined efficiently (i.e., to minimize the amount of generated JavaScript
-    // for the page).
-
-    initializeZones : function (zoneSpecs, linkSpecs)
+    zone : function(spec)
     {
-        // Each spec is a hash ready to pass to Tapestry.Zone
-
-        $A(zoneSpecs).each(function (spec)
-        {
-            new Tapestry.Zone(spec);
-        });
-
-        // Each spec is a pair of argument values suitable for the linkZone method
-
-        $A(linkSpecs).each(function (spec)
-        {
-            Tapestry.linkZone.apply(null, spec);
-        });
+        new Tapestry.Zone(spec);
     },
 
-    initializeFormFragments : function(specs)
+    formFragment : function(spec)
     {
-        $A(specs).each(function(spec)
-        {
-            new Tapestry.FormFragment(spec)
-        });
+        new Tapestry.FormFragment(spec)
     },
 
-    initializeFormInjectors : function(specs)
+    formInjector : function(spec)
     {
-        $A(specs).each(function(spec)
-        {
-            new Tapestry.FormInjector(spec);
-        });
+        new Tapestry.FormInjector(spec);
     },
 
-
     // Links a FormFragment to a trigger (a radio or a checkbox), such that changing the trigger will hide
     // or show the FormFragment. Care should be taken to render the page with the
     // checkbox and the FormFragment('s visibility) in agreement.
@@ -260,34 +304,8 @@
                 }
             });
         }
-    },
-
-    // Adds a validator for a field.  A FieldEventManager is added, if necessary.
-    // The validator will be called only for non-blank values, unless acceptBlank is
-    // true (in most cases, acceptBlank is flase). The validator is a function
-    // that accepts the current field value as its first parameter, and a
-    // Tapestry.FormEvent as its second.  It can invoke recordError() on the event
-    // if the input is not valid.
-
-    addValidator : function(field, acceptBlank, validator)
-    {
-        this.getFieldEventManager(field).addValidator(acceptBlank, validator);
-    },
-
-    getFieldEventManager : function(field)
-    {
-        field = $(field);
-
-        var manager = field.fieldEventManager;
-
-        if (manager == undefined) manager = new Tapestry.FieldEventManager(field);
-
-        return manager;
     }
-
-
-}
-
+};
 
 // New methods added to Element.
 
@@ -473,7 +491,6 @@
         this.hasMessage = true;
 
         this.fadeIn();
-
     },
 
     repositionBubble : function()
@@ -601,8 +618,6 @@
 
         return event.result;
     }
-
-
 };
 
 Tapestry.FieldEventManager.prototype = {
@@ -755,28 +770,31 @@
 Tapestry.Zone.prototype = {
     // spec are the parameters for the Zone:
     // trigger: required -- name or instance of link.
-    // div: required -- name or instance of div element to be shown, hidden and updated
+    // element: required -- name or instance of div element to be shown, hidden and updated
     // show: name of Tapestry.ElementEffect function used to reveal the zone if hidden
     // update: name of Tapestry.ElementEffect function used to highlight the zone after it is updated
     initialize: function(spec)
     {
-        this.div = $(spec.div);
+        if (Object.isString(spec))
+            spec = { element: spec }
+
+        this.element = $(spec.element);
         this.showFunc = Tapestry.ElementEffect[spec.show] || Tapestry.ElementEffect.show;
         this.updateFunc = Tapestry.ElementEffect[spec.update] || Tapestry.ElementEffect.highlight;
 
-     // Link the div back to this zone.
+        // Link the div back to this zone.
 
-        this.div.zone = this;
+        this.element.zone = this;
 
-     // Look inside the Zone div for the another div with the CSS class "t-zone-update".
+        // Look inside the Zone element for an element with the CSS class "t-zone-update".
         // If present, then this is the elements whose content will be changed, rather
         // then the entire Zone div.  This allows a Zone div to contain "wrapper" markup
-        // (borders and such).  Typically, such a Zone div will initially be invisible.
-        // The show and update functions apply to the Zone div, not the update div.
+        // (borders and such).  Typically, such a Zone element will initially be invisible.
+        // The show and update functions apply to the Zone element, not the update element.
 
-        var updates = this.div.select("DIV.t-zone-update");
+        var updates = this.element.select(".t-zone-update");
 
-        this.updatediv = updates.length == 0 ? this.div : updates[0];
+        this.updateElement = updates.length == 0 ? this.element : updates[0];
     },
 
     // Updates the content of the div controlled by this Zone, then
@@ -784,11 +802,11 @@
 
     show: function(content)
     {
-        this.updatediv.innerHTML = content;
+        this.updateElement.update(content);
 
-        var func = this.div.visible() ? this.updateFunc : this.showFunc;
+        var func = this.element.visible() ? this.updateFunc : this.showFunc;
 
-        func.call(this, this.div);
+        func.call(this, this.element);
     }
 };
 
@@ -799,6 +817,9 @@
 
     initialize: function(spec)
     {
+        if (Object.isString(spec))
+            spec = { element: spec };
+
         this.element = $(spec.element);
 
         this.element.formFragment = this;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImplTest.java?rev=648325&r1=648324&r2=648325&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ClientBehaviorSupportImplTest.java Tue Apr 15 09:50:14 2008
@@ -15,7 +15,7 @@
 package org.apache.tapestry.internal.services;
 
 import org.apache.tapestry.PageRenderSupport;
-import static org.apache.tapestry.internal.services.ClientBehaviorSupportImpl.ZONE_INITIALIZER_STRING;
+import org.apache.tapestry.json.JSONArray;
 import org.apache.tapestry.json.JSONObject;
 import org.apache.tapestry.test.TapestryTestCase;
 import org.testng.annotations.Test;
@@ -31,7 +31,7 @@
 
         ClientBehaviorSupportImpl setup = new ClientBehaviorSupportImpl(support);
 
-        setup.writeInitializationScript();
+        setup.commit();
 
         verify();
     }
@@ -41,9 +41,8 @@
     {
         PageRenderSupport support = mockPageRenderSupport();
 
-        JSONObject template = new JSONObject("{ zones: [], links: [['client1', 'zone1'], ['client2', 'zone2']] }");
-
-        support.addScript(ZONE_INITIALIZER_STRING, template.getJSONArray("zones"), template.getJSONArray("links"));
+        support.addInit("linkZone", new JSONArray("['client1', 'zone1']"));
+        support.addInit("linkZone", new JSONArray("['client2', 'zone2']"));
 
         replay();
 
@@ -52,7 +51,7 @@
         setup.linkZone("client1", "zone1");
         setup.linkZone("client2", "zone2");
 
-        setup.writeInitializationScript();
+        setup.commit();
 
         verify();
     }
@@ -62,9 +61,8 @@
     {
         PageRenderSupport support = mockPageRenderSupport();
 
-        JSONObject template = new JSONObject("{ zones: [ {div:'client1'}, {div:'client2'} ], links:[] }");
-
-        support.addScript(ZONE_INITIALIZER_STRING, template.getJSONArray("zones"), template.getJSONArray("links"));
+        support.addInit("zone", "client1");
+        support.addInit("zone", "client2");
 
         replay();
 
@@ -73,7 +71,7 @@
         setup.addZone("client1", null, null);
         setup.addZone("client2", null, null);
 
-        setup.writeInitializationScript();
+        setup.commit();
 
         verify();
     }
@@ -83,11 +81,8 @@
     {
         PageRenderSupport support = mockPageRenderSupport();
 
-
-        JSONObject template = new JSONObject(
-                "{ zones: [ {div:'client1', show:'showme'}, {div:'client2', update:'updateme'} ], links:[] }");
-
-        support.addScript(ZONE_INITIALIZER_STRING, template.getJSONArray("zones"), template.getJSONArray("links"));
+        support.addInit("zone", new JSONObject("{'element':'client1', 'show':'showme' }"));
+        support.addInit("zone", new JSONObject("{'element':'client2', 'update':'updateme' }"));
 
         replay();
 
@@ -96,7 +91,7 @@
         setup.addZone("client1", "showme", null);
         setup.addZone("client2", null, "updateme");
 
-        setup.writeInitializationScript();
+        setup.commit();
 
         verify();
     }
@@ -106,10 +101,8 @@
     {
         PageRenderSupport support = mockPageRenderSupport();
 
-        JSONObject template = new JSONObject(
-                "{ zones: [ {div:'client1', show:'showme'}, {div:'client2', update:'updateme'} ], links:[] }");
-
-        support.addScript(ZONE_INITIALIZER_STRING, template.getJSONArray("zones"), template.getJSONArray("links"));
+        support.addInit("zone", new JSONObject("{'element':'client1', 'show':'showme' }"));
+        support.addInit("zone", new JSONObject("{'element':'client2', 'update':'updateme' }"));
 
         replay();
 
@@ -118,7 +111,7 @@
         setup.addZone("client1", "ShowMe", null);
         setup.addZone("client2", null, "UpdateMe");
 
-        setup.writeInitializationScript();
+        setup.commit();
 
         verify();
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImplTest.java?rev=648325&r1=648324&r2=648325&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/DocumentHeadBuilderImplTest.java Tue Apr 15 09:50:14 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.
@@ -33,7 +33,7 @@
 
         document.newRootElement("not-html").text("not an HTML document");
 
-        DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+        DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
         builder.addScript("foo.js");
         builder.addScript("doSomething();");
@@ -50,7 +50,7 @@
 
         document.newRootElement("html").element("body").element("p").text("Ready to be updated with scripts.");
 
-        DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+        DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
         builder.addScriptLink("foo.js");
         builder.addScriptLink("bar/baz.js");
@@ -67,7 +67,7 @@
 
         document.newRootElement("html").element("body").element("p").text("Ready to be updated with styles.");
 
-        DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+        DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
         builder.addStylesheetLink("foo.css", null);
         builder.addStylesheetLink("bar/baz.css", "print");
@@ -84,7 +84,7 @@
 
         document.newRootElement("html").element("body").element("p").text("Ready to be updated with styles.");
 
-        DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+        DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
         builder.addStylesheetLink("foo.css", null);
         builder.addStylesheetLink("bar/baz.css", "print");
@@ -105,7 +105,7 @@
         document.newRootElement("html").element("head").comment("existing head").getParent()
                 .element("body").text("body content");
 
-        DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+        DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
         builder.addStylesheetLink("foo.css", null);
 
@@ -121,7 +121,7 @@
 
         document.newRootElement("html").element("body").element("p").text("Ready to be updated with scripts.");
 
-        DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+        DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
         for (int i = 0; i < 3; i++)
         {
@@ -142,7 +142,7 @@
 
         document.newRootElement("html").element("body").element("p").text("Ready to be updated with scripts.");
 
-        DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+        DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
         builder.addScript("doSomething();");
         builder.addScript("doSomethingElse();");
@@ -162,7 +162,7 @@
 
         document.newRootElement("html").element("notbody").element("p").text("Ready to be updated with scripts.");
 
-        DocumentHeadBuilder builder = new DocumentHeadBuilderImpl();
+        DocumentHeadBuilderImpl builder = new DocumentHeadBuilderImpl();
 
         builder.addScriptLink("foo.js");