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 Sat Feb
3 18:21:36 2018
@@ -36,13 +36,26 @@
<div class="wrapper bs">
- <div id="navigation"><div class="nav"><ul class="alternate"><li><a
href="index.html">Home</a></li><li><a href="getting-started.html">Getting
Started</a></li><li><a href="documentation.html">Documentation</a></li><li><a
href="download.html">Download</a></li><li><a
href="about.html">About</a></li><li><a class="external-link"
href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a
href="community.html">Community</a></li><li><a class="external-link"
href="http://www.apache.org/security/">Security</a></li><li><a
class="external-link" href="http://www.apache.org/">Apache</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div></div>
+ <div id="navigation"><div class="nav"><ul class="alternate"><li><a
href="index.html">Home</a></li><li><a href="getting-started.html">Getting
Started</a></li><li><a href="documentation.html">Documentation</a></li><li><a
href="download.html">Download</a></li><li><a
href="about.html">About</a></li><li><a class="external-link"
href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a
href="community.html">Community</a></li><li><a class="external-link"
href="http://www.apache.org/security/">Security</a></li><li><a
class="external-link" href="http://www.apache.org/">Apache</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div>
+
+</div>
<div id="top">
- <div id="smallbanner"><div class="searchbox"
style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999;
font-size: 90%">Tapestry docs, issues, wikis & blogs:</span><form
enctype="application/x-www-form-urlencoded" method="get"
action="http://tapestry.apache.org/search.html">
- <input type="text" name="q">
- <input type="submit" value="Search">
-</form></div><div class="emblem" style="float:left"><p><a
href="index.html"><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="http://tapestry.apache.org/images/tapestry_small.png"
data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div><div
class="title" style="float:left; margin: 0 0 0 3em"><h1
id="SmallBanner-PageTitle">JavaScript Rewrite in 5.4</h1></div></div>
+ <div id="smallbanner"><div class="searchbox"
style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999;
font-size: 90%">Tapestry docs, issues, wikis & blogs:</span>
+<form enctype="application/x-www-form-urlencoded" method="get"
action="http://tapestry.apache.org/search.html">
+ <input type="text" name="q">
+ <input type="submit" value="Search">
+</form>
+
+</div>
+
+
+<div class="emblem" style="float:left"><p><a href="index.html"><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="http://tapestry.apache.org/images/tapestry_small.png"
data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div>
+
+
+<div class="title" style="float:left; margin: 0 0 0 3em"><h1
id="SmallBanner-PageTitle">JavaScript Rewrite in 5.4</h1></div>
+
+</div>
<div class="clearer"></div>
</div>
@@ -55,17 +68,21 @@
<div id="content">
<div id="ConfluenceContent"><p><style
type="text/css">/*<![CDATA[*/
-div.rbtoc1478607611980 {padding: 0px;}
-div.rbtoc1478607611980 ul {list-style: disc;margin-left: 0px;}
-div.rbtoc1478607611980 li {margin-left: 0px;padding-left: 0px;}
+div.rbtoc1517682048738 {padding: 0px;}
+div.rbtoc1517682048738 ul {list-style: disc;margin-left: 0px;}
+div.rbtoc1517682048738 li {margin-left: 0px;padding-left: 0px;}
-/*]]>*/</style></p><div class="toc-macro rbtoc1478607611980">
+/*]]>*/</style></p><div class="toc-macro rbtoc1517682048738">
<ul class="toc-indentation"><li><a
href="#JavaScriptRewritein5.4-TapestryandJavaScript">Tapestry and
JavaScript</a></li><li><a
href="#JavaScriptRewritein5.4-TapestryJavaScriptLimitations(through5.3)">Tapestry
JavaScript Limitations (through 5.3)</a>
<ul class="toc-indentation"><li><a
href="#JavaScriptRewritein5.4-DependenceonPrototype/Scriptaculous">Dependence
on Prototype/Scriptaculous</a></li><li><a
href="#JavaScriptRewritein5.4-LackofDocumentation">Lack of
Documentation</a></li><li><a
href="#JavaScriptRewritein5.4-LackofModuleStructure">Lack of Module
Structure</a></li><li><a
href="#JavaScriptRewritein5.4-ComplexInitialization">Complex
Initialization</a></li></ul>
</li><li><a
href="#JavaScriptRewritein5.4-JavaScriptImprovementsfor5.4">JavaScript
Improvements for 5.4</a>
<ul class="toc-indentation"><li><a
href="#JavaScriptRewritein5.4-RequireJS">RequireJS</a></li><li><a
href="#JavaScriptRewritein5.4-SlowPageLoadandInitialization">Slow Page Load and
Initialization</a></li><li><a
href="#JavaScriptRewritein5.4-MappingModulestoAssets">Mapping Modules to
Assets</a></li><li><a
href="#JavaScriptRewritein5.4-ExtensionstoJavaScriptSupport">Extensions to
JavaScriptSupport</a></li><li><a
href="#JavaScriptRewritein5.4-AvoidingJavaScriptClasses">Avoiding JavaScript
Classes</a></li><li><a
href="#JavaScriptRewritein5.4-ExposeGlobalMessageCatalogtoClient">Expose Global
Message Catalog to Client</a></li><li><a
href="#JavaScriptRewritein5.4-PartialPageUpdateResponse">Partial Page Update
Response</a></li></ul>
</li><li><a
href="#JavaScriptRewritein5.4-MaintainingBackwardsCompatibility">Maintaining
Backwards Compatibility</a></li><li><a
href="#JavaScriptRewritein5.4-TwitterBootstrap">Twitter
Bootstrap</a></li><li><a
href="#JavaScriptRewritein5.4-ContentDeliveryNetworkIntegration">Content
Delivery Network Integration</a></li><li><a
href="#JavaScriptRewritein5.4-ExtJSCompatibility">ExtJS
Compatibility</a></li><li><a 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 class="external-link"
href="http://en.wikipedia.org/wiki/Uncanny_valley" rel="nofollow">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 w
ould 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>) namespac
e, 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><script></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
class="external-link" href="http://en.wikipedia.org/wiki/Monkey_patching"
rel="nofollow">monkey patched</a> into the T5.initializers (or older
Tapestry.Initializers) namespace.</li><li>The <a 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 nam
e, 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><script></code> block at
the end of the page (just before the <code></body></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, attached
to the <code><body></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
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>The goals for Tapestry 5.4 are:</p><ul><li>Break
the dependency on Prototype and allow Tapestry to be used with any client-sid
e "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
class="external-link" href="http://requirejs.org/" rel="nofollow">RequireJS</a>
is an excellent candidate, especially considering the new features provided in
its 2.0.1 release.</p><p>RequireJS supports the <a class="external-link"
href="https://github.com/amdjs/amdjs-api/wiki/AMD" rel="nofollow">AMD (Asynchro
nous Module Format)</a>, with some additional support for the <a
class="external-link" href="http://www.commonjs.org/"
rel="nofollow">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 detai
ls 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
class="external-link" href="http:
//api.prototypejs.org/dom/document/observe/" rel="nofollow">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
class="external-link" href="http://requirejs.org/docs/commonjs.html"
rel="nofollow">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 req
uire 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 class="external-link" href="http://documentcloud.github.com/underscore/"
rel="nofollow">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 m
odule functions may be limited to String and <a 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 clas
s="external-link" href="http://api.prototypejs.org/language/Class/"
rel="nofollow">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
Message Cat
alog 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 i
d="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>P
eform 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><script></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 content. Las
tly, 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
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 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, 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 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 class="external-link"
href="http://twitter.gith
ub.com/bootstrap/" rel="nofollow">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 class="external-link"
href="http://en.wikipedia.org/wiki/Content_delivery_network"
rel="nofollow">content delivery network</a>; it can dyna
mically 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 p
asswords, 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><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 class="external-link"
href="http://en.wikipedia.org/wiki/Uncanny_valley" rel="nofollow">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 w
ould 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>) namespac
e, 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><script></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
class="external-link" href="http://en.wikipedia.org/wiki/Monkey_patching"
rel="nofollow">monkey patched</a> into the T5.initializers (or older
Tapestry.Initializers) namespace.</li><li>The <a 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 nam
e, 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><script></code> block at
the end of the page (just before the <code></body></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, attached
to the <code><body></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
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>The goals for Tapestry 5.4 are:</p><ul><li>Break
the dependency on Prototype and allow Tapestry to be used with any client-sid
e "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
class="external-link" href="http://requirejs.org/" rel="nofollow">RequireJS</a>
is an excellent candidate, especially considering the new features provided in
its 2.0.1 release.</p><p>RequireJS supports the <a class="external-link"
href="https://github.com/amdjs/amdjs-api/wiki/AMD" rel="nofollow">AMD (Asynchro
nous Module Format)</a>, with some additional support for the <a
class="external-link" href="http://www.commonjs.org/"
rel="nofollow">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 detai
ls 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
class="external-link" href="http:
//api.prototypejs.org/dom/document/observe/" rel="nofollow">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
class="external-link" href="http://requirejs.org/docs/commonjs.html"
rel="nofollow">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 req
uire 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 class="external-link" href="http://documentcloud.github.com/underscore/"
rel="nofollow">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 m
odule functions may be limited to String and <a 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>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 class="external-link" href="ht
tp://api.prototypejs.org/language/Class/" rel="nofollow">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
Message Catalog to Client</h2><p>Tape
stry 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 re
quest 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 hav
e 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><script></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 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
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/Initi
alizationPriority.html#IMMEDIATE">IMMEDIATE</a> may occur before DOM
changes</li><li>Module requirement/invocation will occur in <a
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, 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 s
tep. 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
href="javascript-rewrite-in-54.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 term
s 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 class="external-link"
href="http://twitter.github.com/bootstrap/" rel="nofollow">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 tha
n 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 class="external-link"
href="http://en.wikipedia.org/wiki/Content_delivery_network"
rel="nofollow">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
woul
d 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">Ex
tJS 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>
Modified: websites/production/tapestry/content/limitations.html
==============================================================================
--- websites/production/tapestry/content/limitations.html (original)
+++ websites/production/tapestry/content/limitations.html Sat Feb 3 18:21:36
2018
@@ -27,6 +27,16 @@
</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/shBrushPlain.js'
type='text/javascript'></script>
+ <script>
+ SyntaxHighlighter.defaults['toolbar'] = false;
+ SyntaxHighlighter.all();
+ </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -67,7 +77,8 @@
</div>
<div id="content">
- <div
id="ConfluenceContent"><plain-text-body>{scrollbar}</plain-text-body><h2
id="Limitations-Limitations">Limitations</h2><h3
id="Limitations-HowdoIaddnewcomponentstoanexistingpagedynamically?">How do I
add new components to an existing page dynamically?</h3><p>The short answer
here is: <strong>you don't</strong>. The long answer here is <strong>you don't
have to, to get the behavior you desire</strong>.</p><p>One of Tapestry basic
values is high scalability: this is expressed in a number of ways, reflecting
scalability concerns within a single server, and within a cluster of
servers.</p><p>Although you code Tapestry pages and components as if they were
ordinary POJOs (<span style="line-height: 1.4285715;">Plain Old Java Objects --
Tapestry does not require you to extend any base classes or implement any
special interfaces)</span><span style="line-height: 1.4285715;">, as deployed
by Tapestry they are closer to a traditional servlet: a single instance of each
page s
ervices requests from multiple threads. Behind the scenes, Tapestry transforms
you code, rewriting it on the fly.</span></p><p>What this means is that
<em>any</em> incoming request must be handled by a <em>single page
instance</em>. Therefore, Tapestry enforces the concept of <strong>static
structure, dynamic behavior</strong>.</p><p>Tapestry provides quite a number of
ways to vary what content is rendered, well beyond simple conditionals and
loops. It is possible to "drag in" components from other pages when rendering a
page (other FAQs will expand on this concept). The point is, that although a
Tapestry page's structure is very rigid, the order in which the components of
the page render does not have to be top to bottom.</p><h3
id="Limitations-Whydoesn'tmyserviceimplementationreloadwhenIchangeit?">Why
doesn't my service implementation reload when I change it?</h3><p>Main article:
<a href="service-implementation-reloading.html">Service Implementation
Reloading</a></p><p>Live servi
ce reloading has some limitations:</p><ul><li>The service must define a
service interface.</li><li>The service implementation must be on the file
system (not inside a JAR).</li><li>The implementation must be instantiated by
Tapestry, not inside code (even code inside a module class).</li><li>The
service must use the default <a
href="defining-tapestry-ioc-services.html">scope</a> (reloading of perthread
scopes is not supported).</li></ul><p>Consider the following example
module:</p><parameter ac:name="controls">true</parameter><parameter
ac:name="linenumbers">true</parameter><plain-text-body>public static void
bind(ServiceBinder binder)
+ <div id="ConfluenceContent"><h2
id="Limitations-Limitations">Limitations</h2><h3
id="Limitations-HowdoIaddnewcomponentstoanexistingpagedynamically?">How do I
add new components to an existing page dynamically?</h3><p>The short answer
here is: <strong>you don't</strong>. The long answer here is <strong>you don't
have to, to get the behavior you desire</strong>.</p><p>One of Tapestry basic
values is high scalability: this is expressed in a number of ways, reflecting
scalability concerns within a single server, and within a cluster of
servers.</p><p>Although you code Tapestry pages and components as if they were
ordinary POJOs (<span>Plain Old Java Objects -- Tapestry does not require you
to extend any base classes or implement any special interfaces)</span><span>,
as deployed by Tapestry they are closer to a traditional servlet: a single
instance of each page services requests from multiple threads. Behind the
scenes, Tapestry transforms you code, rewriting it on the f
ly.</span></p><p>What this means is that <em>any</em> incoming request must be
handled by a <em>single page instance</em>. Therefore, Tapestry enforces the
concept of <strong>static structure, dynamic behavior</strong>.</p><p>Tapestry
provides quite a number of ways to vary what content is rendered, well beyond
simple conditionals and loops. It is possible to "drag in" components from
other pages when rendering a page (other FAQs will expand on this concept). The
point is, that although a Tapestry page's structure is very rigid, the order in
which the components of the page render does not have to be top to
bottom.</p><h3
id="Limitations-Whydoesn'tmyserviceimplementationreloadwhenIchangeit?">Why
doesn't my service implementation reload when I change it?</h3><p>Main article:
<a href="limitations.html">Limitations</a></p><p>Live service reloading has
some limitations:</p><ul><li>The service must define a service
interface.</li><li>The service implementation must be on the file system
(not inside a JAR).</li><li>The implementation must be instantiated by
Tapestry, not inside code (even code inside a module class).</li><li>The
service must use the default <a href="limitations.html">scope</a> (reloading
of perthread scopes is not supported).</li></ul><p>Consider the following
example module:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: true; theme: Default"
style="font-size:12px;">public static void bind(ServiceBinder binder)
{
binder.bind(ArchiveService.class, ArchiveServiceImpl.class);
}
@@ -80,7 +91,8 @@ public static JobQueue buildJobQueue(Mes
return service;
}
-</plain-text-body><p>ArchiveService is reloadable, because Tapestry
instantiates <code>ArchiveServiceImpl</code> itself. On the other hand,
Tapestry invokes <code>buildJobQueue()</code> and it is your code inside the
method that instantiates <code>JobQueueImpl</code>, so the JobQueue service
will not be reloadable.</p><p>Finally, only classes whose class files are
stored directly on the file system, and not packaged inside JARs, are ever
reloadable ... generally, only the services of the application being built (and
not services from libraries) will be stored on the file system. This reflects
the intent of reloading: as an agile development tool, but not something to be
used in deployment.</p><h3
id="Limitations-HowdoIrunmultipleTapestryapplicationsinthesamewebapplication?">How
do I run multiple Tapestry applications in the same web
application?</h3><p>Running multiple Tapestry 5 applications is not supported;
there's only one place to identify the application root package, so even
configuring multiple filters into multiple folders will not
work.</p><p>Support for multiple Tapestry applications in the same web
application was a specific non-goal in Tapestry 5 (it needlessly complicated
Tapestry 4). Given how loosely connected Tapestry 5 pages are from each other,
there doesn't seem to be an advantage to doing so ... and certainly, in terms
of memory utilization, there is a significant down side, were it even
possible.</p><p>You <em>can</em> <span style="color: rgb(0,0,0);">run
a Tapestry 4 app and a Tapestry 5 app side-by-side (the package names are
different, for just this reason), but they know nothing of each other, and
can't interact directly. This is just like the way you could have a single WAR
with multiple servlets; the different applications can only communicate via
URLs, or shared state in the
HttpSession.</span></p><plain-text-body>{scrollbar}</plain-text-body><p> </p><p> </p><p> </p></div>
+</pre>
+</div></div><p>ArchiveService is reloadable, because Tapestry instantiates
<code>ArchiveServiceImpl</code> itself. On the other hand, Tapestry invokes
<code>buildJobQueue()</code> and it is your code inside the method that
instantiates <code>JobQueueImpl</code>, so the JobQueue service will not be
reloadable.</p><p>Finally, only classes whose class files are stored directly
on the file system, and not packaged inside JARs, are ever reloadable ...
generally, only the services of the application being built (and not services
from libraries) will be stored on the file system. This reflects the intent of
reloading: as an agile development tool, but not something to be used in
deployment.</p><h3
id="Limitations-HowdoIrunmultipleTapestryapplicationsinthesamewebapplication?">How
do I run multiple Tapestry applications in the same web
application?</h3><p>Running multiple Tapestry 5 applications is not supported;
there's only one place to identify the application root package, so even config
uring multiple filters into multiple folders will not work.</p><p>Support for
multiple Tapestry applications in the same web application was a specific
non-goal in Tapestry 5 (it needlessly complicated Tapestry 4). Given how
loosely connected Tapestry 5 pages are from each other, there doesn't seem to
be an advantage to doing so ... and certainly, in terms of memory utilization,
there is a significant down side, were it even
possible.</p><p>You <em>can</em> <span style="color: rgb(0,0,0);">run
a Tapestry 4 app and a Tapestry 5 app side-by-side (the package names are
different, for just this reason), but they know nothing of each other, and
can't interact directly. This is just like the way you could have a single WAR
with multiple servlets; the different applications can only communicate via
URLs, or shared state in the
HttpSession.</span> </p><p> </p><p> </p></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/maven-support-faq.html
==============================================================================
--- websites/production/tapestry/content/maven-support-faq.html (original)
+++ websites/production/tapestry/content/maven-support-faq.html Sat Feb 3
18:21:36 2018
@@ -27,6 +27,16 @@
</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/shBrushPlain.js'
type='text/javascript'></script>
+ <script>
+ SyntaxHighlighter.defaults['toolbar'] = false;
+ SyntaxHighlighter.all();
+ </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -67,7 +77,8 @@
</div>
<div id="content">
- <div
id="ConfluenceContent"><plain-text-body>{scrollbar}</plain-text-body><h2
id="MavenSupportFAQ-MavenSupport">Maven Support</h2><h3
id="MavenSupportFAQ-WhydoMavenprojectnamesandotherdetailsshowupinmypages?">Why
do Maven project names and other details show up in my pages?</h3><p>Tapestry
and maven both use the same syntax for dynamic portions of files: the
<code>${...</code>} syntax. When Maven is copying resources from
<code>src/main/resources</code>, and when filtering is <em>enabled</em> (which
is not the default), then any expansions in <em>Tapestry templates</em> that
match against Maven project properties are substituted. If you look at the
deployed application you'll see that <code>${name</code>} is gone, replaced
with your project's name!</p><p>The solution is to update your
<code>pom.xml</code> and ignore any .tml files when copying and
filtering:</p><parameter ac:name="language">xml</parameter><parameter
ac:name="title">pom.xml (partial)</parameter><plain
-text-body> <resource>
+ <div id="ConfluenceContent"><h2
id="MavenSupportFAQ-MavenSupport">Maven Support</h2><h3
id="MavenSupportFAQ-WhydoMavenprojectnamesandotherdetailsshowupinmypages?">Why
do Maven project names and other details show up in my pages?</h3><p>Tapestry
and maven both use the same syntax for dynamic portions of files: the
<code>${...</code>} syntax. When Maven is copying resources from
<code>src/main/resources</code>, and when filtering is <em>enabled</em> (which
is not the default), then any expansions in <em>Tapestry templates</em> that
match against Maven project properties are substituted. If you look at the
deployed application you'll see that <code>${name</code>} is gone, replaced
with your project's name!</p><p>The solution is to update your
<code>pom.xml</code> and ignore any .tml files when copying and
filtering:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>pom.xml
(partial)</b>
</div><div class="codeContent panelContent pdl">
+<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.tml</exclude>
@@ -82,7 +93,8 @@
</includes>
<filtering>false</filtering>
</resource>
-</plain-text-body><plain-text-body>{scrollbar}</plain-text-body></div>
+</pre>
+</div></div><p></p></div>
</div>
<div class="clearer"></div>