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 2015/07/19 23:21:30 UTC

svn commit: r958985 [17/29] - in /websites/production/tapestry/content: ./ 2009/09/13/ 2009/10/27/ 2009/11/25/ 2010/07/18/ 2010/07/24/ 2010/10/11/ 2010/10/31/ 2010/11/18/ 2010/12/16/ 2010/12/17/ 2011/01/18/ 2011/03/23/ 2011/03/29/ 2011/03/30/ 2011/03/3...

Modified: websites/production/tapestry/content/javascript-modules.html
==============================================================================
--- websites/production/tapestry/content/javascript-modules.html (original)
+++ websites/production/tapestry/content/javascript-modules.html Sun Jul 19 21:21:27 2015
@@ -27,6 +27,17 @@
   </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 src='/resources/highlighter/scripts/shBrushXml.js' type='text/javascript'></script>
+  <script src='/resources/highlighter/scripts/shBrushJScript.js' type='text/javascript'></script>
+  <script src='/resources/highlighter/scripts/shBrushPlain.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"/>
 
@@ -59,63 +70,63 @@
 <div id="content">
 <div id="ConfluenceContent"><div class="navmenu" style="float:right; background:#eee; margin:3px; padding:3px">
 <div class="error"><span class="error">Error formatting macro: contentbylabel: com.atlassian.confluence.api.service.exceptions.BadRequestException: Could not parse cql : null</span> </div></div><p><strong>JavaScript Modules</strong> are a mechanism for bringing modern concepts of variable scope and dependency management to JavaScript. <em>Starting with version 5.4</em>, Tapestry uses <a shape="rect" class="external-link" href="http://requirejs.org/" >RequireJS</a> <a shape="rect" class="external-link" href="https://github.com/amdjs/amdjs-api/blob/master/AMD.md" >&#160;</a>modules internally, and provides support for using RequireJS modules in your own Tapestry application.</p><h2 id="JavaScriptModules-TheNeedforModules">The Need for Modules</h2><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 ne
 ver 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="brush: js; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[function onclickHelp(event) {
+<pre class="brush: js; gutter: false; theme: Default" style="font-size:12px;">function onclickHelp(event) {
   if (helpModal === undefined) {
     helpModal = ...
   }
-  document.getElementById(&quot;modalContainer&quot;) ...
+  document.getElementById("modalContainer") ...
 }
-$(&quot;#helpButton&quot;).click(onClickHelp);]]></script>
+$("#helpButton").click(onClickHelp);</pre>
 </div></div><p>What's not apparent here is that function <code>onclickHelp()</code> is actually attached to the global window object. Further, the variable <code>helpModal</code> is also not local, it too gets defined on the window object. If you start to mix and match JavaScript from multiple sources, perhaps various kinds of third-party UI widgets, you start to run the risk of name collisions.</p><p>One approach to solving these kinds of problems is a&#160;<em>hygienic&#160;function wrapper</em>. The concept here is to define a function and immediately execute it. The functions and variables defined inside the function are private to that function.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
-<script class="brush: js; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[(function() {
+<pre class="brush: js; gutter: false; theme: Default" style="font-size:12px;">(function() {
   var helpModal = null;
 
   function onClickHelp(event) { ... }
 
-  $(&quot;#helpButton&quot;).click(onClickHelp);
-})();]]></script>
+  $("#helpButton").click(onClickHelp);
+})();</pre>
 </div></div><p>This is an improvement in so far as it assists with name collisions. The variables and functions can only be referenced by name from inside the wrapper.</p><p>However, if you are building a library of code to reuse across your application (or building a library to share between applications) then something is still missing: a way to expose just the function you want from inside you wrapper to the outside world.</p><p>The old-school route is to choose a hopefully unique prefix, building a cumbersome name (perhaps <code>myapp_onClickHelp</code>), and attach that to the global window object. But that just makes your code that much uglier, and leaves you open to problems if not all members of your development team understand the rules and prefixes.</p><p>Enter the <a shape="rect" class="external-link" href="https://github.com/amdjs/amdjs-api/blob/master/AMD.md" >Asynchronous Module Definition</a>. The AMD is pragmatic way to avoid globals, and adds a number of bells and w
 histles that can themselves be quite important.</p><div class="confluence-information-macro confluence-information-macro-information"><span class="aui-icon aui-icon-small aui-iconfont-info confluence-information-macro-icon"></span><div class="confluence-information-macro-body"><p>Tapestry uses the <a shape="rect" class="external-link" href="http://requirejs.org/" >RequireJS</a> library as the client-side implementation of AMD. It supplements this on the server-side with Tapestry services for even more flexibility.</p></div></div><p>Under AMD, JavaScript is broken up into&#160;<em>modules</em>.</p><ul><li>Modules have a unique name, such as <code>t5/core/dom</code> or <code>app/tree-viewer</code>.</li><li>A module has a constructor function that&#160;<em>exports</em> a value.</li><li>A module defines&#160;<em>dependencies</em> on any number of other modules.</li><li>The export of each dependency is provided as a parameter to the constructor function.</li></ul><p>Here's an example fro
 m Tapestry itself:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Module t5/core/confirm-click</b></div><div class="codeContent panelContent pdl">
-<script class="brush: js; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[(function() {
-  define([&quot;jquery&quot;, &quot;./events&quot;, &quot;./dom&quot;, &quot;bootstrap/modal&quot;], function($, events, dom) {
+<pre class="brush: js; gutter: false; theme: Default" style="font-size:12px;">(function() {
+  define(["jquery", "./events", "./dom", "bootstrap/modal"], function($, events, dom) {
     var runDialog;
     runDialog = function(options) {
       ...
     };
-    $(&quot;body&quot;).on(&quot;click&quot;, &quot;[data-confirm-message]:not(.disabled)&quot;, function() {
+    $("body").on("click", "[data-confirm-message]:not(.disabled)", function() {
       ...
     });
-    dom.onDocument(&quot;click&quot;, &quot;a[data-confirm-message]:not(.disabled)&quot;, function() {
+    dom.onDocument("click", "a[data-confirm-message]:not(.disabled)", function() {
       ...
     });
     return {
       runDialog: runDialog
     };
   });
-}).call(this);]]></script>
+}).call(this);</pre>
 </div></div><div class="confluence-information-macro confluence-information-macro-information"><span class="aui-icon aui-icon-small aui-iconfont-info confluence-information-macro-icon"></span><div class="confluence-information-macro-body"><p>The above code is actually the compiled output of a CoffeeScript source file. The <code><span>confirm-click </span></code>module is used to raise a modal confirmation dialog when certain buttons are clicked; it is loaded by the <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/mixins/Confirm.html">Confirm</a> mixin.</p></div></div><p>This module depends on several other modules:&#160;<code>jquery</code>, <code>t5/core/events</code>, <code>t5/core/dom</code>, and <code>bootstrap/modal</code>. These other modules will have been loaded, and their constructor functions executed, before the <code>confirm-click</code> constructor function is executed. The export of each module is provided a
 s a parameter in the order in which the dependencies are defined.</p><div class="confluence-information-macro confluence-information-macro-note"><span class="aui-icon aui-icon-small aui-iconfont-warning confluence-information-macro-icon"></span><div class="confluence-information-macro-body"><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. 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><h2 id="JavaScriptModules-LocationofModules">Location of Modules</h2><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 writi
 ng 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><a shape="rect" href="coffeescript.html">tapestry-web-resources</a></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/JavaScriptModuleConfiguration.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><h2 id="JavaScriptModules-LoadingModulesfromTapestryCode">Loading Modules from Tapestry Code</h2><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="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[@Import(module = &quot;t5/core/confirm-click&quot;)
+<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">@Import(module = "t5/core/confirm-click")
 public class Confirm
 {
   ...
-}]]></script>
+}</pre>
 </div></div><p>The <code>module</code> attribute may either a single module name, or a list of module names.</p><p>In many cases, you not only want to require the module, but invoke a function exported by the module. In that case you must use the <a shape="rect" class="external-link" href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html">JavaScriptSupport</a> environmental.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
