You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by bu...@apache.org on 2014/06/23 19:19:40 UTC

svn commit: r913400 - in /websites/production/tapestry/content: cache/main.pageCache client-side-javascript.html javascript-modules.html user-guide.html

Author: buildbot
Date: Mon Jun 23 17:19:39 2014
New Revision: 913400

Log:
Production update by buildbot for tapestry

Modified:
    websites/production/tapestry/content/cache/main.pageCache
    websites/production/tapestry/content/client-side-javascript.html
    websites/production/tapestry/content/javascript-modules.html
    websites/production/tapestry/content/user-guide.html

Modified: websites/production/tapestry/content/cache/main.pageCache
==============================================================================
Binary files - no diff available.

Modified: websites/production/tapestry/content/client-side-javascript.html
==============================================================================
--- websites/production/tapestry/content/client-side-javascript.html (original)
+++ websites/production/tapestry/content/client-side-javascript.html Mon Jun 23 17:19:39 2014
@@ -25,6 +25,14 @@
   </title>
   <link type="text/css" rel="stylesheet" href="/resources/space.css">
 
+    <link href='/resources/highlighter/styles/shCoreCXF.css' rel='stylesheet' type='text/css' />
+  <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet' type='text/css' />
+  <script src='/resources/highlighter/scripts/shCore.js' type='text/javascript'></script>
+  <script src='/resources/highlighter/scripts/shBrushJava.js' type='text/javascript'></script>
+  <script type="text/javascript">
+  SyntaxHighlighter.defaults['toolbar'] = false;
+  SyntaxHighlighter.all();
+  </script>
 
   <link href="/styles/style.css" rel="stylesheet" type="text/css"/>
 
@@ -61,7 +69,32 @@
   </div>
 
 <div id="content">
-<div id="ConfluenceContent"><p>Perhaps nothing in Tapestry has changed over the years so much as the way client-side JavaScript is supported. From the get go, the goal was to make JavaScript a first-class citizen in the Tapestry world, and make it easy to encapsulate JavaScript within components.</p><p>The <a shape="rect" href="legacy-javascript.html">legacy JavaScript</a> page discusses the earlier approaches; the main feature of Tapestry 5.4 is a total rewrite of all things client-side, with the following goals:</p><ul><li>Break the hard linkage of Tapestry to <a shape="rect" class="external-link" href="http://prototypejs.org/" >Prototype</a> and <a shape="rect" class="external-link" href="http://script.aculo.us/" >Scriptaculous</a>, by introducing an abstraction layer</li><li>Remove the clumsy <code>Tapestry</code> and <code>T5</code> "namespaces"</li><li>Reduce the amount of page-specific JavaScript initialization</li><li>Make it easier to override behavior associated with clien
 t elements</li><li>Support CoffeeScript and (potentially) other languages that target JavaScript</li><li>Organize client-side JavaScript using <a shape="rect" href="javascript-modules.html">modules</a></li><li>Make pages load faster</li><li>Integrate <a shape="rect" class="external-link" href="http://getbootstrap.com/" >Bootstrap</a></li><li>Make it easier for rich client libraries such as&#160;<a shape="rect" class="external-link" href="http://backbonejs.org/" >Backbone</a> or&#160;<a shape="rect" class="external-link" href="https://angularjs.org/" >AngularJS</a> to operate within a page</li><li>Properly document Tapestry's client support</li></ul><h3 id="Client-SideJavaScript-TheOverallVision">The Overall Vision</h3><p>The overall vision for the client-side in Tapestry is encapsulation, at several different levels.</p><p>On the server-side, a Tapestry component (or mixin) will exposes configurable parameters. The component will write DOM elements or attributes, as well as some amo
 unt of JavaScript initialization. The encapsulation here allows developers with little or no knowledge of client-side JavaScript to enjoy the benefits (as consumers of components created by developers who are versed in client-side coding and Tapestry components).</p><p>On the client-side, the JavaScript combines with the special markup to produce the behaviors that are desired ... anything from controlling the initial focus field, to performing client-side input field validation, to running complex Ajax workflows.</p><p>Where possible, all of this behavior is driven by <code>data-</code> attributes on the elements, combined with top-level event handlers. On the client side, events are used not only to respond directly to user actions (with "click", "mouseOver", "submit", or other event listeners) but also to allow elements to collaborate in various ways. &#160;For example, input validation is based on triggering a specific custom event on each form control element, and top-level eve
 nt handlers can then manage the validation for any number of fields.</p><h3 id="Client-SideJavaScript-Prototypevs.jQuery">Prototype vs. jQuery</h3><p>For several years, it has been obvious that Tapestry "backed the wrong horse" with respect to Prototype and jQuery. When the first code was being laid down in 2007 or 2008, it wasn't so clear that jQuery with its odd abstractions and unfamiliar approach, would go on to conquer the world. Meanwhile, Prototype was very strongly integrated into Ruby on Rails and had first class documentation and books.</p><p>That being said, jQuery may not be the be-all and end-all. Tapestry 5.4 introduces an abstraction layer, that allows many components to write code that doesn't care whether the foundation framework in Prototype or jQuery. This is especially useful during the arduous process of moving Tapestry 5.3 client-side code forward to 5.4.</p><p>If you like jQuery then there's no problem: write your application using just jQuery and you can igno
 re a lot of the features in the abstraction layer. Your code will likely be just a bit more efficient.</p><p>If you are building a reusable component or library, writing to the abstraction layer may be worth the effort; it is entirely possible that someone may write a replacement for the abstraction layer that targets your favorite foundation framework, such as ExtJS, MooTools, or something not even known of today.</p><h3 id="Client-SideJavaScript-Heavyvs.Light">Heavy vs. Light</h3><p>Earlier Tapestry JavaScript was&#160;<em>heavy</em>. Essentially, each component would write some very specific JavaScript initialization that would include the component's DOM id and many other details. This initialization would reference a function on the <code>T5.inits</code> namespace.</p><p>The function there would typically locate the specific element by its client DOM id, then attach event handlers to the one element. It might also create some form of client-side controller object. There were is
 sues due to this: for complex pages (or perhaps even typical pages), the "blob" of JavaScript initialization at the bottom of the page could be quite large.</p><p>The use of individual event handlers meant that Tapestry applications would use more client-side objects that a bespoke jQuery solution ... because the normal approach in jQuery is to attach a single event handler to the document or body that handles any events that bubble up to the top&#160;<em>and</em> match a CSS selector.</p><p>In Tapestry 5.4, the goal is to make things&#160;<em>light</em>. In most cases, there will not be a specific initialization function; instead a <a shape="rect" href="javascript-modules.html">JavaScript module</a> will be loaded, and it will install one or more top-level event handlers; the elements will have <a shape="rect" class="external-link" href="https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes" ><code>data-</code> attributes</a> that are used by those top level
  handlers to recognize which elements they are responsible for.</p><p>This is more of a full lifecycle approach; there's no danger of&#160;</p><p>&#160;</p></div>
