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