-<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[  @Environmental
+<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">  @Environmental
   JavaScriptSupport javaScriptSupport;
 
   ...
 
-  javaScriptSupport.require(&quot;my-module&quot;).with(clientId, actionUrl);
+  javaScriptSupport.require("my-module").with(clientId, actionUrl);
 
   ...
 
-  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><h2 id="JavaScriptModules-DevelopmentMode">Development Mode</h2><p>In development mode, Tapestry will write details into the client-side console.</p><p><span class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image" src="javascript-modules.data/Tapestry_Integration_Test_Application_and_JavaScriptSupport__Tapestry_API_Documentation_.png" 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" data-unresolved-comment-count="0" data-linked-resource-id="42041399" data-linked-resource-version="1" data-linked-resource-type="attachment" data-linked-resource-default-alias="Tapestry_Integr
 ation_Test_Application_and_JavaScriptSupport__Tapestry_API_Documentation_.png" data-base-url="https://cwiki.apache.org/confluence" data-linked-resource-content-type="image/png" data-linked-resource-container-id="41813130" data-linked-resource-container-version="12"></span></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><h2 id="JavaScriptModules-LibrariesversusModules">Libraries versus Modules</h2><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><h2 id="JavaScriptModules-AggregatingModules">Aggregating Modules</h2><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 JavaScript, 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><p><span>This is acceptable in development mode, but quite undesirable in production.</span></p><div class="confluence-information-macro confluence-information-macro-note"><span class="aui-icon aui-icon-small aui-iconfont-warning confluence-information-macro-icon"></s
 pan><div class="confluence-information-macro-body"><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>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 mus
 t 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 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="cod
 eContent panelContent pdl">
-<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[    @Contribute(JavaScriptStack.class)
+  javaScriptSupport.require("my-module").invoke("setup").with(clientId, actionUrl);</pre>
+</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><h2 id="JavaScriptModules-DevelopmentMode">Development Mode</h2><p>In development mode, Tapestry will write details into the client-side console.</p><p><span class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image" src="javascript-modules.data/Tapestry_Integration_Test_Application_and_JavaScriptSupport__Tapestry_API_Documentation_.png"></span></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><h2 id="JavaScriptModules-LibrariesversusModules">Libraries versus M
 odules</h2><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><h2 id="JavaScriptModules-AggregatingModules">Aggregating Modules</h2><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 JavaScript, 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><p><span>This is acceptable in development mode, but quite undesirable in production.</span></p><div class="confluence-information-macro confluence-information-macro-note"><span class="aui-icon aui-icon-small aui-iconfont-warning confluence-information-macro-icon"></span><div class="confluence-information-macro-body"><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>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 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">
+<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">    @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>
+        configuration.add("tree-viewer", StackExtension.module("tree-viewer"));
+		configuration.add("app-utils", StackExtension.module("app-utils"));
+    }</pre>
 </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>
 

Modified: websites/production/tapestry/content/javascript-rewrite-in-54.html
==============================================================================
--- websites/production/tapestry/content/javascript-rewrite-in-54.html (original)
+++ websites/production/tapestry/content/javascript-rewrite-in-54.html Sun Jul 19 21:21:27 2015
@@ -57,7 +57,18 @@
   </div>
 
 <div id="content">
-<div id="ConfluenceContent"><div class="toc-macro client-side-toc-macro" data-headerelements="H1,H2,H3,H4,H5,H6,H7"></div><h1 id="JavaScriptRewritein5.4-TapestryandJavaScript">Tapestry and JavaScript</h1><p>Tapestry 5 has had a interesting mix of characteristics.</p><p>On the one hand, it has had a large number of features that work, and work well, right out of the box, with no special configuration or setup. This includes client-side validation, dynamic content updates, simple animations, progressive enhancement, and other standard Ajax and DHTML use cases.</p><p>In addition, Tapestry has evolved, from Tapestry 5.0 through 5.3, into a quite capable <em>provisioning</em> framework:</p><ul><li>JavaScript libraries may be combined into <em>stacks</em> that are combined (in production) into a single virtual file</li><li>JavaScript libraries and CSS files may be minified</li><li>Libraries, stacks, and other resources are exposed to the browser with a versioned URL and far-future expires
  header, to support aggressive client-caching</li><li>Resources, including JavaScript and CSS, can be distributed inside JARs (as part of reusable component libraries)</li><li>Compressible resources will be automatically GZip compressed if the client supports it</li></ul><p>However, JavaScript support in Tapestry is still unsatisfactory. Too often, Tapestry falls into an <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Uncanny_valley" >uncanny valley</a> where the framework (server-side and client-side) does so much automatically that it becomes accepted that it does everything ... developers later discover, to their dismay, that the last 10% of custom behavior they desire is very hard to implement, because of all the common problems that plague any complex system: insufficient APIs, unexpected leaky abstractions, or just plain bugs.</p><p>Common examples of the challenges imposed by Tapestry include implementing a Confirm mixin, customizing behavior when a Z
 one component is dynamically updated, or any number of issues related to Forms, form elements, and Ajax updates.</p><p>This document is a roadmap for how Tapestry 5.4 will revisit the relationship between server-side Java and client-side JavaScript. Ultimately, we hope to convert this relationship from an obstacle to using Tapestry into an essential reason to select Tapestry in the first place.</p><h1 id="JavaScriptRewritein5.4-TapestryJavaScriptLimitations(through5.3)">Tapestry JavaScript Limitations (through 5.3)</h1><h2 id="JavaScriptRewritein5.4-DependenceonPrototype/Scriptaculous">Dependence on Prototype/Scriptaculous</h2><p>Tapestry made an early choice to embrace Prototype and Scriptaculous at a time when this made sense, circa 2006-2007.</p><p>The goal was to have Tapestry provide a client-side API, the <code>Tapestry</code> namespace, that in turn would delegate complex behaviors (including DOM element selection, event management, and XmlHttpRequest processing) to a <em>fou
 ndational framework</em>. The goal was to isolate all the direct dependencies on Prototype in such a way that it would be possible, in the future, to swap out for a different foundational framework, such as jQuery or ExtJS. Unfortunately, expediency has proven to make this goal even less reachable!</p><h2 id="JavaScriptRewritein5.4-LackofDocumentation">Lack of Documentation</h2><p>There has not, to date, been an adequate documentation of the <code>T5</code> and <code>Tapestry</code> namespaces, beyond the code itself.</p><h2 id="JavaScriptRewritein5.4-LackofModuleStructure">Lack of Module Structure</h2><p>Beyond the basic use of namespaces, Tapestry has not embraced modern JavaScript usage; specifically, it makes limited use of <em>hygenic functions</em> to form modules. Hygenic functions are JavaScript functions that exist as a way to encapsulate private properties and functions. Tapestry 5.3 makes more use of this pattern than previous releases.</p><p>What modularity is present in
  the JavaScript is organized around the <code>T5.initializers</code> (<code>Tapestry.Initializers</code>) namespace, and the mechanics of full-page and partial-page renders (described more fully below).</p><h2 id="JavaScriptRewritein5.4-ComplexInitialization">Complex Initialization</h2><p>Many users are perplexed by how Tapestry performs initialization. In typical web page construction, the developer would create a <code>&lt;script&gt;</code> block at the bottom of the page, and do initializations there. In Tapestry, it can be much more complex:</p><ul><li>A JavaScript library, containing one or more <em>initialization functions</em>, is created</li><li>The initialization functions must be <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Monkey_patching" >monkey patched</a> into the T5.initializers (or older Tapestry.Initializers) namespace.</li><li>The <a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5
 /services/javascript/JavaScriptSupport.html">JavaScriptSupport</a> environmental must be used to invoke the function, by name, passing it a JSONObject to configure itself (the "specification")</li><li>The affected element must have a unique id attribute, used to coordinate the initialization in the client web browser. (Tapestry assists with unique id allocation, but it would be much better if unique ids were not necessary.)</li></ul><p>This often feels like overkill, but it is necessary for a number of desirable characteristics:</p><ul><li>Initialization code occurs in a single Tapestry-generated <code>&lt;script&gt;</code> block at the end of the page (just before the <code>&lt;/body&gt;</code> tag)</li><li>There is limited support for structuring the order of initialization</li><li>The mechanism works transparently in both full-page render requests (traditional) and partial-page render requests (Ajax)</li></ul><p>Despite this, the Tapestry approach can feel very "heavy". In a besp
 oke page, initialization that may affect many elements of the page often takes the form of a single event handler, attached to the <code>&lt;body&gt;</code> element, that catches events that bubble up from much lower in the DOM. The single handler function identifies the applicable elements using CSS selectors, including those that are based on HTML5 data- attributes. Additional data- attributes will define additional behavior ... for example, a URL for a triggered request. This is "light" because:</p><ul><li>There's a single event handler function (rather than a unique handler function instance per element)</li><li>The event handler may be anonymous (there's no name, or possibility of collision)</li><li>Elements are identified by DOM structure and CSS rather than their unique id (the element will often not have an id attribute)</li><li>Additional necessary configuration is directly attached to the element, rather than split</li><li>As the page is dynamically updated, there is no ex
 tra "bookkeeping" for added or removed elements; new elements inserted into the DOM dynamically are recognized as easily as those that were present on the initial render</li></ul><p>By contrast, Tapestry is "heavy":</p><ul><li>The initialization function must have a unique name</li><li>The element must have a unique id, to it can be located by the initialization function</li><li>The event handlers are attached directly to the element</li><li>Duplicated elements will have duplicated event handlers</li><li>Additional behavior is specified as a JSON object passed to the initialization function</li><li>Injecting new elements into the DOM requires invoking initialization functions to wire up the necessary event handlers</li><li>In (older versions of) Internet Explorer, removing elements may leave memory leaks as JavaScript objects retain references to DOM objects and vice-versa</li></ul><h1 id="JavaScriptRewritein5.4-JavaScriptImprovementsfor5.4">JavaScript Improvements for 5.4</h1><p>Th
 e goals for Tapestry 5.4 are:</p><ul><li>Break the dependency on Prototype and allow Tapestry to be used with any client-side "foundation" framework, seamlessly: minimally, this should include jQuery</li><li>Bring Tapestry's JavaScript approach more inline with modern practices (the "light" approach described above)</li><li>Let the JavaScript be modular, and loaded dynamically and asynchonously, only as needed</li><li>Optimize for fast page loads</li><li>Backwards compatibility to the Tapestry 5.3 approach until at least 5.5 or 5.6</li><li>Simplify Tapestry's client-side behavior, but make it easier to hook into, extend, and override</li></ul><h2 id="JavaScriptRewritein5.4-RequireJS">RequireJS</h2><p>Rather than reinvent the wheel, Tapestry should incorporate a proper JavaScript module loader; <a shape="rect" class="external-link" href="http://requirejs.org/" >RequireJS</a> is an excellent candidate, especially considering the new features provided in its 2.0.1 release.</p><p>Requir
 eJS supports the <a shape="rect" class="external-link" href="https://github.com/amdjs/amdjs-api/wiki/AMD" >AMD (Asynchronous Module Format)</a>, with some additional support for the <a shape="rect" class="external-link" href="http://www.commonjs.org/" >CommonJS</a> module format (the format used by Node.js). The latter is simpler, but is designed for a server-side environment; AMD is specifically designed to handle asynchronous loading of JavaScript into a web browser.</p><p>RequireJS is geared towards bespoke applications; for Tapestry it is expected that some of the pathing and other configuration normally done in the client using the RequireJS API will instead by handled more dynamically on the server, using typically Tapestry configuration and extension mechanisms. For example, RequireJS allows mappings of module names to URLs, which is useful when working with multiple third-party JavaScript libraries that may be organized differently form each other. Tapestry can incorporate s
 uch logic on the server side instead, making the interface from the browser to the server uniform, even when the details of where each module is stored is quite variable.</p><h2 id="JavaScriptRewritein5.4-SlowPageLoadandInitialization">Slow Page Load and Initialization</h2><p>Tapestry 5.1 and up has support for dealing with slow page loads (especially, slow loads of extenal JavaScript). This is necessary, because in slow page load situations, the user may submit a form or click a link <em>before</em> page initialization has added an event handler for that submit or click; it was common in those cases for the a traditional request to be sent to the server for a link or form that was expected by the developer to only be accessed via an Ajax request. Without a server-side check (via the <code>Request.isXHR()</code> method), the server-side event handler would return a response that can not be handled in a traditional request, and the user would see the Tapestry exception report page.</
 p><p>Tapestry 5.3 and earlier would wait for the page loaded event (by observing <a shape="rect" class="external-link" href="http://api.prototypejs.org/dom/document/observe/" >Prototype's "dom:loaded" event</a>) before executing any JavaScript initialization functions. Likewise, in a partial page render (Ajax) update, it would ensure that all JavaScript libraries had been loaded before executing any initialization functions.</p><p>It is not clear how this same functionality will be supported in Tapestry 5.4 as the asynchronous module loading makes it difficult to know when all modules have been loaded and all initialization functions have been invoked.</p><p>Tapestry 5.4 uses JavaScript to add a "page loading mask", which is removed once all JavaScript has initialized. Using CSS animation tricks, the mask becomes visible after a fraction of a second, and includes a spinning icon. The page loading mask prevents any interaction by the user on the incompletely initialized page.</p><h2 
 id="JavaScriptRewritein5.4-MappingModulestoAssets">Mapping Modules to Assets</h2><p>Under RequireJS, modules are identified by string that represents a kind of virtual path on the server. The path does not start with a scheme, or a slash, or end with a ".js" suffix: in all those cases, RequireJS will load a JavaScript file but not treat it as a dependency.</p><p>On the server side, Tapestry will map the path to a classpath asset.</p><p>There must be provisions for the following options:</p><ul><li>A module may be overridden (for instance, to work around a bug), in which case a specific asset may be used for the module, rather than the default</li><li>A module may need to be converted from one language to another: specifically, a module may be written in CoffeeScript, and need to be compiled down to JavaScript</li><li>A module's content may be aggregated with other related modules (much like a Tapestry 5.3 stack), especially in production. (A request for any module should provide the
  aggregated set of modules; RequireJS will not need to send additional requests for the other modules.)</li><li>Module content (aggregated or not) should be minimized</li></ul><p>In addition, it may be reasonable to have Tapestry automatically (or via some configuration) <a shape="rect" class="external-link" href="http://requirejs.org/docs/commonjs.html" >wrap CommonJS modules as AMD modules</a>. (Traditionally, Tapestry has configured this kind of behavior via service contributions, but there is ample evidence that this could be done using external configuration, perhaps using a JSON file in the module package, to control aggregation, wrapping, and other aspects the process. This would be more agile, as it would not require restarts when the configuration changes.)</p><p>Modules will be stored on the classpath, in a <code>modulejs</code> package below each library's root package. Modules within that package are referenced by their name relative to the package. (A rarely used featur
 e of Tapestry is that a component library name may be mapped to multiple packages; resolving a module name may require a search among the packages. There is the expectation that the developer will ensure that there are no duplications that would lead to ambiguities.)</p><p>Under this system, module <code>core/pubsub</code> would be the file <code>pubsub.js</code> in the package <code>org.apache.tapestry5.corelib.modulejs</code>, since Tapestry's component library 'core' is mapped to package <code>org.apache.tapestry5.corelib</code>.</p><p>Certain key modules, such as <a shape="rect" class="external-link" href="http://documentcloud.github.com/underscore/" >Underscore</a> may be mapped at the root level, as they are used so often.</p><h2 id="JavaScriptRewritein5.4-ExtensionstoJavaScriptSupport">Extensions to JavaScriptSupport</h2><p>A number of new methods will be added to JavaScriptSupport, to support the following behaviors:</p><ul><li>require one or more modules</li><li>require a m
 odule (that exports a single function) and invoke the function, passing zero or more values. (Values passed to module functions may be limited to String and <a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/json/JSONObject.html">JSONObject</a>.)</li><li>require a module and a function name and invoke named function exported by the module, passing zero or more values</li></ul><p>The intent here is to support shifting of client-side behavior from the 5.3 style, an approach that involved monkey-patching functions onto <code>T5.initializers</code>, and move the same logic into modules, preferably with simpler parameters. It is also expected that there will be greater use of <code>data-</code> prefixed HTML5 attributes in place of separate configuration, as outlined above.</p><h2 id="JavaScriptRewritein5.4-AvoidingJavaScriptClasses"><span style="line-height: 1.5;">Avoiding JavaScript Classes</span></h2><p>Much of the logic for imp
 ortant operations, such as client-side validation (and input field decoration), are based on the use of client-side <a shape="rect" class="external-link" href="http://api.prototypejs.org/language/Class/" >JavaScript classes</a>. This has been somewhat valuable in terms of making the behavior controllable via monkey patching. On the other hand, it cam be clumsy to accomplish in practice, as the desired behavior is only described in terms of the implementation.</p><p>In addition, these JavaScript class instances are yet more memory for the browser to manage.</p><p>By using a fine-grained set of PubSub messages, the logic usually bundled into a single JavaScript class can be assembled (and, in theory, replaced) more easily. In addition, Tapestry can do less. For instance, rather than monkey-patching the <code>Tapestry.ZoneManager</code> class to enable new behavior when a Zone element is updated, relying on a PubSub message to learn when the Zone was updated, and perform the desired up
 dates or animations there.</p><h2 id="JavaScriptRewritein5.4-ExposeGlobalMessageCatalogtoClient">Expose Global Message Catalog to Client</h2><p>Tapestry currently maintains two global message catalogs; a global server-side catalog (usually named <code>WEB-INF/app.properties) and a client-side catalog. (app.properties provides application-specific messages, and overrides of other messages provided by Tapestry and other third-party libraries. The global message catalog is actually a composite of all of these sources.) </code>The client-side catalog is smaller, more limited, and less extensible.</p><p>Allowing the client application to have full access to the entire message catalog would make maintaining the catalog simpler, and make it easier to keep client-side and server-side messages consistent.</p><p>For security purposes, it should be possible to exclude some keys from the message catalog exposed to the client. In addition, keys whose values include <code>String.format()</code> p
 roductions (for example, <code>%s</code>) should be excluded, as those productions are meaningless in the client.</p><h2 id="JavaScriptRewritein5.4-PartialPageUpdateResponse">Partial Page Update Response</h2><p>A key part of Tapestry's dynamic behavior has been the partial page update; a specific JSON reply to Ajax requests (usually initiated via a Zone component).</p><p>The format and behavior of the response has evolved from release to release.</p><p>When an Ajax request is processed by the server, the response should handle any of a number of outcomes:</p><ul><li>Redirect the entire page to a new URL (on the server, or elsewhere)</li><li>A server-side error to be presented to the user. (This was greatly enhanced in 5.3 to present the full exception report in a pop-up iframe.)</li><li>Update the content of an implicit (originating) element; typically the element for the Zone that triggered the request</li><li>Update the content of any number of other elements (identified by their 
 client-side id)</li><li>Inject new JavaScript libraries into the page</li><li>Inject new CSS links into the page</li><li>Peform initializations (using <code>T5.initializers</code>) ... but only after all content updates have occurred</li></ul><p>The injected JavaScript libraries and CSS links will often duplicate libraries and CSS links already present on the page; when the page is partially rendered, the server has no way to know what full or partial page renders have already occurred. (It might be possible for the request to include a list of what's already loaded in the browser, so that the server can filter what it sends back; however, given factors such as content compression and typical upload vs. download bandwidth, it is almost certainly more effective for the browser to send too much, and let the client filter out duplicates.)</p><p>Tapestry 5.3 first loads any additional JavaScript (usually by adding new <code>&lt;script&gt;</code> tags to the page). Once JavaScript librar
 ies and CSS links have been added, and JavaScript libraries have been loaded, the DOM is updated with the new content. Lastly, any initializations are processed.</p><p>For Tapestry 5.4, a number of changes are planned:</p><ul><li>Tapestry 5.3 style initializations will be a specific application of 5.4 style module requirement and invocation</li><li><a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/InitializationPriority.html#IMMEDIATE">IMMEDIATE</a> may occur before DOM changes</li><li>Module requirement/invocation will occur in <a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/InitializationPriority.html">initialization priority order</a>; for any single priority, initialization will occur in render order. (Technically, in the order of invocations on JavaScriptSupport.)</li><li>The response will be embeddable inside other JSONOb
 ject responses.</li></ul><p>To expand on the last note first; the keys that define imported JavaScript and CSS, module requirement and invocation, and content update will not be top-level keys of the JSONObject response: they will be buried inside a <code>tapestry</code> top-level key. An available function will be provided that takes an arbitrary JSONObject, extracts the <code>tapestry</code> key and handles it, then invokes a provided callback before the module requirement and invocation step. The intent is for requests that perform purely data oriented operations, the server-side can not only provide a response, but can <em>piggy back</em> client-side updates in the response.</p><h1 id="JavaScriptRewritein5.4-MaintainingBackwardsCompatibility">Maintaining Backwards Compatibility</h1><p>Backwards compatibility is the greatest challenge here; ideally, applications (and third party libraries) that were written for Tapestry 5.3 will continue to operate unchanged in Tapestry 5.4.</p><
 p>At the same time, much of what Tapestry 5.3 does on the client and server should be deprecated (and hopefully, simplified).</p><p>Compatibility mode will be initially enabled, via a <a shape="rect" href="symbols.html">symbol</a> value.</p><p>In compatibility mode, additional client-side JavaScript will be loaded to provide the same <code>T5</code> and <code>Tapestry</code> namespaces available in Tapestry 5.3.</p><p>The implementations of these namespaces will be reconstructed in terms of the new module system. The loading of the compatibility layer will occur during full page render.</p><h1 id="JavaScriptRewritein5.4-TwitterBootstrap">Twitter Bootstrap</h1><p>In Tapestry 5.3 and earlier, Tapestry automatically includes a default CSS link on all pages. This CSS file acts as a partial CSS reset (normalizing the look of the application across common browsers), and provides a large number of CSS rules that many Tapestry components expect to be present. The CSS rules are all given a "
 t-" (for Tapestry) prefix.</p><p>For Tapestry 5.4, this default CSS link will be changed to be the default <a shape="rect" class="external-link" href="http://twitter.github.com/bootstrap/" >Twitter Bootstrap</a>. This will not only refresh the Tapestry look and feel, but will provide a better structure for customizing the application's look and feel.</p><p>As with today, it will be possible to override the location of this CSS file (for example, to use a newer version of Bootstrap than is packaged in the application, or an application-specific customized version).</p><p>This will entail some changes to some components, to make use of reasonable or equivalent Bootstrap CSS classes, rather than the Tapestry 5.3 classes.</p><p>Twitter Bootstrap also includes a number of jQuery-based plugins; these will be exposed in the module system.</p><h1 id="JavaScriptRewritein5.4-ContentDeliveryNetworkIntegration">Content Delivery Network Integration</h1><p>Tapestry 5.3 has limited ability to inte
 grate into a <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Content_delivery_network" >content delivery network</a>; it can dynamically rewrite URLs for assets (including JavaScript libraries, CSS files, image files, etc.). However, it assumes that the CDN can "pull" the content, as needed, from the live site.</p><p>A desirable feature would be request URL that would produce a JSON-formatted report of all assets that should be mirrored by the CDN: this would include all files that might be exposed to the browser, including virtual assets (such as JavaScript stacks, aggregated modules, and so forth). This could be leveraged by a tool that would use this information to extract the assets from the live application and exported to the CDN.</p><p>Determining what assets are available is somewhat problematic as Tapestry mixes server-side only resources (.class files, .tml files, etc.) freely with assets that might be exposed to the browser. (This should never hav
 e been the case, but that's hindsight.) Some of those server-side resource may expose details, such as other server hosts and potentially user names and passwords, that should never be exposed to the client.</p><p>In addition, a "walk" of the classpath to locate potential exportable assets can be quite expensive (though not considerably more so than what Tapestry already does at startup to identify page and component classes).</p><h1 id="JavaScriptRewritein5.4-ExtJSCompatibility">ExtJS Compatibility</h1><p>To be determined. ExtJS inlcudes it own system for dynamically loading ExtJS modules, as well as expressing dependencies between them. Its capabilities overlap what RequireJS offers. It would be nice if, in an ExtJS application, the ExtJS loader could be used instead of RequireJS, or at least, ensure that they do not interfere with each other.</p><h1 id="JavaScriptRewritein5.4-MoreThoughts">More Thoughts</h1><p>This is a big undertaking; this document is not a contract, and is cer
 tainly not complete, but is only starting point for discussions about what will be forthcoming in Tapestry 5.4.</p></div>
+<div id="ConfluenceContent"><p><style type="text/css">/*<![CDATA[*/
+div.rbtoc1437340811180 {padding: 0px;}
+div.rbtoc1437340811180 ul {list-style: disc;margin-left: 0px;}
+div.rbtoc1437340811180 li {margin-left: 0px;padding-left: 0px;}
+
+/*]]>*/</style></p><div class="toc-macro rbtoc1437340811180">
+<ul class="toc-indentation"><li><a shape="rect" href="#JavaScriptRewritein5.4-TapestryandJavaScript">Tapestry and JavaScript</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-TapestryJavaScriptLimitations(through5.3)">Tapestry JavaScript Limitations (through 5.3)</a>
+<ul class="toc-indentation"><li><a shape="rect" href="#JavaScriptRewritein5.4-DependenceonPrototype/Scriptaculous">Dependence on Prototype/Scriptaculous</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-LackofDocumentation">Lack of Documentation</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-LackofModuleStructure">Lack of Module Structure</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-ComplexInitialization">Complex Initialization</a></li></ul>
+</li><li><a shape="rect" href="#JavaScriptRewritein5.4-JavaScriptImprovementsfor5.4">JavaScript Improvements for 5.4</a>
+<ul class="toc-indentation"><li><a shape="rect" href="#JavaScriptRewritein5.4-RequireJS">RequireJS</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-SlowPageLoadandInitialization">Slow Page Load and Initialization</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-MappingModulestoAssets">Mapping Modules to Assets</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-ExtensionstoJavaScriptSupport">Extensions to JavaScriptSupport</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-AvoidingJavaScriptClasses">Avoiding JavaScript Classes</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-ExposeGlobalMessageCatalogtoClient">Expose Global Message Catalog to Client</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-PartialPageUpdateResponse">Partial Page Update Response</a></li></ul>
+</li><li><a shape="rect" href="#JavaScriptRewritein5.4-MaintainingBackwardsCompatibility">Maintaining Backwards Compatibility</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-TwitterBootstrap">Twitter Bootstrap</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-ContentDeliveryNetworkIntegration">Content Delivery Network Integration</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-ExtJSCompatibility">ExtJS Compatibility</a></li><li><a shape="rect" href="#JavaScriptRewritein5.4-MoreThoughts">More Thoughts</a></li></ul>
+</div><h1 id="JavaScriptRewritein5.4-TapestryandJavaScript">Tapestry and JavaScript</h1><p>Tapestry 5 has had a interesting mix of characteristics.</p><p>On the one hand, it has had a large number of features that work, and work well, right out of the box, with no special configuration or setup. This includes client-side validation, dynamic content updates, simple animations, progressive enhancement, and other standard Ajax and DHTML use cases.</p><p>In addition, Tapestry has evolved, from Tapestry 5.0 through 5.3, into a quite capable <em>provisioning</em> framework:</p><ul><li>JavaScript libraries may be combined into <em>stacks</em> that are combined (in production) into a single virtual file</li><li>JavaScript libraries and CSS files may be minified</li><li>Libraries, stacks, and other resources are exposed to the browser with a versioned URL and far-future expires header, to support aggressive client-caching</li><li>Resources, including JavaScript and CSS, can be distributed in
 side JARs (as part of reusable component libraries)</li><li>Compressible resources will be automatically GZip compressed if the client supports it</li></ul><p>However, JavaScript support in Tapestry is still unsatisfactory. Too often, Tapestry falls into an <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Uncanny_valley" >uncanny valley</a> where the framework (server-side and client-side) does so much automatically that it becomes accepted that it does everything ... developers later discover, to their dismay, that the last 10% of custom behavior they desire is very hard to implement, because of all the common problems that plague any complex system: insufficient APIs, unexpected leaky abstractions, or just plain bugs.</p><p>Common examples of the challenges imposed by Tapestry include implementing a Confirm mixin, customizing behavior when a Zone component is dynamically updated, or any number of issues related to Forms, form elements, and Ajax updates.</p>
 <p>This document is a roadmap for how Tapestry 5.4 will revisit the relationship between server-side Java and client-side JavaScript. Ultimately, we hope to convert this relationship from an obstacle to using Tapestry into an essential reason to select Tapestry in the first place.</p><h1 id="JavaScriptRewritein5.4-TapestryJavaScriptLimitations(through5.3)">Tapestry JavaScript Limitations (through 5.3)</h1><h2 id="JavaScriptRewritein5.4-DependenceonPrototype/Scriptaculous">Dependence on Prototype/Scriptaculous</h2><p>Tapestry made an early choice to embrace Prototype and Scriptaculous at a time when this made sense, circa 2006-2007.</p><p>The goal was to have Tapestry provide a client-side API, the <code>Tapestry</code> namespace, that in turn would delegate complex behaviors (including DOM element selection, event management, and XmlHttpRequest processing) to a <em>foundational framework</em>. The goal was to isolate all the direct dependencies on Prototype in such a way that it wou
 ld be possible, in the future, to swap out for a different foundational framework, such as jQuery or ExtJS. Unfortunately, expediency has proven to make this goal even less reachable!</p><h2 id="JavaScriptRewritein5.4-LackofDocumentation">Lack of Documentation</h2><p>There has not, to date, been an adequate documentation of the <code>T5</code> and <code>Tapestry</code> namespaces, beyond the code itself.</p><h2 id="JavaScriptRewritein5.4-LackofModuleStructure">Lack of Module Structure</h2><p>Beyond the basic use of namespaces, Tapestry has not embraced modern JavaScript usage; specifically, it makes limited use of <em>hygenic functions</em> to form modules. Hygenic functions are JavaScript functions that exist as a way to encapsulate private properties and functions. Tapestry 5.3 makes more use of this pattern than previous releases.</p><p>What modularity is present in the JavaScript is organized around the <code>T5.initializers</code> (<code>Tapestry.Initializers</code>) namespace,
  and the mechanics of full-page and partial-page renders (described more fully below).</p><h2 id="JavaScriptRewritein5.4-ComplexInitialization">Complex Initialization</h2><p>Many users are perplexed by how Tapestry performs initialization. In typical web page construction, the developer would create a <code>&lt;script&gt;</code> block at the bottom of the page, and do initializations there. In Tapestry, it can be much more complex:</p><ul><li>A JavaScript library, containing one or more <em>initialization functions</em>, is created</li><li>The initialization functions must be <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Monkey_patching" >monkey patched</a> into the T5.initializers (or older Tapestry.Initializers) namespace.</li><li>The <a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html">JavaScriptSupport</a> environmental must be used to invoke the function
 , by name, passing it a JSONObject to configure itself (the "specification")</li><li>The affected element must have a unique id attribute, used to coordinate the initialization in the client web browser. (Tapestry assists with unique id allocation, but it would be much better if unique ids were not necessary.)</li></ul><p>This often feels like overkill, but it is necessary for a number of desirable characteristics:</p><ul><li>Initialization code occurs in a single Tapestry-generated <code>&lt;script&gt;</code> block at the end of the page (just before the <code>&lt;/body&gt;</code> tag)</li><li>There is limited support for structuring the order of initialization</li><li>The mechanism works transparently in both full-page render requests (traditional) and partial-page render requests (Ajax)</li></ul><p>Despite this, the Tapestry approach can feel very "heavy". In a bespoke page, initialization that may affect many elements of the page often takes the form of a single event handler, a
 ttached to the <code>&lt;body&gt;</code> element, that catches events that bubble up from much lower in the DOM. The single handler function identifies the applicable elements using CSS selectors, including those that are based on HTML5 data- attributes. Additional data- attributes will define additional behavior ... for example, a URL for a triggered request. This is "light" because:</p><ul><li>There's a single event handler function (rather than a unique handler function instance per element)</li><li>The event handler may be anonymous (there's no name, or possibility of collision)</li><li>Elements are identified by DOM structure and CSS rather than their unique id (the element will often not have an id attribute)</li><li>Additional necessary configuration is directly attached to the element, rather than split</li><li>As the page is dynamically updated, there is no extra "bookkeeping" for added or removed elements; new elements inserted into the DOM dynamically are recognized as ea
 sily as those that were present on the initial render</li></ul><p>By contrast, Tapestry is "heavy":</p><ul><li>The initialization function must have a unique name</li><li>The element must have a unique id, to it can be located by the initialization function</li><li>The event handlers are attached directly to the element</li><li>Duplicated elements will have duplicated event handlers</li><li>Additional behavior is specified as a JSON object passed to the initialization function</li><li>Injecting new elements into the DOM requires invoking initialization functions to wire up the necessary event handlers</li><li>In (older versions of) Internet Explorer, removing elements may leave memory leaks as JavaScript objects retain references to DOM objects and vice-versa</li></ul><h1 id="JavaScriptRewritein5.4-JavaScriptImprovementsfor5.4">JavaScript Improvements for 5.4</h1><p>The goals for Tapestry 5.4 are:</p><ul><li>Break the dependency on Prototype and allow Tapestry to be used with any cl
 ient-side "foundation" framework, seamlessly: minimally, this should include jQuery</li><li>Bring Tapestry's JavaScript approach more inline with modern practices (the "light" approach described above)</li><li>Let the JavaScript be modular, and loaded dynamically and asynchonously, only as needed</li><li>Optimize for fast page loads</li><li>Backwards compatibility to the Tapestry 5.3 approach until at least 5.5 or 5.6</li><li>Simplify Tapestry's client-side behavior, but make it easier to hook into, extend, and override</li></ul><h2 id="JavaScriptRewritein5.4-RequireJS">RequireJS</h2><p>Rather than reinvent the wheel, Tapestry should incorporate a proper JavaScript module loader; <a shape="rect" class="external-link" href="http://requirejs.org/" >RequireJS</a> is an excellent candidate, especially considering the new features provided in its 2.0.1 release.</p><p>RequireJS supports the <a shape="rect" class="external-link" href="https://github.com/amdjs/amdjs-api/wiki/AMD" >AMD (Asyn
 chronous Module Format)</a>, with some additional support for the <a shape="rect" class="external-link" href="http://www.commonjs.org/" >CommonJS</a> module format (the format used by Node.js). The latter is simpler, but is designed for a server-side environment; AMD is specifically designed to handle asynchronous loading of JavaScript into a web browser.</p><p>RequireJS is geared towards bespoke applications; for Tapestry it is expected that some of the pathing and other configuration normally done in the client using the RequireJS API will instead by handled more dynamically on the server, using typically Tapestry configuration and extension mechanisms. For example, RequireJS allows mappings of module names to URLs, which is useful when working with multiple third-party JavaScript libraries that may be organized differently form each other. Tapestry can incorporate such logic on the server side instead, making the interface from the browser to the server uniform, even when the det
 ails of where each module is stored is quite variable.</p><h2 id="JavaScriptRewritein5.4-SlowPageLoadandInitialization">Slow Page Load and Initialization</h2><p>Tapestry 5.1 and up has support for dealing with slow page loads (especially, slow loads of extenal JavaScript). This is necessary, because in slow page load situations, the user may submit a form or click a link <em>before</em> page initialization has added an event handler for that submit or click; it was common in those cases for the a traditional request to be sent to the server for a link or form that was expected by the developer to only be accessed via an Ajax request. Without a server-side check (via the <code>Request.isXHR()</code> method), the server-side event handler would return a response that can not be handled in a traditional request, and the user would see the Tapestry exception report page.</p><p>Tapestry 5.3 and earlier would wait for the page loaded event (by observing <a shape="rect" class="external-lin
 k" href="http://api.prototypejs.org/dom/document/observe/" >Prototype's "dom:loaded" event</a>) before executing any JavaScript initialization functions. Likewise, in a partial page render (Ajax) update, it would ensure that all JavaScript libraries had been loaded before executing any initialization functions.</p><p>It is not clear how this same functionality will be supported in Tapestry 5.4 as the asynchronous module loading makes it difficult to know when all modules have been loaded and all initialization functions have been invoked.</p><p>Tapestry 5.4 uses JavaScript to add a "page loading mask", which is removed once all JavaScript has initialized. Using CSS animation tricks, the mask becomes visible after a fraction of a second, and includes a spinning icon. The page loading mask prevents any interaction by the user on the incompletely initialized page.</p><h2 id="JavaScriptRewritein5.4-MappingModulestoAssets">Mapping Modules to Assets</h2><p>Under RequireJS, modules are ide
 ntified by string that represents a kind of virtual path on the server. The path does not start with a scheme, or a slash, or end with a ".js" suffix: in all those cases, RequireJS will load a JavaScript file but not treat it as a dependency.</p><p>On the server side, Tapestry will map the path to a classpath asset.</p><p>There must be provisions for the following options:</p><ul><li>A module may be overridden (for instance, to work around a bug), in which case a specific asset may be used for the module, rather than the default</li><li>A module may need to be converted from one language to another: specifically, a module may be written in CoffeeScript, and need to be compiled down to JavaScript</li><li>A module's content may be aggregated with other related modules (much like a Tapestry 5.3 stack), especially in production. (A request for any module should provide the aggregated set of modules; RequireJS will not need to send additional requests for the other modules.)</li><li>Modu
 le content (aggregated or not) should be minimized</li></ul><p>In addition, it may be reasonable to have Tapestry automatically (or via some configuration) <a shape="rect" class="external-link" href="http://requirejs.org/docs/commonjs.html" >wrap CommonJS modules as AMD modules</a>. (Traditionally, Tapestry has configured this kind of behavior via service contributions, but there is ample evidence that this could be done using external configuration, perhaps using a JSON file in the module package, to control aggregation, wrapping, and other aspects the process. This would be more agile, as it would not require restarts when the configuration changes.)</p><p>Modules will be stored on the classpath, in a <code>modulejs</code> package below each library's root package. Modules within that package are referenced by their name relative to the package. (A rarely used feature of Tapestry is that a component library name may be mapped to multiple packages; resolving a module name may requi
 re a search among the packages. There is the expectation that the developer will ensure that there are no duplications that would lead to ambiguities.)</p><p>Under this system, module <code>core/pubsub</code> would be the file <code>pubsub.js</code> in the package <code>org.apache.tapestry5.corelib.modulejs</code>, since Tapestry's component library 'core' is mapped to package <code>org.apache.tapestry5.corelib</code>.</p><p>Certain key modules, such as <a shape="rect" class="external-link" href="http://documentcloud.github.com/underscore/" >Underscore</a> may be mapped at the root level, as they are used so often.</p><h2 id="JavaScriptRewritein5.4-ExtensionstoJavaScriptSupport">Extensions to JavaScriptSupport</h2><p>A number of new methods will be added to JavaScriptSupport, to support the following behaviors:</p><ul><li>require one or more modules</li><li>require a module (that exports a single function) and invoke the function, passing zero or more values. (Values passed to modul
 e functions may be limited to String and <a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/json/JSONObject.html">JSONObject</a>.)</li><li>require a module and a function name and invoke named function exported by the module, passing zero or more values</li></ul><p>The intent here is to support shifting of client-side behavior from the 5.3 style, an approach that involved monkey-patching functions onto <code>T5.initializers</code>, and move the same logic into modules, preferably with simpler parameters. It is also expected that there will be greater use of <code>data-</code> prefixed HTML5 attributes in place of separate configuration, as outlined above.</p><h2 id="JavaScriptRewritein5.4-AvoidingJavaScriptClasses"><span style="line-height: 1.5;">Avoiding JavaScript Classes</span></h2><p>Much of the logic for important operations, such as client-side validation (and input field decoration), are based on the use of client-side 
 <a shape="rect" class="external-link" href="http://api.prototypejs.org/language/Class/" >JavaScript classes</a>. This has been somewhat valuable in terms of making the behavior controllable via monkey patching. On the other hand, it cam be clumsy to accomplish in practice, as the desired behavior is only described in terms of the implementation.</p><p>In addition, these JavaScript class instances are yet more memory for the browser to manage.</p><p>By using a fine-grained set of PubSub messages, the logic usually bundled into a single JavaScript class can be assembled (and, in theory, replaced) more easily. In addition, Tapestry can do less. For instance, rather than monkey-patching the <code>Tapestry.ZoneManager</code> class to enable new behavior when a Zone element is updated, relying on a PubSub message to learn when the Zone was updated, and perform the desired updates or animations there.</p><h2 id="JavaScriptRewritein5.4-ExposeGlobalMessageCatalogtoClient">Expose Global Messa
 ge Catalog to Client</h2><p>Tapestry currently maintains two global message catalogs; a global server-side catalog (usually named <code>WEB-INF/app.properties) and a client-side catalog. (app.properties provides application-specific messages, and overrides of other messages provided by Tapestry and other third-party libraries. The global message catalog is actually a composite of all of these sources.) </code>The client-side catalog is smaller, more limited, and less extensible.</p><p>Allowing the client application to have full access to the entire message catalog would make maintaining the catalog simpler, and make it easier to keep client-side and server-side messages consistent.</p><p>For security purposes, it should be possible to exclude some keys from the message catalog exposed to the client. In addition, keys whose values include <code>String.format()</code> productions (for example, <code>%s</code>) should be excluded, as those productions are meaningless in the client.</p
 ><h2 id="JavaScriptRewritein5.4-PartialPageUpdateResponse">Partial Page Update Response</h2><p>A key part of Tapestry's dynamic behavior has been the partial page update; a specific JSON reply to Ajax requests (usually initiated via a Zone component).</p><p>The format and behavior of the response has evolved from release to release.</p><p>When an Ajax request is processed by the server, the response should handle any of a number of outcomes:</p><ul><li>Redirect the entire page to a new URL (on the server, or elsewhere)</li><li>A server-side error to be presented to the user. (This was greatly enhanced in 5.3 to present the full exception report in a pop-up iframe.)</li><li>Update the content of an implicit (originating) element; typically the element for the Zone that triggered the request</li><li>Update the content of any number of other elements (identified by their client-side id)</li><li>Inject new JavaScript libraries into the page</li><li>Inject new CSS links into the page</li
 ><li>Peform initializations (using <code>T5.initializers</code>) ... but only after all content updates have occurred</li></ul><p>The injected JavaScript libraries and CSS links will often duplicate libraries and CSS links already present on the page; when the page is partially rendered, the server has no way to know what full or partial page renders have already occurred. (It might be possible for the request to include a list of what's already loaded in the browser, so that the server can filter what it sends back; however, given factors such as content compression and typical upload vs. download bandwidth, it is almost certainly more effective for the browser to send too much, and let the client filter out duplicates.)</p><p>Tapestry 5.3 first loads any additional JavaScript (usually by adding new <code>&lt;script&gt;</code> tags to the page). Once JavaScript libraries and CSS links have been added, and JavaScript libraries have been loaded, the DOM is updated with the new conten
 t. Lastly, any initializations are processed.</p><p>For Tapestry 5.4, a number of changes are planned:</p><ul><li>Tapestry 5.3 style initializations will be a specific application of 5.4 style module requirement and invocation</li><li><a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/InitializationPriority.html#IMMEDIATE">IMMEDIATE</a> may occur before DOM changes</li><li>Module requirement/invocation will occur in <a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/InitializationPriority.html">initialization priority order</a>; for any single priority, initialization will occur in render order. (Technically, in the order of invocations on JavaScriptSupport.)</li><li>The response will be embeddable inside other JSONObject responses.</li></ul><p>To expand on the last note first; the keys that define imported JavaScript and CSS, modu
 le requirement and invocation, and content update will not be top-level keys of the JSONObject response: they will be buried inside a <code>tapestry</code> top-level key. An available function will be provided that takes an arbitrary JSONObject, extracts the <code>tapestry</code> key and handles it, then invokes a provided callback before the module requirement and invocation step. The intent is for requests that perform purely data oriented operations, the server-side can not only provide a response, but can <em>piggy back</em> client-side updates in the response.</p><h1 id="JavaScriptRewritein5.4-MaintainingBackwardsCompatibility">Maintaining Backwards Compatibility</h1><p>Backwards compatibility is the greatest challenge here; ideally, applications (and third party libraries) that were written for Tapestry 5.3 will continue to operate unchanged in Tapestry 5.4.</p><p>At the same time, much of what Tapestry 5.3 does on the client and server should be deprecated (and hopefully, sim
 plified).</p><p>Compatibility mode will be initially enabled, via a <a shape="rect" href="symbols.html">symbol</a> value.</p><p>In compatibility mode, additional client-side JavaScript will be loaded to provide the same <code>T5</code> and <code>Tapestry</code> namespaces available in Tapestry 5.3.</p><p>The implementations of these namespaces will be reconstructed in terms of the new module system. The loading of the compatibility layer will occur during full page render.</p><h1 id="JavaScriptRewritein5.4-TwitterBootstrap">Twitter Bootstrap</h1><p>In Tapestry 5.3 and earlier, Tapestry automatically includes a default CSS link on all pages. This CSS file acts as a partial CSS reset (normalizing the look of the application across common browsers), and provides a large number of CSS rules that many Tapestry components expect to be present. The CSS rules are all given a "t-" (for Tapestry) prefix.</p><p>For Tapestry 5.4, this default CSS link will be changed to be the default <a shape=
 "rect" class="external-link" href="http://twitter.github.com/bootstrap/" >Twitter Bootstrap</a>. This will not only refresh the Tapestry look and feel, but will provide a better structure for customizing the application's look and feel.</p><p>As with today, it will be possible to override the location of this CSS file (for example, to use a newer version of Bootstrap than is packaged in the application, or an application-specific customized version).</p><p>This will entail some changes to some components, to make use of reasonable or equivalent Bootstrap CSS classes, rather than the Tapestry 5.3 classes.</p><p>Twitter Bootstrap also includes a number of jQuery-based plugins; these will be exposed in the module system.</p><h1 id="JavaScriptRewritein5.4-ContentDeliveryNetworkIntegration">Content Delivery Network Integration</h1><p>Tapestry 5.3 has limited ability to integrate into a <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Content_delivery_network" >con
 tent delivery network</a>; it can dynamically rewrite URLs for assets (including JavaScript libraries, CSS files, image files, etc.). However, it assumes that the CDN can "pull" the content, as needed, from the live site.</p><p>A desirable feature would be request URL that would produce a JSON-formatted report of all assets that should be mirrored by the CDN: this would include all files that might be exposed to the browser, including virtual assets (such as JavaScript stacks, aggregated modules, and so forth). This could be leveraged by a tool that would use this information to extract the assets from the live application and exported to the CDN.</p><p>Determining what assets are available is somewhat problematic as Tapestry mixes server-side only resources (.class files, .tml files, etc.) freely with assets that might be exposed to the browser. (This should never have been the case, but that's hindsight.) Some of those server-side resource may expose details, such as other server 
 hosts and potentially user names and passwords, that should never be exposed to the client.</p><p>In addition, a "walk" of the classpath to locate potential exportable assets can be quite expensive (though not considerably more so than what Tapestry already does at startup to identify page and component classes).</p><h1 id="JavaScriptRewritein5.4-ExtJSCompatibility">ExtJS Compatibility</h1><p>To be determined. ExtJS inlcudes it own system for dynamically loading ExtJS modules, as well as expressing dependencies between them. Its capabilities overlap what RequireJS offers. It would be nice if, in an ExtJS application, the ExtJS loader could be used instead of RequireJS, or at least, ensure that they do not interfere with each other.</p><h1 id="JavaScriptRewritein5.4-MoreThoughts">More Thoughts</h1><p>This is a big undertaking; this document is not a contract, and is certainly not complete, but is only starting point for discussions about what will be forthcoming in Tapestry 5.4.</p><
 /div>
 </div>
 
 <div class="clearer"></div>