+<div id="ConfluenceContent"><p>Perhaps nothing in Tapestry has changed over the years so much as the way client-side JavaScript is supported. From the get go, the goal was to make JavaScript a first-class citizen in the Tapestry world, and make it easy to encapsulate JavaScript within components.</p><p>The <a shape="rect" href="legacy-javascript.html">legacy JavaScript</a> page discusses the earlier approaches; the main feature of Tapestry 5.4 is a total rewrite of all things client-side, with the following goals:</p><ul><li>Break the hard linkage of Tapestry to <a shape="rect" class="external-link" href="http://prototypejs.org/" >Prototype</a> and <a shape="rect" class="external-link" href="http://script.aculo.us/" >Scriptaculous</a>, by introducing an abstraction layer</li><li>Remove the clumsy <code>Tapestry</code> and <code>T5</code> "namespaces"</li><li>Reduce the amount of page-specific JavaScript initialization</li><li>Make it easier to override behavior associated with clien
 t elements</li><li>Support CoffeeScript and (potentially) other languages that target JavaScript</li><li>Organize client-side JavaScript using <a shape="rect" href="javascript-modules.html">modules</a></li><li>Make pages load faster</li><li>Integrate <a shape="rect" class="external-link" href="http://getbootstrap.com/" >Bootstrap</a></li><li>Make it easier for rich client libraries such as&#160;<a shape="rect" class="external-link" href="http://backbonejs.org/" >Backbone</a> or&#160;<a shape="rect" class="external-link" href="https://angularjs.org/" >AngularJS</a> to operate within a page</li><li>Properly document Tapestry's client support</li></ul><h3 id="Client-SideJavaScript-TheOverallVision">The Overall Vision</h3><p>The overall vision for the client-side in Tapestry is encapsulation, at several different levels.</p><p>On the server-side, a Tapestry component (or mixin) will exposes configurable parameters. The component will write DOM elements or attributes, as well as some amo
 unt of JavaScript initialization. The encapsulation here allows developers with little or no knowledge of client-side JavaScript to enjoy the benefits (as consumers of components created by developers who are versed in client-side coding and Tapestry components).</p><p>On the client-side, the JavaScript combines with the special markup to produce the behaviors that are desired ... anything from controlling the initial focus field, to performing client-side input field validation, to running complex Ajax workflows.</p><p>Where possible, all of this behavior is driven by <code>data-</code> attributes on the elements, combined with top-level event handlers. On the client side, events are used not only to respond directly to user actions (with "click", "mouseOver", "submit", or other event listeners) but also to allow elements to collaborate in various ways. &#160;For example, input validation is based on triggering a specific custom event on each form control element, and top-level eve
 nt handlers can then manage the validation for any number of fields.</p><h3 id="Client-SideJavaScript-Prototypevs.jQuery">Prototype vs. jQuery</h3><p>For several years, it has been obvious that Tapestry "backed the wrong horse" with respect to Prototype and jQuery. When the first code was being laid down in 2007 or 2008, it wasn't so clear that jQuery with its odd abstractions and unfamiliar approach, would go on to conquer the world. Meanwhile, Prototype was very strongly integrated into Ruby on Rails and had first class documentation and books.</p><p>That being said, jQuery may not be the be-all and end-all. Tapestry 5.4 introduces an abstraction layer, that allows many components to write code that doesn't care whether the foundation framework in Prototype or jQuery. This is especially useful during the arduous process of moving Tapestry 5.3 client-side code forward to 5.4.</p><p>If you like jQuery then there's no problem: write your application using just jQuery and you can igno
 re a lot of the features in the abstraction layer. Your code will likely be just a bit more efficient.</p><p>If you are building a reusable component or library, writing to the abstraction layer may be worth the effort; it is entirely possible that someone may write a replacement for the abstraction layer that targets your favorite foundation framework, such as ExtJS, MooTools, or something not even known of today.</p><h3 id="Client-SideJavaScript-Heavyvs.Light">Heavy vs. Light</h3><p>Earlier Tapestry JavaScript was&#160;<em>heavy</em>. Essentially, each component would write some very specific JavaScript initialization that would include the component's DOM id and many other details. This initialization would reference a function on the <code>T5.inits</code> namespace.</p><p>The function there would typically locate the specific element by its client DOM id, then attach event handlers to the one element. It might also create some form of client-side controller object. There were is
 sues due to this: for complex pages (or perhaps even typical pages), the "blob" of JavaScript initialization at the bottom of the page could be quite large.</p><p>The use of individual event handlers meant that Tapestry applications would use more client-side objects that a bespoke jQuery solution ... because the normal approach in jQuery is to attach a single event handler to the document or body that handles any events that bubble up to the top&#160;<em>and</em> match a CSS selector.</p><p>In Tapestry 5.4, the goal is to make things&#160;<em>light</em>. In most cases, there will not be a specific initialization function; instead a <a shape="rect" href="javascript-modules.html">JavaScript module</a> will be loaded, and it will install one or more top-level event handlers; the elements will have <a shape="rect" class="external-link" href="https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes" ><code>data-</code> attributes</a> that are used by those top level
  handlers to recognize which elements they are responsible for.</p><p>This is more of a full lifecycle approach; adding or removing page content (such as with a&#160;<a shape="rect" href="ajax-and-zones.html">Zone</a> component) is both cheaper and less error prone using top-level event handlers than per-element event handlers; there's also less of a chance of memory leaks under Internet Explorer.</p>    <div class="aui-message warning shadowed information-macro">
+                            <span class="aui-icon icon-warning">Icon</span>
+                <div class="message-content">
+                            <p>Internet Explorer is pretty well known for memory leaks; its DOM and JavaScript run in different kinds of memory, which are garbage collected individually. This means that a reference from JavaScript to a DOM element will keep the DOM element live, even if that's the only reference to the DOM element anywhere. Meanwhile, event handler JavaScript functions are kept live from the DOM element, making a cycle that can't be broken. Libraries like Prototype and jQuery have to expend some effort to break this link by unregistering event handlers from DOM elements when removing them from the DOM.</p>
+                    </div>
+    </div>
+<p>A specific example of this approach is how client-side validation now works; in the past, there was a complex system of classes and event listeners that were specific to each individual field. Field controllers had to register with Form controllers. Validators had to register with Field controllers.</p><p>Under 5.4, there are a number of&#160;<code>data-</code> attributes that can be attached to any DOM element. A form will search for elements with a non-blank value for&#160;<code>data-validation</code>; each such element has a series of custom events triggered on it. The top-level handlers for those events will receive notifications for elements throughout the document.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>t5/core/validation.coffee (partial)</b></div><div class="codeContent panelContent pdl">
+<script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[define [&quot;underscore&quot;, &quot;./dom&quot;, &quot;./events&quot;, &quot;./utils&quot;, &quot;./messages&quot;, &quot;./fields&quot;],
+  (_, dom, events, utils, messages) -&gt;
+
+
+  ...
+ 
+    dom.onDocument events.field.optional, &quot;[data-optionality=required]&quot;, (event, memo) -&gt;
+      if utils.isBlank memo.value
+        memo.error =  (@attr &quot;data-required-message&quot;) or &quot;REQUIRED&quot;
+
+
+  ...
+ 
+    dom.onDocument events.field.validate, &quot;[data-validate-min-length]&quot;, (event, memo) -&gt;
+      min = parseInt @attr &quot;data-validate-min-length&quot;
+      if memo.translated.length &lt; min
+        memo.error = (@attr &quot;data-min-length-message&quot;) or &quot;TOO SHORT&quot;
+        return false]]></script>
+</div></div><p>The&#160;<code>t5/core/events</code> module defines constants for different custom event name, it's also a handy place to hang&#160;<a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/coffeescript/events.html">hang documentation</a> about those events.</p><p>The&#160;<code>t5/core/dom</code> namespace is the abstraction layer. &#160;<code>onDocument</code> is a handy way to attach a top-level event handler.</p><p>Fields that are required will have the attribute&#160;<code>data-optionality=required</code>; the event handler is passed a&#160;<em>memo</em> object that includes a&#160;<code>value</code> property, the value from the field. This makes it easier to generate an error if the value is blank. &#160;Because the exact error message may be customized or localized, it is provided in the element as well, as the&#160;<code>data-required-message</code> attribute. Setting&#160;<code>memo.error</code> to a validation error string will cause the fie
 ld to be decorated with the error message and will indicate that the form itself is in error and not ready for submission.</p><p>A different event is triggered after the optionality check; The&#160;<code>memo.translated</code> property is the value translated before validation (for a numeric field, it would be translated from a string to a number, for example). Again, the&#160;<code>error</code> property is set, and the&#160;<code>return false</code> ensures that the event will stop bubbling to containing elements or event handlers.</p></div>
 </div>
 
 <div class="clearer"></div>

