You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2012/12/26 22:41:26 UTC

[5/6] More modules under a "t5/" folder umbrella e.g. "core/dom" is now "t5/core/dom"

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
deleted file mode 100644
index 1c5ef9b..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-# ##core/form-fragment
-#
-define ["_", "core/dom", "core/events", "core/forms"],
-  (_, dom, events) ->
-
-    SELECTOR = '[data-component-type="core/FormFragment"]'
-
-    # This is mostly for compatibility with 5.3, which supported
-    # a DOM event to ask a fragment to remove itself.  This makes less sense since
-    # default animations were eliminated in 5.4.
-    dom.onDocument events.formfragment.remove, SELECTOR, (event) ->
-      this.remove()
-
-    # When any form fires the prepareForSubmit event, check to see if
-    # any form fragments are contained within, and give them a chance
-    # to enabled/disable their hidden field.
-    dom.onDocument events.form.prepareForSubmit, "form", (event) ->
-
-      fragments = this.find SELECTOR
-
-      _.each fragments, (frag) ->
-
-        fragmentId = frag.attribute "id"
-
-        hidden = frag.findFirst "input[type=hidden][data-for-fragment=#{fragmentId}]"
-
-        # If found (e.g., not alwaysSubmit), then enable/disable the field.
-        hidden && hidden.attribute "disabled", not frag.deepVisible()
-
-    # Again, a DOM event to make the FormFragment visible or invisible; this is useful
-    # because of the didShow/didHide events ... but we're really just seeing the evolution
-    # from the old style (the FormFragment class as controller) to the new style (DOM events and
-    # top-level event handlers).
-    dom.onDocument events.formfragment.changeVisibility, SELECTOR, (event) ->
-        makeVisible = event.memo.visible
-
-        this[if makeVisible then "show" else "hide"]()
-
-        this.trigger events.element[if makeVisible then "didShow" else "didHide"]
-
-        return false
-
-    # Initializes a trigger for a FormFragment
-    #
-    # * spec.triggerId - id of checkbox or radio button
-    # * spec.fragmentId - id of FormFragment element
-    # * spec.invert - (optional) if true, then checked trigger hides (not shows) the fragment
-    linkTrigger = (spec) ->
-      trigger = dom spec.triggerId
-      invert = spec.invert or false
-
-      update = ->
-        checked = trigger.element.checked
-        makeVisible = checked isnt invert
-
-        (dom spec.fragmentId).trigger events.formfragment.changeVisibility,  visible: makeVisible
-
-        return
-
-      if trigger.element.type is "radio"
-        dom.on trigger.element.form, "click", update
-      else
-        trigger.on "click", update
-
-    { linkTrigger }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
deleted file mode 100644
index 934d390..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
+++ /dev/null
@@ -1,167 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# ## core/forms
-#
-# Defines handlers for HTML forms and HTML field elements, specifically to control input validation.
-
-define ["core/events", "core/dom", "core/builder", "_"],
-  (events, dom, builder, _) ->
-
-    # Meta-data name that indicates the next submission should skip validation (typically, because
-    # the form was submitted by a "cancel" button).
-    SKIP_VALIDATION = "t5:skip-validation"
-
-    clearSubmittingHidden = (form) ->
-      hidden = form.findFirst "[name='t:submit']"
-
-      # Clear if found
-      hidden and hidden.value null
-
-      form.meta SKIP_VALIDATION, null
-
-      return
-
-    setSubmittingHidden = (form, submitter) ->
-
-      mode = submitter.attribute "data-submit-mode"
-      isCancel = mode is "cancel"
-      if mode and mode isnt "normal"
-        form.meta SKIP_VALIDATION, true
-
-      hidden = form.findFirst "[name='t:submit']"
-
-      unless hidden
-        firstHidden = form.findFirst "input[type=hidden]"
-        hidden = builder "input", type:"hidden", name:"t:submit"
-        firstHidden.insertBefore hidden
-
-      # TODO: Research why we need id and name and get rid of one if possible.
-      name = if isCancel then "cancel" else submitter.element.name
-      value = Object.toJSON [ submitter.element.id, name ]
-
-      hidden.value value
-
-      return
-
-    # Passed the element wrapper for a form element, returns a map of all the values
-    # for all non-disabled fields (including hidden fields, select, textarea). This is primarily
-    # used when assembling an Ajax request for a form submission.
-    gatherParameters = (form) ->
-      result = {}
-
-      fields = form.find "input, select, textarea"
-
-      _.each fields, (field) ->
-          return if field.attribute "disabled"
-
-          type = field.element.type
-
-          # Ignore types file and submit; file doesn't make sense for Ajax, and submit
-          # is handled by keeping a hidden field active with the data Tapestry needs
-          # on the server.
-          return if type is "file" || type is "submit"
-
-          value = field.value()
-
-          return if value is null
-
-          name = field.element.name
-
-          existing = result[name]
-
-          if _.isArray existing
-            existing.push value
-            return
-
-          if existing
-            result[name] = [existing, value]
-            return
-
-          result[name] = value
-
-      return result
-
-
-    defaultValidateAndSubmit = ->
-
-      if ((this.attribute "data-validate") is "submit") and
-         (not this.meta SKIP_VALIDATION)
-
-        this.meta SKIP_VALIDATION, null
-
-        memo = error: false
-
-        for field in this.find "[data-validation]"
-          field.trigger events.field.inputValidation, memo
-
-        # Only do form validation if all individual field validation
-        # was successful.
-        unless memo.error
-          this.trigger events.form.validate, memo
-
-        if memo.error
-          clearSubmittingHidden this
-          # Cancel the original submit event when there's an error
-          return false
-
-      # Allow certain types of elements to do last-moment set up. Basically, this is for
-      # FormFragment, or similar, to make their hidden field enabled or disabled to match
-      # their UI's visible/hidden status. This is assumed to work or throw an exception; there
-      # is no memo.
-      this.trigger events.form.prepareForSubmit
-
-      # Otherwise, the event is good, there are no validation problems, let the normal processing commence.
-      # Possibly, the document event handler in core/zone will intercept form submission if this
-      # is an Ajax submission.
-      return
-
-    dom.onDocument "submit", "form", defaultValidateAndSubmit
-
-    # On any click on a submit or image, update the containing form to indicate that the element
-    # was responsible for the eventual submit; this is very important to Ajax updates, otherwise the
-    # information about which control triggered the submit gets lost.
-    dom.onDocument "click", "input[type=submit], input[type=image]", ->
-      setSubmittingHidden (dom this.element.form), this
-      return
-
-    # Support for link submits. `data-submit-mode` will be non-null, possibly "cancel".
-    # Update the hidden field, but also cancel the default behavior for the click.
-    dom.onDocument "click", "a[data-submit-mode]", ->
-      form = this.findContainer "form"
-
-      unless form
-        console.error "Submitting link element not contained inside a form element."
-        return false
-
-      setSubmittingHidden form, this
-
-      # Now the ugly part; if we just invoke submit() on the form, it does not trigger
-      # the form's "submit" event, which we need.
-
-      if form.trigger "submit"
-        form.submit()
-
-      # And cancel the default behavior for the original click event
-      return false
-
-    exports =
-      gatherParameters: gatherParameters
-
-      setSubmittingElement: setSubmittingHidden
-
-      # Sets a flag on the form to indicate that client-side validation should be bypassed.
-      # This is typically associated with submit buttons that "cancel" the form.
-      skipValidation: (form) ->
-        form.meta SKIP_VALIDATION, true
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/grid.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/grid.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/grid.coffee
deleted file mode 100644
index eacddf3..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/grid.coffee
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-# ##core/grid
-#
-# Adds support for in-place updates of the Grid component.  The Grid renders a
-# div[data-zone] around the table, and code here intercepts clicks on links that
-# are inside a div[data-inplace-grid-links].
-#
-define ["core/dom", "core/events", "core/console"],
-
-  (dom, events, console) ->
-
-    dom.onDocument "[data-inplace-grid-links] a", ->
-
-      zone = this.findContainer "[data-container-type=zone]"
-
-      unless zone
-        console.error "Unable to find containing zone for live update of grid."
-        return false
-
-      zone.trigger events.zone.refresh, url: this.attribute "href"
-
-      return false
-
-    return null

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/init.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/init.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/init.coffee
deleted file mode 100644
index 0655e19..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/init.coffee
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# ## core/init
-#
-# Compatibility module, invokes functions on the T5.initializers namespace.
-#
-# Introduced in 5.4, to be removed at some point in the future, when T5.initializers is itself no more.
-define ["core/console"],
-
-  (console) ->
-
-    # Temporary, until we rework the client-side input validation.
-
-    T5.initializers.validate = ->
-
-    # Exports a single function that finds an initializer in `T5.initializers` and invokes it.
-    (initName, args...) ->
-      fn = T5.initializers[initName]
-      if not fn
-        console.error "Initialization function '#{initName}' not found in T5.initializers namespace."
-      else
-        fn.apply null, args

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/messages.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/messages.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/messages.coffee
deleted file mode 100644
index 3a93f5a..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/messages.coffee
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# For all of these modules, we've turned off CoffeeScript's normal outer function
-# wrapper, as each module is just a call to `define()` with a function that fulfills
-# the same purpose. This one is different, as it is necessary to compute one of the dependencies.
-# On the server `core/messages/<locale>` is actually generated dynamically, as is a simple
-# mapping of message keys to message values, from the global application message catalog.
-#
-# This module provides access to localized messages from the Tapestry applications' server-side
-# application message catalog (which is, itself, built from multiple resources, some provided by
-# the framework, others provided by the application, or third-party libraries).
-#
-# Messages in the catalog that contain Java-style format specifiers are not included, as there
-# is no facility for formatting those on the client. This is actually done as a simple test for the
-# presence of the `%` character.  In addition, any message key that begins with "private-" is
-# assumed to contain sensitive data (such as database URLs or passwords) and will not be
-# exposed to the client.
-do ->
-  # In the unexpected case that the data-locale attribute is missing, assume English
-  locale = (document.documentElement.getAttribute "data-locale") or "en"
-
-  define ["core/messages/#{locale}", "_", "core/console"],
-    (messages, _, console) ->
-
-      # Returns the application message catalog message for the given key. Returns
-      # a placeholder if the key is not found.
-      get = (key) ->
-        value = messages[key]
-
-        if value
-          return value
-        else
-          console.error "No value for message catalog key '#{key}' exists."
-          return "[[Missing Key: '#{key}']]"
-
-      # Returns all keys that are defined by the underlying catalog, in no specific order.
-      get.keys = -> _.keys messages
-
-
-      # Export get as the main function.
-      return get

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/pageinit.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/pageinit.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/pageinit.coffee
deleted file mode 100644
index 5c7a218..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/pageinit.coffee
+++ /dev/null
@@ -1,218 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# ##core/pageinit
-#
-# Module that defines functions used for page initialization.
-# The initialize function is passed an array of initializers; each initializer is itself
-# an array. The first value in the initializer defines the name of the module to invoke.
-# The module name may also indicate the function exported by the module, as a suffix following a colon:
-# e.g., "my/module:myfunc".
-# Any additional values in the initializer are passed to the function. The context of the function (this) is null.
-define ["_", "core/console", "core/dom", "core/events"],
-  (_, console, dom, events) ->
-    pathPrefix = null
-
-    # Borrowed from Prototype:
-    isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'
-    isIE = !!window.attachEvent && !isOpera
-
-    rebuildURL = (path) ->
-      return path if path.match /^https?:/
-
-      # See Tapestry.rebuildURL() for an error about the path not starting with a leading '/'
-      # We'll assume that doesn't happen.
-
-      if !pathPrefix
-        l = window.location
-        pathPrefix = "#{l.protocol}//#{l.host}"
-
-      return pathPrefix + path
-
-    rebuildURLOnIE =
-      if isIE then rebuildURL else _.identity
-
-    addStylesheets = (newStylesheets) ->
-      return unless newStylesheets
-
-      # Figure out which stylesheets are loaded; adjust for IE, and especially, IE 9
-      # which can report a stylesheet's URL as null (not empty string).
-      loaded = _.chain(document.styleSheets)
-      .pluck("href")
-      .without("")
-      .without(null)
-      .map(rebuildURLOnIE)
-
-      insertionPoint = _.find(document.styleSheets, (ss) -> ss.ownerNode.rel is "stylesheet t-ajax-insertion-point")
-
-      # Most browsers support document.head, but older IE doesn't:
-      head = document.head or document.getElementsByTagName("head")[0]
-
-      _.chain(newStylesheets)
-      .map((ss) -> { href: rebuildURL(ss.href), media: ss.media })
-      .reject((ss) -> loaded.contains(ss.href).value())
-      .each((ss) ->
-        element = document.createElement "link"
-        element.setAttribute "type", "text/css"
-        element.setAttribute "rel", "stylesheet"
-        element.setAttribute "href", ss.href
-        if ss.media
-          element.setAttribute "media", ss.media
-
-        if insertionPoint
-          head.insertBefore element, insertionPoint.ownerNode
-        else
-          head.appendChild element
-
-        console.debug "Added stylesheet #{ss.href}"
-      )
-
-      return
-
-    invokeInitializer = (tracker, qualifiedName, initArguments) ->
-      [moduleName, functionName] = qualifiedName.split ':'
-
-      require [moduleName], (moduleLib) ->
-
-        # Some modules export nothing but do some full-page initialization, such as adding
-        # event handlers to the body.
-        if not functionName and
-          initArguments.length is 0 and
-          not _.isFunction moduleLib
-            console.debug "Loaded module #{moduleName}"
-            tracker()
-            return
-
-        fn = if functionName? then moduleLib[functionName] else moduleLib
-
-        if console.debugEnabled
-          argsString = _.map(initArguments, JSON.stringify).join(", ")
-          console.debug "Invoking #{qualifiedName}(#{argsString})"
-
-        fn.apply null, initArguments
-
-
-        tracker()
-
-    exports =
-      # Passed a list of initializers, executes each initializer in order. Due to asynchronous loading
-      # of modules, the exact order in which initializer functions are invoked is not predictable.
-      initialize: (inits = [], callback) ->
-        console.debug "Executing #{inits.length} inits"
-        callbackCountdown = inits.length + 1
-
-        # tracker gets invoked once after each require/callback, plus once extra
-        # (to handle the case where there are no inits). When the count hits zero,
-        # it invokes the callback (if there is one).
-        tracker = ->
-          callbackCountdown--
-
-          if callbackCountdown is 0
-            console.debug "All inits executed"
-            callback() if callback
-
-        # First value in each init is the qualified module name; anything after
-        # that are arguments to be passed to the identified function. A string
-        # is the name of a module to load, or function to invoke, that
-        # takes no parameters.
-        for init in inits
-          if _.isString init
-            invokeInitializer tracker, init, []
-          else
-            [qualifiedName, initArguments...] = init
-            invokeInitializer tracker, qualifiedName, initArguments
-
-        tracker()
-
-      # Pre-loads a number of libraries in order. When the last library is loaded,
-      # invokes the callback (with no parameters).
-      loadLibraries: (libraries, callback) ->
-        reducer = (callback, library) -> ->
-          console.debug "Loading library #{library}"
-          require [library], callback
-
-        finalCallback = _.reduceRight libraries, reducer, callback
-
-        finalCallback.call null
-
-      # Loads all specified libraries in order (this includes other the core stack, other stacks, and
-      # any free-standing libraries). It then executes the immediate initializations. After that, it waits for the DOM to be
-      # ready (which, given typical Tapestry page structure, it almost certainly is at the point this function
-      # executed), and then executes the other initializations.
-      loadLibrariesAndInitialize: (libraries, inits) ->
-        console.debug "Loading #{libraries?.length or 0} libraries"
-        exports.loadLibraries libraries,
-          -> exports.initialize inits,
-            ->
-              # At this point, all libraries have been loaded, and all inits should have executed. Unless some of
-              # the inits triggered Ajax updates (such as a core/ProgressiveDisplay component), then the page should
-              # be ready to go. We set a flag, mostly used by test suites, to ensure that all is ready.
-              # Note that later Ajax requests do not change this attribute, so their timing continues to be tricky.
-
-              (dom document.documentElement).attribute "data-page-initialized", "true"
-
-      evalJavaScript: (js) ->
-        console.debug "Evaluating: #{js}"
-        eval js
-
-      focus: (fieldId) ->
-        field = dom fieldId
-
-        field && field.focus()
-
-      # Passed the response from an Ajax request, when the request is successful.
-      # This is used for any request that attaches partial-page-render data to the main JSON object
-      # response.  If no such data is attached, the callback is simply invoked immediately.
-      # Otherwise, Tapestry processes the partial-page-render data. This may involve loading some number
-      # of JavaScript libraries and CSS style sheets, and a number of direct updates to the DOM. After DOM updates,
-      # the callback is invoked, passed the response (with any Tapestry-specific data removed).
-      # After the callback is invoked, page initializations occur.  This method returns null.
-
-      # * response - the Ajax response object
-      # * callback - invoked after scripts are loaded, but before page initializations occur (may be null)
-      handlePartialPageRenderResponse: (response, callback) ->
-
-        # Capture the partial page response portion of the overall response, and
-        # then remove it so it doesn't interfere elsewhere.
-        responseJSON = response.responseJSON or {}
-        partial = responseJSON._tapestry
-        delete responseJSON._tapestry
-
-        # Extreme case: the data has a redirectURL which forces an immediate redirect to the URL.
-        # No other initialization or callback invocation occurs.
-        if partial?.redirectURL
-          window.location.href = partial.redirectURL
-          return
-
-        addStylesheets partial?.stylesheets
-
-        # Make sure all libraries are loaded
-        exports.loadLibraries partial?.libraries, ->
-
-          # After libraries are loaded, update each zone:
-          _(partial?.content).each ([id, content]) ->
-            console.debug "Updating content for zone #{id}"
-
-            zone = dom.wrap id
-
-            if zone
-              zone.trigger events.zone.update, { content }
-
-          # Invoke the callback, if present.  The callback may do its own content updates.
-          callback and callback(response)
-
-          # Lastly, perform initializations from the partial page render response.
-          exports.initialize partial?.inits
-
-        return

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/palette.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/palette.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/palette.coffee
deleted file mode 100644
index c435045..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/palette.coffee
+++ /dev/null
@@ -1,228 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# ##core/palette
-#
-# Support for the `core/Palette` component.
-define ["core/dom", "_"],
-  (dom, _) ->
-    class PaletteController
-
-      constructor: (id) ->
-        @selected = (dom id)
-        @container = @selected.findContainer ".t-palette"
-        @available = @container.findFirst ".t-palette-available select"
-        @hidden = @container.findFirst "input[type=hidden]"
-
-        @select = @container.findFirst "[data-action=select]"
-        @deselect = @container.findFirst "[data-action=deselect]"
-
-        @moveUp = @container.findFirst "[data-action=move-up]"
-        @moveDown = @container.findFirst "[data-action=move-down]"
-
-        # Track where reorder is allowed based on whether the buttons actually exist
-        @reorder = @moveUp isnt null
-
-        @valueToOrderIndex = {}
-
-        _.each @available.element.options, (option, i) =>
-          @valueToOrderIndex[option.value] = i
-
-        # This occurs even when the palette is disabled, to present the
-        # values correctly. Otherwise it looks like nothing is selected.
-        @initialTransfer()
-
-        unless @selected.element.disabled
-          @updateButtons()
-          @bindEvents()
-
-      initialTransfer: ->
-        # Get the values for options that should move over
-        values = JSON.parse @hidden.value()
-        valueToPosition = {}
-
-        _.each values, (v, i) -> valueToPosition[v] = i
-
-        e = @available.element
-
-        movers = []
-
-        for i in [(e.options.length - 1)..0] by -1
-          option = e.options[i]
-          value = option.value
-          pos = valueToPosition[value]
-          unless pos is undefined
-            movers[pos] = option
-            e.remove i
-
-        for option in movers
-          @selected.element.add option
-
-      updateAfterChange: ->
-        @updateHidden()
-        @updateButtons()
-
-      updateHidden: ->
-        values = _.pluck(@selected.element.options, "value")
-        @hidden.value JSON.stringify values
-
-      bindEvents: ->
-        @container.on "change", "select", =>
-          @updateButtons()
-          return false
-
-        @select.on "click", =>
-          @doSelect()
-          return false
-
-        @available.on "dblclick", =>
-          @doSelect()
-          return false
-
-        @deselect.on "click", =>
-          @doDeselect()
-          return false
-
-        @selected.on "dblclick", =>
-          @doDeselect()
-          return false
-
-        if @reorder
-          @moveUp.on "click", =>
-            @doMoveUp()
-            return false
-
-          @moveDown.on "click", =>
-            @doMoveDown()
-            return false
-
-      updateButtons: ->
-        @select.element.disabled = @available.element.selectedIndex < 0
-
-        nothingSelected = @selected.element.selectedIndex < 0
-
-        @deselect.element.disabled = nothingSelected
-
-        if @reorder
-          @moveUp.element.disabled = nothingSelected or @allSelectionsAtTop()
-          @moveDown.element.disabled = nothingSelected or @allSelectionsAtBottom()
-
-      doSelect: -> @transferOptions @available, @selected, @reorder
-
-      doDeselect: -> @transferOptions @selected, @available, false
-
-      doMoveUp: ->
-        e = @selected.element
-        pos = e.selectedIndex - 1
-        movers = @removeSelectedOptions @selected
-        before = e.options[if pos < 0 then 0 else pos]
-
-        @reorderOptions movers, before
-
-      doMoveDown: ->
-        e = @selected.element
-        lastSelected = _.chain(e.options).toArray().reverse().find((o) -> o.selected).value()
-
-        lastPos = lastSelected.index
-        before = e.options[lastPos + 2]
-
-        movers = @removeSelectedOptions @selected
-
-        @reorderOptions movers, before
-
-      reorderOptions: (movers, before) ->
-        for mover in movers
-          @addOption @selected, mover, before
-        @updateAfterChange()
-
-      transferOptions: (from, to, atEnd) ->
-        if from.element.selectedIndex is -1
-          return
-
-        _(to.element.options).each (o) -> o.selected = false
-
-        movers = @removeSelectedOptions from
-
-        @moveOptions movers, to, atEnd
-
-      removeSelectedOptions: (select) ->
-        movers = []
-        e = select.element
-        options = e.options
-
-        for i in [(e.length - 1)..(e.selectedIndex)] by -1
-          o = options[i]
-          if o.selected
-            e.remove i
-            movers.unshift o
-
-        return movers
-
-      moveOptions: (movers, to, atEnd) ->
-        _.each movers, (o) =>
-          @moveOption o, to, atEnd
-
-        @updateAfterChange()
-
-      moveOption: (option, to, atEnd) ->
-        before = null
-
-        unless atEnd
-          optionOrder = @valueToOrderIndex[option.value]
-          candidate = _.find to.element.options, (o) => @valueToOrderIndex[o.value] > optionOrder
-          if candidate
-            before = candidate
-
-        @addOption to, option, before
-
-      addOption: (to, option, before) ->
-        try
-          to.element.add option, before
-        catch ex
-          if before is null
-            # IE throws an exception about type mismatch; here's the fix:
-            to.add option
-          else
-            to.add option, before.index
-
-      indexOfLastSelection: (select) ->
-        e = select.element
-        if e.selectedIndex < 0
-          return -1
-
-        for i in [(e.options.length - 1)..(e.selectedIndex)] by -1
-          if e.options[i].selected
-            return i
-
-        return -1
-
-      allSelectionsAtTop: ->
-        last = @indexOfLastSelection @selected
-        options = _.toArray @selected.element.options
-
-        _(options[0..last]).all (o) -> o.selected
-
-      allSelectionsAtBottom: ->
-        e = @selected.element
-        last = e.selectedIndex
-        options = _.toArray e.options
-
-        _(options[last..]).all (o) -> o.selected
-
-
-    initialize = (id) ->
-      new PaletteController(id)
-
-    # Export just the initialize function
-    return initialize
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/select.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/select.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/select.coffee
deleted file mode 100644
index 364f840..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/select.coffee
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# ##core/select
-#
-# Provides a document event handler that triggers an update a zone when the value
-# of a select element within the zone changes.
-define ["core/events", "core/dom", "core/zone"],
-
-  (events, dom, zone) ->
-
-        dom.onDocument "change", "select[data-update-zone]", ->
-
-          containingZone = zone.findZone this
-
-          containingZone and containingZone.trigger events.zone.refresh,
-            url: this.attribute "data-update-url"
-            parameters:
-              "t:selectvalue": this.value()

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/tree.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/tree.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/tree.coffee
deleted file mode 100644
index 43d33f7..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/tree.coffee
+++ /dev/null
@@ -1,113 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# ## core/tree
-#
-# Handlers to support to the core/Tree Tapestry component
-define ["core/dom", "core/ajax", "core/zone"],
-  (dom, ajax) ->
-    TREE = "[data-component-type=core/Tree]"
-    NODE_ID = "data-node-id"
-    SELECTOR = "#{TREE} [#{NODE_ID}]"
-
-    LOADING = "tree-children-loading"
-    LOADED = "tree-children-loaded"
-    EXPANDED = "t-tree-expanded"
-    SELECTED = "t-selected-leaf-node"
-
-    send = (node, action, onsuccess) ->
-      container = node.findContainer TREE
-      url = container.attribute "data-tree-action-url"
-
-      ajax url,
-        parameters:
-          "t:action": action
-          "t:nodeid": node.attribute NODE_ID
-        onsuccess: onsuccess
-
-    loadChildren = (node) ->
-
-      # Ignore duplicate requests to load the children.
-      return if node.meta LOADING
-
-      node.meta LOADING, true
-
-      node.addClass "t-empty-node"
-      node.update "<span class='t-ajax-wait'/>"
-
-      send node, "expand", (reply) ->
-        # Remove the Ajax spinner and  mark the node as expanded (it will have a "-"
-        # icon instead of a "+" icon)
-        node.update("").addClass(EXPANDED).removeClass("t-empty-node")
-
-        label = node.findContainer("li").findFirst(".t-tree-label")
-
-        label.insertAfter reply.responseJSON.content
-
-        node.meta LOADING, false
-        node.meta LOADED, true
-
-    toggle = (node) ->
-      sublist = node.findContainer("li").findFirst("ul")
-
-      if node.hasClass EXPANDED
-        node.removeClass EXPANDED
-        sublist.hide()
-        send node, "markCollapsed"
-        return
-
-      node.addClass EXPANDED
-      sublist.show()
-      send node, "markExpanded"
-
-    clickHandler = ->
-
-      # First case is dynamically loaded due to user action; second case
-      # is rendered with overall page due to server-side expansion model.
-      loaded = (this.meta LOADED) or (this.hasClass EXPANDED)
-
-      if (not loaded) and (not this.hasClass "t-empty-node")
-        loadChildren this
-        return false
-
-      unless this.hasClass "t-leaf-node"
-        toggle this
-        return false
-
-      return false
-
-    toggleSelection = ->
-
-      selected = this.hasClass SELECTED
-
-      node = this.findContainer("li").findFirst("[#{NODE_ID}]")
-
-      if selected
-        this.removeClass SELECTED
-        send node, "deselect"
-      else
-        this.addClass SELECTED
-        send node, "select"
-
-      return false
-
-    dom.onDocument "click", SELECTOR, clickHandler
-
-    dom.onDocument "click",
-      "#{TREE}[data-tree-node-selection-enabled] LI.t-leaf-node > .t-tree-label",
-      toggleSelection
-
-
-    return null
-  

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/utils.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/utils.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/utils.coffee
deleted file mode 100644
index 89eba85..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/utils.coffee
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-## core/utils
-#
-# A few handy functions.
-define ["_"], (_) ->
-
-  trim = (input) ->
-    if String.prototype.trim
-      input.trim()
-    else
-      input.replace(/^\s+/, '').replace(/\s+$/, '')
-
-  exports =
-    # Trims leading and trailing whitespace from a string. Delegates to String.prototype.trim if present.
-    trim: trim
-    # Determines if the input is a blank string, or null, or an empty array.
-    isBlank: (input) ->
-
-        return true if input is null
-
-        if _.isArray input
-          return input.length is 0
-
-        return (exports.trim input).length is 0
-
-    # Splits the input string into words separated by whitespace
-    split: (str) -> _(str.split " ").reject((s) -> s is "")

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/validation.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/validation.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/validation.coffee
deleted file mode 100644
index 2a0bf4d..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/validation.coffee
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-## core/translator
-#
-# Support for Tapestry's built-in set of translators and validators.
-#
-define ["_", "core/dom", "core/events", "core/utils", "core/messages", "core/fields"],
-  (_, dom, events, utils, messages) ->
-
-    REGEXP_META = "t5:regular-expression"
-
-    minus = messages "decimal-symbols.minus"
-    grouping = messages "decimal-symbols.group"
-    decimal = messages "decimal-symbols.decimal"
-
-    # Formats a string for a localized number into a simple number. This uses localization
-    # information provided by `core/messages` to remove grouping seperators and convert the
-    # minus sign and decimal seperator into english norms ("-" and ".") that are compatible
-    # with the `Number` constructor. May throw `Error` if the input can not be parsed.
-    #
-    # A little state machine does the parsing; the input may have a leading minus sign.
-    # It then consists primarily of numeric digits. At least one digit must occur
-    # between each grouping character. Grouping characters are not allowed
-    # after the decimal point.
-    #
-    # * input - input string to be converted
-    # * isInteger - restrict to integer values (decimal point not allowed)
-    parseNumber = (input, isInteger) ->
-
-      canonical = ""
-
-      accept = (ch) -> canonical += ch
-
-      acceptDigitOnly = (ch) ->
-        if ch < "0" or ch > "9"
-          throw new Error messages "core-input-not-numeric"
-
-        accept ch
-        return
-
-      mustBeDigit = (ch) ->
-        acceptDigitOnly ch
-        return any
-
-      decimalPortion = (ch) ->
-        acceptDigitOnly ch
-        return decimalPortion
-
-      any = (ch) ->
-        switch ch
-          when grouping then return mustBeDigit
-          when decimal
-            if isInteger
-              throw new Error messages "core-input-not-integer"
-
-            accept "."
-            return decimalPortion
-          else
-            mustBeDigit ch
-
-      leadingMinus = (ch) ->
-        if ch is minus
-          accept "-"
-          return mustBeDigit
-        else
-          any ch
-
-      state = leadingMinus
-
-      for ch in utils.trim input
-        state = (state ch)
-
-      return Number canonical
-
-    translate = (field, memo, isInteger) ->
-      try
-        result = parseNumber memo.value, isInteger
-
-        if _.isNaN result
-          throw messages "core-input-not-numeric"
-
-        memo.translated = result
-      catch e
-        memo.error = (field.attribute "data-translation-message") or e.message or "ERROR"
-        return false
-
-    dom.onDocument events.field.optional, "[data-optionality=required]", (event, memo) ->
-
-      if utils.isBlank memo.value
-        memo.error =  (this.attribute "data-required-message") or "REQUIRED"
-
-    dom.onDocument events.field.translate, "[data-translation=numeric]", (event, memo) ->
-      translate this, memo, false
-
-    dom.onDocument events.field.translate, "[data-translation=integer]", (event, memo) ->
-      translate this, memo, true
-
-    dom.onDocument events.field.validate, "[data-validate-min-length]", (event, memo) ->
-      min = parseInt this.attribute "data-validate-min-length"
-
-      if memo.translated.length < min
-        memo.error = (this.attribute "data-min-length-message") or "TOO SHORT"
-        return false
-
-    dom.onDocument events.field.validate, "[data-validate-max-length]", (event, memo) ->
-      max = parseInt this.attribute "data-validate-max-length"
-
-      if memo.translated.length > max
-        memo.error = (this.attribute "data-max-length-message") or "TOO LONG"
-        return false
-
-    dom.onDocument events.field.validate, "[data-validate-max]", (event, memo) ->
-      max = parseInt this.attribute "data-validate-max"
-
-      if memo.translated > max
-        memo.error = (this.attribute "data-max-message") or "TOO LARGE"
-        return false
-
-    dom.onDocument events.field.validate, "[data-validate-min]", (event, memo) ->
-      min = parseInt this.attribute "data-validate-min"
-
-      if memo.translated < min
-        memo.error = (this.attribute "data-min-message") or "TOO SMALL"
-        return false
-
-    dom.onDocument events.field.validate, "[data-validate-regexp]", (event, memo) ->
-
-      # Cache the compiled regular expression.
-      re = this.meta REGEXP_META
-      unless re
-        re = new RegExp(this.attribute "data-validate-regexp")
-        this.meta REGEXP_META, re
-
-      unless re.test memo.translated
-        memo.error = (this.attribute "data-regexp-message") or "INVALID"
-        return false
-
-    # Export the number parser, just to be nice (and to support some testing).
-    return { parseNumber }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/zone-refresh.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/zone-refresh.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/zone-refresh.coffee
deleted file mode 100644
index adfb091..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/zone-refresh.coffee
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# ##core/zone-refresh
-
-define ["core/events", "core/dom", "core/console"],
-  (events, dom, console) ->
-
-    # Initialize a timer for the zone at the specified period (in seconds). The zone will be
-    # refreshed with the provided URL.
-    initialize = (zoneId, period, url) ->
-      zone = dom zoneId
-
-      unless zone
-        console.err "Zone #{zoneId} not found for periodic refresh."
-        return
-
-      # Only one periodic refresh per zone.
-      return if zone.meta "periodic-refresh"
-
-      zone.meta "periodic-refresh", true
-
-      executing = false
-
-      # Whenever the zone updates, we can clear the executing flag.
-
-      zone.on events.zone.didUpdate, -> executing = false
-
-      handler = ->
-        # Don't clog things up if the response rate is too slow
-        return if executing
-
-        # Set the flag now, it will clear when the zone updates.
-        executing = true
-
-        zone.trigger events.zone.refresh, { url }
-
-      intervalId = window.setInterval handler, period * 1000
-
-      # Not sure if this is needed except for IE:
-      (dom window).on "beforeunload", ->
-        window.clearInterval intervalId
-
-    # export the single function:
-    return initialize

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/core/zone.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/zone.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/zone.coffee
deleted file mode 100644
index 427f4ac..0000000
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/zone.coffee
+++ /dev/null
@@ -1,120 +0,0 @@
-# Copyright 2012 The Apache Software Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# ##core/zone
-#
-# Provides a default handler for events related to zones. A zone is any kind of
-# client-side element that can be updated; a zone will normally have a unique id.
-# Typically, a client-side zone element is rendered by, and corresponds to, a server-side
-# core/Zone component; however, certain other components (such as core/ProgressiveDisplay) may
-# also be treated as zones.
-#
-# Most often, a zone is any element with attribute `data-container-type=zone` and corresponds
-# to a core/Zone server-side component.
-define ["core/dom", "core/events", "core/ajax", "core/console", "core/forms",  "_"],
-
-  (dom, events, ajax, console, forms, _) ->
-
-    # For a given element that may have the `data-update-zone` attribute, locates the
-    # zone element. May return null if the zone can not be found (after logging an error
-    # to the console).
-    #
-    # * element - starting point for determining zone
-    findZone = (element) ->
-      zoneId = element.attribute "data-update-zone"
-
-      if zoneId is "^"
-        zone = element.findContainer "[data-container-type=zone]"
-
-        if zone is null
-          console.error "Unable to locate containing zone for #{element}."
-
-        return zone
-
-      zone = dom zoneId
-
-      if zone is null
-        console.error "Unable to locate zone '#{zoneId}'."
-
-      return zone
-
-    dom.onDocument "click", "a[data-update-zone]", ->
-
-      zone = findZone this
-
-      if zone
-        zone.trigger events.zone.refresh,  url: this.attribute "href"
-
-      return false
-
-    dom.onDocument "submit", "form[data-update-zone]", ->
-
-      zone = findZone this
-
-      if zone
-        formParameters = forms.gatherParameters this
-
-        zone.trigger events.zone.refresh,
-          url: (this.attribute "action")
-          parameters: formParameters
-
-      return false
-
-    dom.onDocument events.zone.update, (event) ->
-
-      this.trigger events.zone.willUpdate
-
-      content = event.memo.content
-
-      # The server may have passed down the empty string for the content; that removes the existing content.
-      # On the other hand, the server may have not provided a content key; in that case, content is undefined
-      # which means to leave the existing content alone.
-      #
-      # Note that currently, the willUpdate and didUpdate events are triggered even when the zone is not actually
-      # updated. That may be a bug.
-      unless content is undefined
-        this.update content
-
-      this.trigger events.zone.didUpdate
-
-    dom.onDocument events.zone.refresh, (event) ->
-
-      # A Zone inside a form will render some additional parameters to coordinate updates with the Form on the server.
-      attr = this.attribute "data-zone-parameters"
-
-      parameters = attr and JSON.parse attr
-
-      ajax event.memo.url,
-        parameters: _.extend { "t:zoneid": this.element.id }, parameters, event.memo.parameters
-        onsuccess: (reply) =>
-          this.trigger events.zone.update, content: reply.responseJSON?.content
-
-    # Locates a zone element by its unique id attribute, and (deferred, to a later event loop cycle),
-    # performs a standard refresh of the zone. This is primarily used by the core/ProgressiveDisplay component.
-    #
-    # * id - client id of the element
-    # * url - URL to use to refresh the element.
-    deferredZoneUpdate = (id, url) ->
-
-      _.defer ->
-        zone = dom id
-
-        if zone is null
-          console.error "Could not locate element '#{id}' to update."
-          return
-
-        zone.trigger events.zone.refresh, { url }
-
-    # Most of this module is document-level event handlers, but there's also some exports.
-    return { deferredZoneUpdate, findZone }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.coffee
new file mode 100644
index 0000000..b455011
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.coffee
@@ -0,0 +1,60 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ## t5/core/ajax
+#
+# Exports a single function, that invokes `t5/core/dom:ajaxRequest()` with the provided `url` and a modified version of the
+# `options`.
+#
+# It wraps (or provides) `onsuccess`, `onexception`, and `onfailure` handlers, extended to handle a partial page render
+# response (for success), or properly log a server-side failure or client-side exception, including using the
+# `t5/core/exceptionframe` module to display a server-side processing exception.
+define ["./pageinit", "./dom", "./exceptionframe", "./console", "_"],
+  (pageinit, dom, exceptionframe, console, _) ->
+    (url, options) ->
+      newOptions = _.extend {}, options,
+
+        # Logs the exception to the console before passing it to the
+        # provided exception handler or throwing the exception.
+        onexception: (exception) ->
+          console.error "Request to #{url} failed with #{exception}"
+
+          if options.onexception
+            options.onexception exception
+          else
+            throw exception
+
+        onfailure: (response, failureMessage) ->
+          raw = response.getHeader "X-Tapestry-ErrorMessage"
+          unless _.isEmpty raw
+            message = window.unescape raw
+            console.error "Request to #{url} failed with '#{message}'."
+
+            contentType = response.getHeader "content-type"
+
+            isHTML = contentType and (contentType.split(';')[0] is "text/html")
+
+            if isHTML
+              exceptionframe response.responseText
+          else
+            console.error failureMessage
+
+          options.onfailure and options.onfailure(response)
+
+          return null
+
+        onsuccess: (response) ->
+          pageinit.handlePartialPageRenderResponse response, options.onsuccess
+
+      dom.ajaxRequest url, newOptions
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajaxformloop.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajaxformloop.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajaxformloop.coffee
new file mode 100644
index 0000000..5e98596
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajaxformloop.coffee
@@ -0,0 +1,82 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ## t5/core/ajaxformloop
+#
+# Provides handlers related to the core/AjaxFormLoop component (as well as core/AddRowLink and
+# core/RemoveRowLink).
+define ["./dom", "./events", "./console", "./ajax", "./builder"],
+  (dom, events, console, ajax, builder) ->
+
+    # "afl" is short for "AjaxFormLoop".
+    AFL_SELECTOR = "[data-container-type=core/AjaxFormLoop]"
+    FRAGMENT_TYPE = "core/ajaxformloop-fragment"
+
+    dom.onDocument "click", "#{AFL_SELECTOR} [data-afl-behavior=remove]", ->
+
+      afl = this.findContainer AFL_SELECTOR
+
+      unless afl
+        console.error "Enclosing element for AjaxFormLoop remove row link not found."
+        return false
+
+      url = afl.attribute "data-remove-row-url"
+
+      ajax url,
+        parameters:
+          "t:rowvalue": this.attribute "data-afl-row-value"
+        onsuccess: =>
+          # The server has removed the row from persistent storage, lets
+          # do the same on the UI.
+
+          fragment = this.findContainer "[data-container-type=#{FRAGMENT_TYPE}]"
+
+          # TODO: Fire some before & after events, to allow for animation.
+
+          # The fragment takes with it the hidden fields that control form submission
+          # for its portion of the form.
+          fragment.remove()
+
+      return false
+
+    dom.onDocument "click", "#{AFL_SELECTOR} [data-afl-behavior=insert-before] [data-afl-trigger=add]", ->
+
+      afl = this.findContainer AFL_SELECTOR
+
+      insertionPoint = this.findContainer "[data-afl-behavior=insert-before]"
+
+      url = afl.attribute "data-inject-row-url"
+
+      ajax url,
+        onsuccess: (response) =>
+          content = response.responseJSON?.content
+
+          # Create a new element with the same type (usually "div") and class as this element.
+          # It will contain the new content.
+          newElement = builder insertionPoint.element.tagName,
+              class: insertionPoint.element.className,
+              "data-container-type": FRAGMENT_TYPE
+
+          newElement.update content
+
+          insertionPoint.insertBefore newElement
+
+          # Trigger this event, to inform the world that the zone-like new element has been updated
+          # with content.
+          newElement.trigger events.zone.didUpdate
+
+      return false
+
+    # This module is all event handlers, and no exported functions.
+    return null
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/alert.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/alert.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/alert.coffee
new file mode 100644
index 0000000..fa992ed
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/alert.coffee
@@ -0,0 +1,126 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ## t5/core/alert
+#
+# Support for the core/Alerts components.
+#
+define ["./dom", "./console", "./messages", "./builder", "./ajax", "_"],
+  (dom, console, messages, builder, ajax, _) ->
+
+    severityToClass =
+      success: "alert alert-success"
+      warn: "alert alert-warning"
+      error: "alert alert-error"
+
+    getURL = (container) -> container.attribute "data-dismiss-url"
+
+    removeAlert = (container, alert) ->
+      alert.remove()
+
+      if container.find(".alert").length is 0
+        container.update null
+
+    dismissAll = (container) ->
+      console.debug "dismiss all"
+
+      alerts = container.find "[data-alert-id]"
+
+      if alerts.length is 0
+        container.update null
+        return
+
+      ajax (getURL container),
+        onsuccess: -> container.update null
+
+    dismissOne = (container, button) ->
+      console.debug "dismiss single"
+
+      alert = button.container()
+
+      id = alert.attribute "data-alert-id"
+
+      unless id
+        removeAlert container, alert
+        return
+
+      ajax (getURL container),
+        parameters: { id }
+        onsuccess: -> removeAlert container, alert
+
+    setupUI = (container) ->
+
+      clickHandler = ->
+        dismissAll container
+        return false
+
+      container.update builder ".well",
+        ["div", "data-container-type": "inner"],
+        [".row-fluid > button.btn.btn-mini.pull-right",
+            onclick: clickHandler
+            ["strong", "\u00d7 "],
+            messages "core-dismiss-label"
+        ]
+
+      container.on "click button.close", ->
+        dismissOne container, this
+        return false
+
+    findInnerContainer = ->
+      outer = dom.body().findFirst "[data-container-type=alerts]"
+
+      unless outer
+        console.error "Unable to locate alert container element to present an alert."
+        return null
+
+      # Set up the inner content when needed
+      unless outer.element.firstChild
+        setupUI outer
+
+      return outer?.findFirst "[data-container-type=inner]"
+
+    # The `data` for the alert has a number of keys to control its behavior:
+    #
+    # * severity - used to determine the CSS class, may be "warn", "error", or "info" (the default)
+    # * message - message to display to as te alert's body
+    # * markup - if true, then the message contains markup that should not be HTML escaped
+    alert = (data) ->
+
+      container = findInnerContainer()
+
+      return unless container
+
+      className = severityToClass[data.severity] or "alert"
+
+      content = if data.markup then data.message else dom.escapeHTML data.message
+
+      # Note that `data-dismiss=alert` is purposely excluded
+      # - we want to handle closes w/ notifications to the server if not transient
+      # - we don't want to rely on bootstrap.js, as that will drag jQuery into the application
+      element = builder "div", class: className,
+        ["button.close", "\u00d7"]
+        content
+
+      if data.id
+        element.attribute "data-alert-id", data.id
+
+      container.append element
+
+      if data.transient
+        _.delay removeAlert, exports.TRAINSIENT_DURATION, container, element
+
+    alert.TRAINSIENT_DURATION = 5000
+
+    # Export the alert function
+    exports = alert

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/autocomplete.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/autocomplete.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/autocomplete.coffee
new file mode 100644
index 0000000..f45e415
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/autocomplete.coffee
@@ -0,0 +1,40 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ## t5/core/autocomplete
+#
+# Support for the core/Autocomplete Tapestry mixin.
+define ["./dom", "./ajax", "jquery", "bootstrap"],
+  (dom, ajax, $) ->
+
+    doLookup = ($field, url, query, process) ->
+      $field.addClass "ajax-wait"
+
+      ajax url,
+        parameters:
+          "t:input": query
+        onsuccess: (response) ->
+          $field.removeClass "ajax-wait"
+
+          process response.responseJSON.matches
+
+    init = (spec) ->
+      $field = $ document.getElementById spec.id
+
+      $field.typeahead
+        minLength: spec.minChars
+        source: (query, process) -> doLookup $field, spec.url, query, process
+
+
+    exports = init
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/builder.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/builder.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/builder.coffee
new file mode 100644
index 0000000..5868751
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/builder.coffee
@@ -0,0 +1,128 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ## t5/core/builder
+#
+# A system for constructing DOM element nodes for a particular structure in minimal code.  The basic syntax is:
+# `builder(elementDescription, body...)` and the result is a `t5/core/dom:ElementWrapper` (a wrapper around the constructed
+# DOM elements). The element description is primarily the name of the element.
+#
+# The element description may contain sequences of "._name_"; these appropriate CSS syntax to describe a CSS class name
+# for the element.  The element name may be omitted when CSS class names are specified, in which case `div` is the
+# default element name. Multiple CSS class names are allowed, e.g., `button.btn.btn-primary`.
+#
+# The description may also include the `>` character; this represents the start of a nested element; in this way
+# a structure can quickly be specified. e.g. `builder "label.checkbox > input", type: "checkbox", " Remember me"`
+# would construct:
+#
+#     <label class="checkbox">
+#        <input type="checkbox"> Remember me</input>
+#     </label>
+#
+# The body may consist of:
+#
+# * Objects: used to specify attributes and event handlers of the element
+# * Strings: literal markup text
+# * Array: a nested element definition
+#
+# Literal text is NOT escaped. You should be careful to use `t5/core/dom:escapeHTML` if the body contains
+# any potential markup that should be escaped; alternately, it may be easier to use embedded markup in the body
+# than to use an element definition.
+#
+# For an Object, each key and value is simply added as an attribute. However, for keys that start with "on", the value
+# is assumed to be an event handler function. The special key "on" consists of nested event handlers for the events
+# whose name matches the key. The following are equivalent:
+#
+#     { onclick: -> ... }
+#
+# and
+#
+#     { on: { click: -> ... }}
+define ["_", "./dom", "./utils"], (_, dom, utils) ->
+  # _internal_: creates a single DOM element and CSS class attribute
+  createElement = (elementDescription) ->
+    # TODO: Support #id for setting the id of an element, maybe others, such as ?name for the name of an input element.
+    # That will require a regex or more sophisticated parsing.
+    terms = utils.trim(elementDescription).split(".")
+
+    elementName = terms.shift() or "div"
+
+    element = document.createElement elementName
+
+    if terms.length
+      element.className = terms.join " "
+
+    return element
+
+  # _internal_: adds attributes and event handlers to a DOM element
+  addAttributes = (element, attributes) ->
+    return unless attributes
+
+    wrapper = dom element
+
+    for name, value of attributes
+      if name is "on"
+        for eventName, handler of value
+          wrapper.on eventName, handler
+      else if name.startsWith "on"
+        wrapper.on (name.substring 2), value
+      else
+        wrapper.attribute name, value
+
+    return null
+
+  # _internal_: processes the body, adding attributes and nested nodes to the DOM element
+  addAttributesAndBody = (element, body) ->
+    for nested in body
+      unless nested?
+        # Ignore null nodes
+      else if _.isString nested
+        element.innerHTML += nested
+      else if _.isArray nested
+        [elementDescription, nestedBody...] = nested
+        nestedElement = buildTree elementDescription, nestedBody
+        element.appendChild nestedElement
+      else if _.isObject nested
+        addAttributes element, nested
+      else throw new Error "Unexpected body value <#{nested}> while building DOM elements."
+
+    return null
+
+  # _internal_: builds the tree from the element description, handing nested nodes, and split
+  # descriptions (containing `>`), returning the topmost DOM element
+  buildTree = (elementDescription, body) ->
+    splitx = elementDescription.indexOf ">"
+    currentDescription =
+      if splitx is -1
+        elementDescription
+      else
+        elementDescription.substring 0, splitx
+
+    element = createElement currentDescription
+
+    if splitx is -1
+      addAttributesAndBody element, body
+    else
+      nestedDescription = elementDescription.substring splitx + 1
+      nestedElement = buildTree nestedDescription, body
+      element.appendChild nestedElement
+
+    return element
+
+  # The module exports a single function that builds the tree of elements and returns the top element, wrapped as an
+  # `t5/core/dom:ElementWrapper`.
+  (elementDescription, body...) ->
+    element = buildTree elementDescription, body
+
+    dom element
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
new file mode 100644
index 0000000..d35478a
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
@@ -0,0 +1,98 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ## t5/core/console
+#
+# A wrapper around the native console, when it exists.
+define ["./dom", "./builder", "_"], (dom, builder, _) ->
+  nativeConsole = {}
+  floatingConsole = null
+
+  FADE_DURATION = 0.25
+
+  # module exports are mutable; someone else could
+  # require this module to change the default DURATION
+  exports =
+  # Default duration for floating console is 10 seconds.
+    DURATION: 10
+
+  try
+    # FireFox will throw an exception if you even access the console object and it does
+    # not exist. Wow!
+    nativeConsole = console
+  catch e
+
+  # _internal_: displays the message inside the floating console, creating the floating
+  # console as needed.
+  display = (className, message) ->
+    unless floatingConsole
+      floatingConsole = builder ".t-console"
+      dom.body().prepend floatingConsole
+
+    div = builder ".t-console-entry.#{className}", (dom.escapeHTML message)
+
+    floatingConsole.append div.hide().fadeIn FADE_DURATION
+
+    removed = false
+
+    runFadeout = ->
+      div.fadeOut FADE_DURATION, ->
+        div.remove() unless removed
+
+    window.setTimeout runFadeout, exports.DURATION * 1000
+
+    div.on "click", ->
+      div.remove()
+      removed = true
+
+  level = (className, consolefn) ->
+    (message) ->
+      # consolefn may be null if there's no console; under IE it may be non-null, but not a function.
+      unless consolefn
+        # Display it floating. If there's a real problem, such as a failed Ajax request, then the
+        # client-side code should be alerting the user in some other way, and not rely on them
+        # being able to see the logged console output.
+        display className, message
+        return
+
+      if _.isFunction consolefn
+        # Use the available native console, calling it like an instance method
+        consolefn.call console, message
+      else
+        # And IE just has to be different. The properties of console are callable, like functions,
+        # but aren't proper functions that work with `call()` either.
+        consolefn message
+
+      return
+
+
+  # Determine whether debug is enabled by checking for the necessary attribute (which is missing
+  # in production mode).
+  exports.debugEnabled = (document.documentElement.getAttribute "data-debug-enabled")?
+
+  # When debugging is not enabled, then the debug function becomes a no-op.
+  exports.debug =
+    if exports.debugEnabled
+      # If native console available, go for it.  IE doesn't have debug, so we use log instead.
+      level "t-debug", (nativeConsole.debug or nativeConsole.log)
+    else
+      ->
+
+  exports.info = level "t-info", nativeConsole.info
+  exports.warn = level "t-warn", nativeConsole.warn
+  exports.error = level "t-err", nativeConsole.error
+
+  # Return the exports; we keep a reference to it, so we can see exports.DURATION, even
+  # if some other module imports this one and modifies that property.
+  return exports
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3a7ac53e/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/datefield.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/datefield.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/datefield.coffee
new file mode 100644
index 0000000..aead041
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/datefield.coffee
@@ -0,0 +1,165 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ## t5/core/datefield
+#
+# Provides support for the `core/DateField` component.
+define ["./dom", "./events", "./messages", "./builder", "./ajax",
+  "_", "./fields"],
+  (dom, events, messages, builder, ajax, _) ->
+
+
+    # Translate from the provided order (SUNDAY = 0, MONDAY = 1), to
+    # the order needed by the DatePicker component (MONDAY = 0 ... SUNDAY = 6)
+    serverFirstDay = parseInt messages "date-symbols.first-day"
+    datePickerFirstDay = if serverFirstDay is 0 then 6 else serverFirstDay - 1
+
+    # Loalize a few other things.
+    DatePicker.months = (messages "date-symbols.months").split ","
+    days = (messages "date-symbols.days").split ","
+
+    # Shuffle sunday to the end, so that monday is first.
+
+    days.push days.shift()
+
+    DatePicker.days = _.map days, (name) -> name.substr(0, 1).toLowerCase()
+
+    DatePicker.TODAY = messages "core-datefield-today"
+    DatePicker.NONE = messages "core-datefield-none"
+
+    # Track the active popup; only one allowed at a time. May look to rework this
+    # later so that there's just one popup and it is moved around the viewport, or
+    # around the DOM.
+    activePopup = null
+
+    class Controller
+      constructor: (@container) ->
+        @field = @container.findFirst "input"
+        @trigger = @container.findFirst "button"
+
+        @trigger.on "click", =>
+          @doTogglePopup()
+          false
+
+      showPopup: ->
+        if activePopup and activePopup isnt @popup
+          activePopup.hide()
+
+        @popup.show()
+        activePopup = @popup
+
+      hidePopup: ->
+        @popup.hide()
+        activePopup = null
+
+      doTogglePopup: ->
+        return if @field.element.disabled
+
+        unless @popup
+          @createPopup()
+          activePopup?.hide()
+        else if @popup.visible()
+          @hidePopup()
+          return
+
+        value = @field.value()
+
+        if value is ""
+          @datePicker.setDate null
+          @showPopup()
+          return
+
+        @field.addClass "ajax-wait"
+
+        ajax (@container.attribute "data-parse-url"),
+          parameters:
+            input: value
+          onerror: (message) =>
+            @field.removeClass "ajax-wait"
+            @fieldError message
+
+          onsuccess: (response) =>
+            @field.removeClass "ajax-wait"
+            reply = response.responseJSON
+
+            if reply.result
+              @clearFieldError()
+
+              date = new Date()
+              date.setTime reply.result
+              @datePicker.setDate date
+              @showPopup()
+              return
+
+            @fieldError (dom.escapeHTML reply.error)
+            @hidePopup()
+            return
+
+      fieldError: (message) ->
+        @field.focus().trigger events.field.showValidationError, { message }
+
+      clearFieldError: ->
+        @field.trigger events.field.clearValidationError
+
+      createPopup: ->
+        @datePicker = new DatePicker()
+        @datePicker.setFirstWeekDay datePickerFirstDay
+        @popup = builder "div.t-datefield-popup"
+        @popup.append dom @datePicker.create()
+        @container.append @popup
+
+        @datePicker.onselect = _.bind @onSelect, this
+
+      onSelect: ->
+        date = @datePicker.getDate()
+
+        if date is null
+          @hidePopup()
+          @clearFieldError()
+          @field.value ""
+          return
+
+        @field.addClass "ajax-wait"
+
+
+        ajax (@container.attribute "data-format-url"),
+          parameters:
+            input: date.getTime()
+          onerror: (message) =>
+            @field.removeClass "ajax-wait"
+            @fieldError message
+          onsuccess: (response) =>
+            @field.removeClass "ajax-wait"
+            @clearFieldError()
+            @field.value response.responseJSON.result
+            @hidePopup()
+
+
+    scan = (root) ->
+      for container in root.find "[data-component-type=core/DateField]"
+        # Hide it from later scans
+        container.attribute "data-component-type", null
+
+        new Controller(container)
+
+    # Initialization:
+
+    scan dom.body()
+
+    # And scan any newly added content:
+
+    dom.onDocument events.zone.didUpdate, -> scan this
+
+    # Exports nothing.
+    return null
\ No newline at end of file