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/08/11 00:07:25 UTC
[9/13] git commit: Re-instate the floating console - add
ElementWrapper.prepend(), .fadeIn(),
and .fadeOut() - since core/console depends on core/spi,
core/spi cannot depend on core/console - refactor the code in
core/spi:ajaxRequest() that uses core/con
Re-instate the floating console
- add ElementWrapper.prepend(), .fadeIn(), and .fadeOut()
- since core/console depends on core/spi, core/spi cannot depend on core/console
- refactor the code in core/spi:ajaxRequest() that uses core/console into core/ajax
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/309253ad
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/309253ad
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/309253ad
Branch: refs/heads/5.4-js-rewrite
Commit: 309253ad4dd2b86ebdc4fa947037b0c4dbac4117
Parents: febfe01
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Fri Aug 10 11:24:28 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Fri Aug 10 11:26:54 2012 -0700
----------------------------------------------------------------------
.../coffeescript/META-INF/modules/core/ajax.coffee | 28 +++--
.../META-INF/modules/core/builder.coffee | 50 +++++---
.../META-INF/modules/core/console.coffee | 49 +++++----
.../coffeescript/META-INF/modules/core/spi.coffee | 92 +++++++++++++--
4 files changed, 159 insertions(+), 60 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/309253ad/tapestry-core/src/main/coffeescript/META-INF/modules/core/ajax.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/ajax.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/ajax.coffee
index d503b68..1e35292 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/ajax.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/ajax.coffee
@@ -17,14 +17,25 @@
# Exports a single function, that invokes `core/spi:ajaxRequest()` with the provided `url` and a modified version of the
# `options`.
#
-# It wraps (or provides) `onsuccess` and `onfailure` elements, extended to handle a partial page render reponse (for
-# success), or properly log a server-side failure, including using `core/exceptionframe` module to display a server-side
-# processing exception.
+# 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
+# `core/exceptionframe` module to display a server-side processing exception.
define ["core/pageinit", "core/spi", "core/exceptionframe", "core/console", "_"],
(pageinit, spi, exceptionframe, console, _) ->
- (url, options = {}) ->
+ (url, options) ->
newOptions = _.extend {}, options,
- onfailure: (response) ->
+
+ # 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
@@ -37,12 +48,7 @@ define ["core/pageinit", "core/spi", "core/exceptionframe", "core/console", "_"]
if isHTML
exceptionframe response.responseText
else
- message = "Request to #{url} failed with status #{response.getStatus()}"
- text = response.getStatusText()
- if not _.isEmpty text
- message += " -- #{text}"
-
- console.error message + "."
+ console.error failureMessage
options.onfailure and options.onfailure(response)
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/309253ad/tapestry-core/src/main/coffeescript/META-INF/modules/core/builder.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/builder.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/builder.coffee
index 0bf76d8..2037dfc 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/builder.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/builder.coffee
@@ -12,29 +12,41 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# core/builder
+# ##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 DOM element. The element description is
-# primarily the name of the element.
+# `builder(elementDescription, body...)` and the result is a `core/spi:ElementWrapper` (a wrapper around the constructed
+# DOM elements). The element description is primarily the name of the element.
#
-# It 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. Multiple CSS class
-# names are allowed.
+# 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 a '>' character; this represents the start of a nested element; in this way
-# a structure can quickly be specified.
+# 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 handles of the element
-# Strings: literal text
-# Array: a nested element definition
-# For an Object, each key and value is simply added as an attribute. However, keys that start with "on" are assumed to
-# be event handler functions. The special key "on" consists of nested event handlers for the keys. The following are
-# equivlent:
-# { onclick: -> ... } and
-# { on: { click: -> ... }}
+#
+# * Objects: used to specify attributes and event handlers of the element
+# * Strings: literal text
+# * Array: a nested 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 ["_", "core/spi"], (_, spi) ->
+ # _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.
@@ -48,6 +60,7 @@ define ["_", "core/spi"], (_, spi) ->
return element
+ # _internal_: adds attributes and event handlers to a DOM element
addAttributes = (element, attributes) ->
return unless attributes
@@ -64,6 +77,7 @@ define ["_", "core/spi"], (_, spi) ->
return null
+ # _internal_: processes the body, adding attributes and nested nodes to the DOM element
addAttributesAndBody = (element, body) ->
for nested in body
unless nested?
@@ -80,6 +94,8 @@ define ["_", "core/spi"], (_, spi) ->
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 =
@@ -100,7 +116,7 @@ define ["_", "core/spi"], (_, spi) ->
return element
# The module exports a single function that builds the tree of elements and returns the top element, wrapped as an
- # spi.ElementWrapper.
+ # `core/spi:ElementWrapper`.
(elementDescription, body...) ->
element = buildTree elementDescription, body
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/309253ad/tapestry-core/src/main/coffeescript/META-INF/modules/core/console.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/console.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/console.coffee
index 273b73d..32b0c7b 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/console.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/console.coffee
@@ -12,42 +12,49 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-define ->
+# ##core/console
+#
+# A wrapper around the native console, when it exists.
+define ["core/spi", "core/builder", "_"], (spi, 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
- # seconds
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"
+ spi.body().prepend floatingConsole
+
+ div = builder ".t-console-entry.#{className}", message
+
+ floatingConsole.append div.hide().fadeIn FADE_DURATION
+
+ removed = false
+
+ runFadeout = ->
+ div.fadeOut FADE_DURATION, ->
+ div.remove() unless removed
- # Disable the floating console temporarily, since we have to resolve the tangle of dependencies
- # related to loading vs. the core stack, etc.
+ window.setTimeout runFadeout, exports.DURATION * 1000
- # unless floatingConsole
- # floatingConsole = new Element "div", class: "t-console"
- # $(document.body).insert top: floatingConsole
- #
- # div = new Element "div", class: "t-console-entry #{className}"
- # div.update(_.escape(message)).hide()
- # floatingConsole.insert top:div
- #
- # new Effect.Appear div, duration: .25
- #
- # fade = new Effect.Fade div,
- # delay: exports.DURATION
- # afterFinish: -> div.remove() # was T5.dom.remove(div)
- #
- # div.observe "click", ->
- # fade.cancel()
- # div.remove() # was T5.dom.remove(div)
+ div.on "click", ->
+ div.remove()
+ removed = true
level = (className, consolefn) ->
(message) ->
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/309253ad/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
index 64e8452..5f9dfdc 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
@@ -18,9 +18,7 @@
# This is the core of the abstraction layer that allows the majority of components to operate without caring whether the
# underlying infrastructure framework is Prototype, jQuery, or something else. This is the standard SPI, which wraps
# Prototype ... but does it in a way that makes it relatively easy to swap in jQuery instead.
-#
-# TODO: Define a dependency on "prototype" when that's exposed as a stub module.
-define ["_", "core/console"], (_, console) ->
+define ["_", "prototype"], (_) ->
# _internal_: splits the string into words separated by whitespace
split = (str) ->
@@ -40,7 +38,36 @@ define ["_", "core/console"], (_, console) ->
throw new Error "Provided value <#{content}> is not valid as DOM element content."
- # Generic view of an DOM event that is passed to a handler function.
+ # _internal_: Currently don't want to rely on Scriptaculous, since our needs are pretty minor.
+ animate = (element, styleName, initial, final, duration, callbacks) ->
+ styles = {}
+ range = final - initial
+ initialTime = Date.now()
+ first = true
+ animator = ->
+ elapsed = Date.now() - initialTime
+ if elapsed >= duration
+ styles[styleName] = final
+ element.setStyle styles
+ window.clearInterval timeoutID
+ callbacks.oncomplete and callbacks.oncomplete()
+
+ # TODO: Add an easein/easeout function
+
+ newValue = initial + range * (elapsed / duration)
+
+ element.setStyle styles
+
+ if first
+ callbacks.onstart and callbacks.onstart()
+ first = false
+
+ timeoutID = window.setInterval animator
+
+ styles[styleName] = initial
+ element.setStyle styles
+
+ # Generic view of an DOM event that is passed to a handler function.
#
# Properties:
#
@@ -52,7 +79,7 @@ define ["_", "core/console"], (_, console) ->
# or a key name for others.
class EventWrapper
- constructor: (event) ->
+ constructor: (event) ->
@nativeEvent = event
@memo = event.memo
@type = event.type
@@ -118,7 +145,7 @@ define ["_", "core/console"], (_, console) ->
# Exposes the original element as property `element`.
class ElementWrapper
- constructor: (element) ->
+ constructor: (element) ->
@element = $(element)
# Hides the wrapped element, setting its display to 'none'.
@@ -182,13 +209,48 @@ define ["_", "core/console"], (_, console) ->
@element.update (convertContent content)
this
- # Appends new content (Element or HTML markup string) to the element.
+ # Appends new content (Element, ElementWrapper, or HTML markup string) to the body of the element.
#
# Returns this ElementWrapper.
append: (content) ->
@element.insert bottom: (convertContent content)
this
+ # Prepends new content (Element, ElementWrapper, or HTML markup string) to the body of the element.
+ #
+ # Returns this ElementWrapper
+ prepend: (content) ->
+ @element.insert top: (convertContent content)
+ this
+
+ # Runs an animation to fade-in the element over the specified duration. The element may be hidden (via `hide()`)
+ # initially, and will be made visible (with initial opacity 0, which will increase over time) when the animation
+ # starts.
+ #
+ # * duration - animation duration time, in seconds
+ # * callback - function invoked after the animation is complete
+ #
+ # Returns this ElementWrapper
+ fadeIn: (duration, callback) ->
+ animate @element, "opacity", 0, 1, duration * 1000,
+ onstart: => @element.show()
+ oncomplete: callback
+
+ this
+
+ # Runs an animation to fade out an element over the specified duration. The element should already
+ # be visible and fully opaque.
+ #
+ # * duration - animation duration time, in seconds
+ # * callback - function invoked after the animation is complete
+ #
+ # Returns this ElementWrapper
+ fadeOut: (duration, callback) ->
+ animate @element, "opacity", 1, 0, duration * 1000,
+ oncomplete: callback
+
+ this
+
# Finds the first child element that matches the CSS selector.
#
# Returns the ElementWrapper for the child element, or null if not found.
@@ -255,6 +317,10 @@ define ["_", "core/console"], (_, console) ->
bodyWrapper = null
# Performs an asynchronous Ajax request, invoking callbacks when it completes.
+ #
+ # This is very low level; most code will want to go through the `core/ajax` module instead,
+ # which adds better handling of exceptions and failures, and handles Tapestry's
+ #
# * options.method - "post", "get", etc., default: "post".
# Adds a "_method" parameter and uses "post" to handle "delete", etc.
# * options.contentType - default "context "application/x-www-form-urlencoded"
@@ -262,9 +328,10 @@ define ["_", "core/console"], (_, console) ->
# * options.onsuccess - handler to invoke on success. Passed the XMLHttpRequest transport object.
# Default does nothing.
# * options.onfailure - handler to invoke on failure (server responds with a non-2xx code).
- # Passed the response. Default will log and error to the console.
+ # Passed the response. Default will throw the exception
# * options.onexception - handler to invoke when an exception occurs (often means the server is unavailable).
- # Passed the exception. Default will log an error to the cnsole and throw the exception.
+ # Passed the exception. Default will generate an exception message and throw an `Error`.
+ #
# TODO: Clarify what the response object looks like and/or wrap the Prototype Ajax.Response object.
# TODO: Define what the return value is, or return exports
ajaxRequest = (url, options = {}) ->
@@ -276,7 +343,6 @@ define ["_", "core/console"], (_, console) ->
if options.onexception
options.onexception exception
else
- console.error "Request to #{url} failed with #{exception}"
throw exception
onFailure: (response) ->
@@ -287,8 +353,12 @@ define ["_", "core/console"], (_, console) ->
text = response.getStatusText()
if not _.isEmpty text
message += " -- #{text}"
+ message += "."
- console.error message + "."
+ if options.onfailure
+ options.onfailure response, message
+ else
+ throw new Error(message)
onSuccess: (response) ->