Modified: websites/production/tapestry/content/javascript-modules.html
==============================================================================
--- websites/production/tapestry/content/javascript-modules.html (original)
+++ websites/production/tapestry/content/javascript-modules.html Mon Jun 23 17:19:39 2014
@@ -70,7 +70,7 @@
   </div>
 
 <div id="content">
-<div id="ConfluenceContent"><p>As web applications have evolved, the use of JavaScript in the client has expanded almost exponentially. This has caused all kinds of growing pains, since the original design of the web browser, and the initial design of JavaScript, was never intended for this level of complexity. Unlike Java, JavaScript has no native concept of a "package" or "namespace" and has the undesirable tendency to make everything a global.</p><p>In the earliest days, client-side JavaScript was constructed as libraries that define simple functions and variables:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<div id="ConfluenceContent"><p>As web applications have evolved, the use of JavaScript in the client has expanded almost exponentially. This has caused all kinds of growing pains, since the original design of the web browser, and the initial design of JavaScript, was never intended for this level of complexity. Unlike Java, JavaScript has no native concept of a "package" or "namespace" and has the undesirable tendency to make everything a global.</p><p>In the earliest days, client-side JavaScript was constructed as libraries that would define simple functions and variables:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: js; gutter: false" type="syntaxhighlighter"><![CDATA[ 
 function onclickHelp(event) {
   if (helpModal === undefined) {
@@ -124,7 +124,7 @@ $(&quot;#helpButton&quot;).click(onClick
                             <p>With AMD, the JavaScript libraries may be loaded in parallel by the browser (that's the <em>asynchronous</em> part of AMD); RequireJS manages the dependency graph and invokes each function just once, as soon as its dependencies are ready, as libraries are loaded. In some cases, a module may be loaded just for its side effects; such modules will be listed last in the dependency array, and will not have a corresponding parameter in the dependent module's constructor function. In <code>confirm-click</code>, the <code>bootstrap/modal</code> module is loaded for side-effects.</p>
                     </div>
     </div>
-<p><code>confirm-click</code> defines a local function, <code>runDialog</code>. It performs some side-effects, attaching event handlers to the body and the document. It's export is a JavaScript object containing a function that allows other modules to raise the modal dialog.</p><p>If a module truly exports only a single function and is unlikely to change, then it is acceptable to just return the function itself, not an object containing the function. However, returning an object makes it easier to expand the responsibilities of <code>confirm-click</code> in the future; perhaps to add a <code>dismissDialog</code> function.</p><h3 id="JavaScriptModules-LocationofModules">Location of Modules</h3><p>Modules are stored as a special kind of Tapestry <a shape="rect" href="assets.html">asset</a>. On the server, modules are stored on the class path under <code>META-INF/modules</code>. In a typical environment, that means the sources will be in <code>src/main/resources/META-INF/modules</code>
 .</p><p>Typically, your application will place it's modules directly in this folder. If you are writing a reusable library, you will put modules for that library into a subfolder to prevent naming conflicts. Tapestry's own modules are prefixed with <code>t5/core</code>.</p><p>If you are using the optional&#160;<code>tapestry-web-resources</code> module (that's a server-side module, not an AMD module), then you can write your modules as CoffeeScript files; Tapestry will take care of compiling them to JavaScript as necessary.</p><p>The service <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/ModuleManager.html">ModuleManager</a> is the central piece of server-side support for modules. It supports&#160;<em>overriding</em> of existing modules by contributing overriding <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/JavaScriptModuleConfig
 uration.html">module definitions</a>. This can be useful to <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Monkey_patch" >monkey patch</a> an existing module supplied with Tapestry, or as part of a third-party library.</p><h3 id="JavaScriptModules-LoadingModulesfromTapestryCode">Loading Modules from Tapestry Code</h3><p>Often, you will have a Tapestry page or component that defines client-side behavior; such a component will need to load a module.</p><p>The simplest approach is to use the <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/annotations/Import.html">Import</a> annotation:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<p><code>confirm-click</code> defines a local function, <code>runDialog</code>. It performs some side-effects, attaching event handlers to the body and the document. The module's export is a JavaScript object containing a function that allows other modules to raise the modal dialog.</p><p>If a module truly exports only a single function and is unlikely to change, then it is acceptable to just return the function itself, not an object containing the function. However, returning an object makes it easier to expand the responsibilities of <code>confirm-click</code> in the future; perhaps to add a <code>dismissDialog</code> function.</p><h3 id="JavaScriptModules-LocationofModules">Location of Modules</h3><p>Modules are stored as a special kind of Tapestry <a shape="rect" href="assets.html">asset</a>. On the server, modules are stored on the class path under <code>META-INF/modules</code>. In a typical environment, that means the sources will be in <code>src/main/resources/META-INF/module
 s</code>.</p><p>Typically, your application will place it's modules directly in this folder. If you are writing a reusable library, you will put modules for that library into a subfolder to prevent naming conflicts. Tapestry's own modules are prefixed with <code>t5/core</code>.</p><p>If you are using the optional&#160;<code>tapestry-web-resources</code> module (that's a server-side module, not an AMD module), then you can write your modules as CoffeeScript files; Tapestry will take care of compiling them to JavaScript as necessary.</p><p>The service <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/ModuleManager.html">ModuleManager</a> is the central piece of server-side support for modules. It supports&#160;<em>overriding</em> of existing modules by contributing overriding <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/JavaScriptModu
 leConfiguration.html">module definitions</a>. This can be useful to <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Monkey_patch" >monkey patch</a> an existing module supplied with Tapestry, or as part of a third-party library.</p><h3 id="JavaScriptModules-LoadingModulesfromTapestryCode">Loading Modules from Tapestry Code</h3><p>Often, you will have a Tapestry page or component that defines client-side behavior; such a component will need to load a module.</p><p>The simplest approach is to use the <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/annotations/Import.html">Import</a> annotation:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[@Import(module = &quot;t5/core/confirm-click&quot;)
 public class Confirm
 {
@@ -141,14 +141,20 @@ public class Confirm
   ...
  
   javaScriptSupport.require(&quot;my-module&quot;).invoke(&quot;setup&quot;).with(clientId, actionUrl);]]></script>
-</div></div><p>In the first example, <code>my-module</code> exports a single function of two parameters. In the second example, <code>my-module</code> exports an object and the <code>setup</code> key is the function that is invoked.</p><h3 id="JavaScriptModules-DevelopmentMode">Development Mode</h3><p>In development mode, Tapestry will write details into the client-side console.</p><p><img class="confluence-embedded-image" src="https://cwiki.apache.org/confluence/download/attachments/41813130/Tapestry_Integration_Test_Application_and_JavaScriptSupport__Tapestry_API_Documentation_.png?version=1&amp;modificationDate=1401727827255&amp;api=v2" data-image-src="/confluence/download/attachments/41813130/Tapestry_Integration_Test_Application_and_JavaScriptSupport__Tapestry_API_Documentation_.png?version=1&amp;modificationDate=1401727827255&amp;api=v2"></p><p>This lists modules&#160;<em>explicitly</em> loaded (for initialization), but does not include modules loaded only as dependencies. You
  can see more details about what was actually loaded using&#160;<em>view source</em>; RequireJS adds <code>&lt;script&gt;</code> tags to the document to load libraries and modules.</p><h3 id="JavaScriptModules-Librariesvs.Modules">Libraries vs. Modules</h3><p>Tapestry still supports JavaScript libraries. &#160;When the page is loading, all libraries are loaded before any modules.</p><p>Libraries are loaded sequentially, so if you can avoid using libraries, so much the better in terms of page load time.</p><p>Libraries work in both normal page rendering, and Ajax partial page updates. Even in partial page updates, the libraries will be loaded sequentially before modules are loaded or exported functions invoked.</p><h3 id="JavaScriptModules-AggregatingModules">Aggregating Modules</h3><p>An important part of performance for production applications is JavaScript aggregation.</p><p>In development mode, you want your modules and other assets to load individually. Unlike assets, modules ca
 n't be fingerprinted, so on each page load, the client browser must ask the server for the module again (typically getting a 304 Not Modified response).</p><p>This is acceptable in development mode, but quite undesirable in production.</p><p>With JavaScript aggregation, the module can be included in the single virtual JavaScript library that represents a <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/JavaScriptStack.html">JavaScript stack</a>. This significantly cuts down on both the number of requests from the client to the server, and the overall number of bytes transferred.</p><p>Adding a module to the stack is not the same as <code>require</code>-ing it. In fact, you must still use <code>JavaScriptSupport.require()</code> regardless.</p><p>What adding a module to a stack accomplishes is that the module's code is downloaded in the first, initial JavaScript download; the download of the stack's virtual li
 brary. When (and if) the module is required as a dependency, the code will already be present in the browser and ready to execute.</p><p>Tapestry&#160;<strong>does not</strong> attempt to do dependency analysis; that is left as a manual exercise. Typically, if you aggregate a module, your should look at its dependencies, and aggregate those as well. Failure to do so will cause unwanted requests back to the Tapestry server for the dependency modules, even though the aggregated module's code is present.</p><p>Because Tapestry is open, it is possible to contribute modules even into the&#160;<strong>core</strong> JavaScript stack. &#160;This is done using your application's module:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+</div></div><p>In the first example, <code>my-module</code> exports a single function of two parameters. In the second example, <code>my-module</code> exports an object and the <code>setup</code> key is the function that is invoked.</p><h3 id="JavaScriptModules-DevelopmentMode">Development Mode</h3><p>In development mode, Tapestry will write details into the client-side console.</p><p><img class="confluence-embedded-image" src="https://cwiki.apache.org/confluence/download/attachments/41813130/Tapestry_Integration_Test_Application_and_JavaScriptSupport__Tapestry_API_Documentation_.png?version=1&amp;modificationDate=1401727827000&amp;api=v2" data-image-src="/confluence/download/attachments/41813130/Tapestry_Integration_Test_Application_and_JavaScriptSupport__Tapestry_API_Documentation_.png?version=1&amp;modificationDate=1401727827000&amp;api=v2"></p><p>This lists modules&#160;<em>explicitly</em> loaded (for initialization), but does not include modules loaded only as dependencies. You
  can see more details about what was actually loaded using&#160;<em>view source</em>; RequireJS adds <code>&lt;script&gt;</code> tags to the document to load libraries and modules.</p><h3 id="JavaScriptModules-Librariesvs.Modules">Libraries vs. Modules</h3><p>Tapestry still supports JavaScript libraries. &#160;When the page is loading, all libraries are loaded before any modules.</p><p>Libraries are loaded sequentially, so if you can avoid using libraries, so much the better in terms of page load time.</p><p>Libraries work in both normal page rendering, and Ajax partial page updates. Even in partial page updates, the libraries will be loaded sequentially before modules are loaded or exported functions invoked.</p><h3 id="JavaScriptModules-AggregatingModules">Aggregating Modules</h3><p>An important part of performance for production applications is JavaScript aggregation.</p><p>In development mode, you want your modules and other assets to load individually. For both CSS and JavaScri
 pt, smaller files that align with corresponding server-side files makes it much easier to debug problems.</p><p>Unlike assets, modules can't be fingerprinted, so on each page load, the client browser must ask the server for the module's contents frequently (typically getting a 304 Not Modified response).</p>    <div class="aui-message warning shadowed information-macro">
+                            <span class="aui-icon icon-warning">Icon</span>
+                <div class="message-content">
+                            <p>By default, Tapestry sets a max age of 60 (seconds) on modules, so you won't see module requests on every page load. This is configurable and you may want a much higher value in production. If you are rapidly iterating on the source of a module, you may need to force the browser to reload after clearing local cache. Chrome has an option to disable the client-side cache when its developer tools are open.</p>
+                    </div>
+    </div>
+<p><span style="line-height: 1.4285715;">This is acceptable in development mode, but quite undesirable in production.</span></p><p>With JavaScript aggregation, the module can be included in the single virtual JavaScript library that represents a <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/JavaScriptStack.html">JavaScript stack</a>. This significantly cuts down on both the number of requests from the client to the server, and the overall number of bytes transferred.</p><p>Adding a module to the stack is not the same as <code>require</code>-ing it. In fact, you must still use <code>JavaScriptSupport.require()</code> regardless.</p><p>What adding a module to a stack accomplishes is that the module's code is downloaded in the first, initial JavaScript download; the download of the stack's virtual library. When (and if) the module is required as a dependency, the code will already be present in the browser an
 d ready to execute.</p><p>Tapestry&#160;<strong>does not</strong> attempt to do dependency analysis; that is left as a manual exercise. Typically, if you aggregate a module, your should look at its dependencies, and aggregate those as well. Failure to do so will cause unwanted requests back to the Tapestry server for the dependency modules, even though the aggregated module's code is present.</p><p>Because Tapestry is open, it is possible to contribute modules even into the&#160;<strong>core</strong> JavaScript stack. &#160;This is done using your application's module:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[    @Contribute(JavaScriptStack.class)
     @Core
     public static void addAppModules(OrderedConfiguration&lt;StackExtension&gt; configuration) {
         configuration.add(&quot;tree-viewer&quot;, StackExtension.module(&quot;tree-viewer&quot;));
 		configuration.add(&quot;app-utils&quot;, StackExtension.module(&quot;app-utils&quot;));
     }]]></script>
-</div></div><p>To break this down:</p><ul><li>@Contribute indicates we are contributing to a JavaScriptStack service</li><li>Since there are (or at least, could be) multiple services that implement JavaScriptStack, we provide the&#160;@Core annotation to indicate which one we are contributing to</li><li>It is possible to contribute libraries, CSS files, other stacks, and modules; here we are contributing modules</li><li>Each contribution has a unique id and a <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/StackExtension.html">StackExtension</a> value</li></ul><p>The core stack includes several libraries and modules; the exact configuration is subject to a number of factors (such as whether Prototype or jQuery is being used as the underlying framework). That being said, this is the&#160;<em>current</em> list of modules aggregated into the core stack:</p><ul><li>jquery</li><li>underscore</li><li>t5/core/<ul><
 li>alert</li><li>ajax</li><li>bootstrap</li><li>console</li><li>dom</li><li>events</li><li>exception-frame</li><li>fields</li><li>pageinit</li><li>messages</li><li>util</li><li>validation</li></ul></li></ul><p>The optimum configuration is always a balancing act between including too little and including too much. Generally speaking, including too much is less costly than including too little. It is up to you to analyze the requests coming into your application and determine what modules should be aggregated.</p></div>
+</div></div><p>To break this down:</p><ul><li>@Contribute indicates we are contributing to a JavaScriptStack service</li><li>Since there are (or at least, could be) multiple services that implement JavaScriptStack, we provide the&#160;@Core annotation to indicate which one we are contributing to (this is a marker annotation, which exists for this exact purpose)</li><li>It is possible to contribute libraries, CSS files, other stacks, and modules; here we are contributing modules</li><li>Each contribution has a unique id and a <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/StackExtension.html">StackExtension</a> value</li></ul><p>The core stack includes several libraries and modules; the exact configuration is subject to a number of factors (such as whether Prototype or jQuery is being used as the underlying framework). That being said, this is the&#160;<em>current</em> list of modules aggregated into the cor
 e stack:</p><ul><li>jquery</li><li>underscore</li><li>t5/core/<ul><li>alert</li><li>ajax</li><li>bootstrap</li><li>console</li><li>dom</li><li>events</li><li>exception-frame</li><li>fields</li><li>pageinit</li><li>messages</li><li>util</li><li>validation</li></ul></li></ul><p>The optimum configuration is always a balancing act between including too little and including too much. Generally speaking, including too much is less costly than including too little. It is up to you to analyze the requests coming into your application and determine what modules should be aggregated.</p></div>
 </div>
 
 <div class="clearer"></div>

Modified: websites/production/tapestry/content/user-guide.html
==============================================================================
--- websites/production/tapestry/content/user-guide.html (original)
+++ websites/production/tapestry/content/user-guide.html Mon Jun 23 17:19:39 2014
@@ -61,7 +61,7 @@
   </div>
 
 <div id="content">
-<div id="ConfluenceContent"><p>This <strong>User Guide</strong> serves as the encyclopedia for Tapestry concepts, capabilities and practices.</p><div class="sectionColumnWrapper"><div class="sectionMacro"><div class="sectionMacroRow"><div class="columnMacro" style="width:30%;min-width:30%;max-width:30%;"><h2 id="UserGuide-TapestryBasics">Tapestry Basics</h2><p>Going beyond <a shape="rect" href="getting-started.html">Getting Started</a> and the <a shape="rect" href="tapestry-tutorial.html">Tutorial</a>, these topics will dive into basic Tapestry capabilities that you'll want to understand first.</p><ul><li><a shape="rect" href="project-layout.html">Project Layout</a></li><li><a shape="rect" href="configuration.html">Configuration</a></li><li><a shape="rect" href="class-reloading.html">Class Reloading</a></li><li><a shape="rect" href="component-reference.html">Component Reference</a></li><li><a shape="rect" href="annotations.html">Annotations</a></li></ul><h3 id="UserGuide-PagesandCom
 ponents">Pages and Components</h3><ul><li><a shape="rect" href="component-classes.html">Component Classes</a></li><li><a shape="rect" href="component-templates.html">Component Templates</a></li><li><a shape="rect" href="property-expressions.html">Property Expressions</a></li><li><a shape="rect" href="component-parameters.html">Component Parameters</a></li><li><a shape="rect" href="type-coercion.html">Type Coercion</a></li><li><a shape="rect" href="layout-component.html">Layout Component</a></li><li><a shape="rect" href="component-mixins.html">Component Mixins</a></li><li><a shape="rect" href="page-navigation.html">Page Navigation</a></li><li><a shape="rect" href="localization.html">Localization</a></li></ul><h3 id="UserGuide-Request/ResponseProcessing">Request/Response Processing</h3><ul><li><a shape="rect" href="page-life-cycle.html">Page Life Cycle</a></li><li><a shape="rect" href="request-processing.html">Request Processing</a></li><li><a shape="rect" href="component-rendering.ht
 ml">Component Rendering</a></li><li><a shape="rect" href="component-events.html">Component Events</a></li><li><a shape="rect" href="url-rewriting.html">URL Rewriting</a></li><li><a shape="rect" href="dom.html">Document Object Model</a> (DOM)</li><li><a shape="rect" href="response-compression.html">Response Compression</a></li><li><a shape="rect" href="https.html">Securing your application with HTTPS</a></li><li><a shape="rect" href="content-type-and-markup.html">Content Type and Markup</a></li></ul><h3 id="UserGuide-Datapersistence">Data persistence</h3><ul><li><a shape="rect" href="persistent-page-data.html">Persistent Page Data</a></li><li><a shape="rect" href="session-storage.html">Session Storage</a></li></ul><h3 id="UserGuide-Interactingwithcomponents">Interacting with components</h3><ul><li><a shape="rect" href="injection.html">Injection</a></li><li><a shape="rect" href="environmental-services.html">Environmental Services</a></li></ul><h3 id="UserGuide-JavaScript,AJAX,CSS&amp;
 BinaryData">JavaScript, AJAX, CSS &amp; Binary Data</h3><ul><li><span style="font-size: 14.0px;line-height: 1.4285715;"><a shape="rect" href="javascript.html">Client-Side JavaScript</a></span></li><li><span style="font-size: 14.0px;line-height: 1.4285715;"><a shape="rect" href="javascript-modules.html">JavaScript Modules</a></span></li><li><span style="font-size: 14.0px;line-height: 1.4285715;">CoffeeScript</span></li><li><a shape="rect" href="ajax-and-zones.html">Ajax and Zones</a></li><li><a shape="rect" href="css.html">Cascading Style Sheets</a> (CSS)</li><li><a shape="rect" href="assets.html">Assets</a></li><li><a shape="rect" href="legacy-javascript.html">Legacy JavaScript</a> (prior to Tapestry 5.4)</li></ul><h3 id="UserGuide-Workingwithforms">Working with forms</h3><ul><li><a shape="rect" href="forms-and-validation.html">Forms and Validation</a></li><li><a shape="rect" href="beaneditform-guide.html">BeanEditForm Guide</a></li><li><a shape="rect" href="uploading-files.html">Up
 loading files</a></li></ul><h3 id="UserGuide-Debugging,Testing&amp;Tooling">Debugging, Testing &amp; Tooling</h3><ul><li><a shape="rect" href="logging.html">Logging &amp; Debugging</a></li><li><a shape="rect" href="unit-testing-pages-or-components.html">Unit testing pages or components</a></li><li><a shape="rect" href="integration-testing.html">Integration testing with Selenium</a></li><li><a shape="rect" href="service-status.html">Service Status</a></li></ul></div><div class="columnMacro" style="width:30%;min-width:30%;max-width:30%;"><h2 id="UserGuide-TapestryIOC">Tapestry IOC</h2><p>As with Tapestry 5 in general, the goal of Tapestry Inversion of Control (IoC) is greater simplicity and power--without XML. You'll want to explore Tapestry IoC to make full use of the extensibility of the framework.</p><h3 id="UserGuide-Architecture">Architecture</h3><ul><li><a shape="rect" href="ioc.html">Introduction</a></li><li><a shape="rect" href="tapestry-ioc-overview.html">Overview</a></li><li
 ><a shape="rect" href="tapestry-ioc-modules.html">Modules</a></li></ul><h3 id="UserGuide-Features">Features</h3><ul><li><a shape="rect" href="defining-tapestry-ioc-services.html">Services</a></li><li><a shape="rect" href="service-advisors.html">Advisors</a></li><li><a shape="rect" href="tapestry-ioc-decorators.html">Decorators</a></li></ul><h3 id="UserGuide-Configuration">Configuration</h3><ul><li><a shape="rect" href="tapestry-ioc-configuration.html">Configuration</a></li><li><a shape="rect" href="case-insensitivity.html">Case Insensitivity</a></li><li><a shape="rect" href="autoloading-modules.html">Module Autoloading</a></li><li><a shape="rect" href="service-implementation-reloading.html">Service Implementation Reloading</a></li><li><a shape="rect" href="ordering-by-constraints.html">Ordering by Constraints</a></li><li><a shape="rect" href="symbols.html">Symbols</a></li></ul><h3 id="UserGuide-ServiceBuilders">Service Builders</h3><p>Tapestry provides a set of ready-to-use service 
 builders for common design patterns:</p><ul><li><a shape="rect" href="chainbuilder-service.html">Chain of Command</a> (the ChainBuilder service)</li><li><a shape="rect" href="pipelinebuilder-service.html">Pipeline</a> (the PipelineBuilder service)</li><li><a shape="rect" href="shadowbuilder-service.html">Shadow Properties</a> (the PropertyShadowBuilder service)</li><li><a shape="rect" href="strategybuilder-service.html">Strategy</a> (the StrategyBuilder service)</li></ul><h3 id="UserGuide-Usage">Usage</h3><ul><li><a shape="rect" href="injection-in-detail.html">Injection in detail</a></li><li><a shape="rect" href="object-providers.html">Object providers</a></li><li><a shape="rect" href="service-serialization.html">Service Serialization</a></li></ul><h3 id="UserGuide-Built-inservices">Built-in services</h3><ul><li><a shape="rect" href="typecoercer-service.html">Type Coercer</a></li></ul><h3 id="UserGuide-Registrystartup&amp;shutdown">Registry startup &amp; shutdown</h3><ul><li><a shap
 e="rect" href="starting-the-ioc-registry.html">Starting and stopping the registry</a></li><li><a shape="rect" href="registry-startup.html">Listening for registry startup</a></li></ul><h3 id="UserGuide-Tooling">Tooling</h3><ul><li><a shape="rect" href="parallel-execution.html">Parallel Execution</a></li><li><a shape="rect" href="logging-in-tapestry.html">Logging</a></li></ul></div><div class="columnMacro" style="width:30%;min-width:30%;max-width:30%;"><h2 id="UserGuide-Built-inmodules">Built-in modules</h2>Tapestry comes with a collection of add-on modules to let you extend the framework beyond its core.
+<div id="ConfluenceContent"><p>This <strong>User Guide</strong> serves as the encyclopedia for Tapestry concepts, capabilities and practices.</p><div class="sectionColumnWrapper"><div class="sectionMacro"><div class="sectionMacroRow"><div class="columnMacro" style="width:30%;min-width:30%;max-width:30%;"><h2 id="UserGuide-TapestryBasics">Tapestry Basics</h2><p>Going beyond <a shape="rect" href="getting-started.html">Getting Started</a> and the <a shape="rect" href="tapestry-tutorial.html">Tutorial</a>, these topics will dive into basic Tapestry capabilities that you'll want to understand first.</p><ul><li><a shape="rect" href="project-layout.html">Project Layout</a></li><li><a shape="rect" href="configuration.html">Configuration</a></li><li><a shape="rect" href="class-reloading.html">Class Reloading</a></li><li><a shape="rect" href="component-reference.html">Component Reference</a></li><li><a shape="rect" href="annotations.html">Annotations</a></li></ul><h3 id="UserGuide-PagesandCom
 ponents">Pages and Components</h3><ul><li><a shape="rect" href="component-classes.html">Component Classes</a></li><li><a shape="rect" href="component-templates.html">Component Templates</a></li><li><a shape="rect" href="property-expressions.html">Property Expressions</a></li><li><a shape="rect" href="component-parameters.html">Component Parameters</a></li><li><a shape="rect" href="type-coercion.html">Type Coercion</a></li><li><a shape="rect" href="layout-component.html">Layout Component</a></li><li><a shape="rect" href="component-mixins.html">Component Mixins</a></li><li><a shape="rect" href="page-navigation.html">Page Navigation</a></li><li><a shape="rect" href="localization.html">Localization</a></li></ul><h3 id="UserGuide-Request/ResponseProcessing">Request/Response Processing</h3><ul><li><a shape="rect" href="page-life-cycle.html">Page Life Cycle</a></li><li><a shape="rect" href="request-processing.html">Request Processing</a></li><li><a shape="rect" href="component-rendering.ht
 ml">Component Rendering</a></li><li><a shape="rect" href="component-events.html">Component Events</a></li><li><a shape="rect" href="url-rewriting.html">URL Rewriting</a></li><li><a shape="rect" href="dom.html">Document Object Model</a> (DOM)</li><li><a shape="rect" href="response-compression.html">Response Compression</a></li><li><a shape="rect" href="https.html">Securing your application with HTTPS</a></li><li><a shape="rect" href="content-type-and-markup.html">Content Type and Markup</a></li></ul><h3 id="UserGuide-Datapersistence">Data persistence</h3><ul><li><a shape="rect" href="persistent-page-data.html">Persistent Page Data</a></li><li><a shape="rect" href="session-storage.html">Session Storage</a></li></ul><h3 id="UserGuide-Interactingwithcomponents">Interacting with components</h3><ul><li><a shape="rect" href="injection.html">Injection</a></li><li><a shape="rect" href="environmental-services.html">Environmental Services</a></li></ul><h3 id="UserGuide-JavaScript,AJAX,CSS&amp;
 BinaryData">JavaScript, AJAX, CSS &amp; Binary Data</h3><ul><li><span style="font-size: 14.0px;line-height: 1.4285715;"><a shape="rect" href="client-side-javascript.html">Client-Side JavaScript</a></span></li><li><span style="font-size: 14.0px;line-height: 1.4285715;"><a shape="rect" href="javascript-modules.html">JavaScript Modules</a></span></li><li><span style="font-size: 14.0px;line-height: 1.4285715;"><a shape="rect" class="unresolved" href="#">CoffeeScript</a></span></li><li><a shape="rect" href="ajax-and-zones.html">Ajax and Zones</a></li><li><a shape="rect" href="css.html">Cascading Style Sheets</a> (CSS)</li><li><a shape="rect" href="assets.html">Assets</a></li><li><a shape="rect" href="legacy-javascript.html">Legacy JavaScript</a> (prior to Tapestry 5.4)</li></ul><h3 id="UserGuide-Workingwithforms">Working with forms</h3><ul><li><a shape="rect" href="forms-and-validation.html">Forms and Validation</a></li><li><a shape="rect" href="beaneditform-guide.html">BeanEditForm Guid
 e</a></li><li><a shape="rect" href="uploading-files.html">Uploading files</a></li></ul><h3 id="UserGuide-Debugging,Testing&amp;Tooling">Debugging, Testing &amp; Tooling</h3><ul><li><a shape="rect" href="logging.html">Logging &amp; Debugging</a></li><li><a shape="rect" href="unit-testing-pages-or-components.html">Unit testing pages or components</a></li><li><a shape="rect" href="integration-testing.html">Integration testing with Selenium</a></li><li><a shape="rect" href="service-status.html">Service Status</a></li></ul></div><div class="columnMacro" style="width:30%;min-width:30%;max-width:30%;"><h2 id="UserGuide-TapestryIOC">Tapestry IOC</h2><p>As with Tapestry 5 in general, the goal of Tapestry Inversion of Control (IoC) is greater simplicity and power--without XML. You'll want to explore Tapestry IoC to make full use of the extensibility of the framework.</p><h3 id="UserGuide-Architecture">Architecture</h3><ul><li><a shape="rect" href="ioc.html">Introduction</a></li><li><a shape="
 rect" href="tapestry-ioc-overview.html">Overview</a></li><li><a shape="rect" href="tapestry-ioc-modules.html">Modules</a></li></ul><h3 id="UserGuide-Features">Features</h3><ul><li><a shape="rect" href="defining-tapestry-ioc-services.html">Services</a></li><li><a shape="rect" href="service-advisors.html">Advisors</a></li><li><a shape="rect" href="tapestry-ioc-decorators.html">Decorators</a></li></ul><h3 id="UserGuide-Configuration">Configuration</h3><ul><li><a shape="rect" href="tapestry-ioc-configuration.html">Configuration</a></li><li><a shape="rect" href="case-insensitivity.html">Case Insensitivity</a></li><li><a shape="rect" href="autoloading-modules.html">Module Autoloading</a></li><li><a shape="rect" href="service-implementation-reloading.html">Service Implementation Reloading</a></li><li><a shape="rect" href="ordering-by-constraints.html">Ordering by Constraints</a></li><li><a shape="rect" href="symbols.html">Symbols</a></li></ul><h3 id="UserGuide-ServiceBuilders">Service Buil
 ders</h3><p>Tapestry provides a set of ready-to-use service builders for common design patterns:</p><ul><li><a shape="rect" href="chainbuilder-service.html">Chain of Command</a> (the ChainBuilder service)</li><li><a shape="rect" href="pipelinebuilder-service.html">Pipeline</a> (the PipelineBuilder service)</li><li><a shape="rect" href="shadowbuilder-service.html">Shadow Properties</a> (the PropertyShadowBuilder service)</li><li><a shape="rect" href="strategybuilder-service.html">Strategy</a> (the StrategyBuilder service)</li></ul><h3 id="UserGuide-Usage">Usage</h3><ul><li><a shape="rect" href="injection-in-detail.html">Injection in detail</a></li><li><a shape="rect" href="object-providers.html">Object providers</a></li><li><a shape="rect" href="service-serialization.html">Service Serialization</a></li></ul><h3 id="UserGuide-Built-inservices">Built-in services</h3><ul><li><a shape="rect" href="typecoercer-service.html">Type Coercer</a></li></ul><h3 id="UserGuide-Registrystartup&amp;s
 hutdown">Registry startup &amp; shutdown</h3><ul><li><a shape="rect" href="starting-the-ioc-registry.html">Starting and stopping the registry</a></li><li><a shape="rect" href="registry-startup.html">Listening for registry startup</a></li></ul><h3 id="UserGuide-Tooling">Tooling</h3><ul><li><a shape="rect" href="parallel-execution.html">Parallel Execution</a></li><li><a shape="rect" href="logging-in-tapestry.html">Logging</a></li></ul></div><div class="columnMacro" style="width:30%;min-width:30%;max-width:30%;"><h2 id="UserGuide-Built-inmodules">Built-in modules</h2>Tapestry comes with a collection of add-on modules to let you extend the framework beyond its core.
 
 <h3 id="UserGuide-HibernateIntegration">Hibernate Integration</h3>