- Jump To … +
  beanvalidator-validation.coffee t5-core-dom-jquery.coffee t5-core-dom-prototype.coffee ajax.coffee ajaxformloop.coffee alert.coffee autocomplete.coffee bootstrap.coffee confirm-click.coffee console.coffee datefield.coffee events.coffee exception-display.coffee exception-frame.coffee fields.coffee form-fragment.coffee forms.coffee init.coffee localdate.coffee messages.coffee moment.coffee pageinit.coffee palette.coffee select.coffee time-interval.coffee tree.coffee utils.coffee validation.coffee zone-refresh.coffee zone.coffee

- # forms.coffee

- ¶

  Copyright 2012, 2013 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/forms

  Defines handlers for HTML forms and HTML field elements, specifically to control input validation.

      define ["t5/core/events", "t5/core/dom", "underscore"],
        (events, dom, _) ->

- ¶

  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"

- ¶

  Data attribute and value added to HTML forms generated by the Tapestry’s Form component from the core library.

          DATA_ATTRIBUTE = "data-generator"
          DATA_ATTRIBUTE_VALUE = "tapestry/core/form"
          TAPESTRY_CORE_FORM_SELECTOR = "form[" + DATA_ATTRIBUTE + "='" + DATA_ATTRIBUTE_VALUE + "']"

          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.attr "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 = dom.create "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

- ¶

  Not going to drag in all of json2 just for this one purpose, but even so, I’d like to get rid of this. Prototype includes Object.toJSON(), but jQuery is curiously absent an equivalent.

            hidden.value "[\"#{submitter.element.id}\",\"#{name}\"]"

            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.attr "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" or type is "submit"

                return if (type is "checkbox" or type is "radio") and field.checked() is false

                value = field.value()

                return if value is null

                name = field.element.name

- ¶

  Many modern UIs create name-less elements on the fly (e.g., Backbone); these may be mixed in with normal elements managed by Tapestry but should be ignored (not sent to the server in a POST or Ajax update).

                return if name is ""

                existing = result[name]

                if _.isArray existing
                  existing.push value
                  return

                if existing
                  result[name] = [existing, value]
                  return

                result[name] = value

            return result

          defaultValidateAndSubmit = ->

            where = -> "processing form submission"

            try

              if ((@attr "data-validate") is "submit") and
                 (not @meta SKIP_VALIDATION)

                @meta SKIP_VALIDATION, null

                hasError = false
                focusField = null

                for field in @find "[data-validation]"
                  memo = {}
                  where = -> "triggering #{events.field.inputValidation} event on #{field.toString()}"
                  field.trigger events.field.inputValidation, memo

                  if memo.error
                    hasError = true
                    focusField = field unless focusField

- ¶

  Only do form validation if all individual field validation was successful.

                unless hasError
                  memo = {}
                  where = -> "trigging cross-form validation event"
                  @trigger events.form.validate, memo

                  hasError = memo.error

                if hasError
                  clearSubmittingHidden this

- ¶

  If a specific field has been identified as the source of the validation error, then focus on it.

                  focusField.focus() if focusField

- ¶

  Trigger an event to inform that form validation results in error

                  where = -> "triggering validation in error event"
                  @trigger events.form.validateInError

- ¶

  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.

              where = -> "triggering #{events.form.prepareForSubmit} event (after validation)"

              @trigger events.form.prepareForSubmit

            catch error
              console.error "Form validiation/submit error `#{error.toString()}', in form #{this.toString()}, #{where()}"
              console.error error
              return false

- ¶

  Otherwise, the event is good, there are no validation problems, let the normal processing commence. Possibly, the document event handler provided by the t5/core/zone module will intercept form submission if this is an Ajax submission.

            return

          dom.onDocument "submit", TAPESTRY_CORE_FORM_SELECTOR, 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", TAPESTRY_CORE_FORM_SELECTOR + " input[type=submit], " + TAPESTRY_CORE_FORM_SELECTOR + " input[type=image]", ->
            setSubmittingHidden (dom @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 = @findParent "form"

            unless form
              console.error "Submitting link element not contained inside a form element."
              return false

            setSubmittingHidden form, @closest "a[data-submit-mode]"

- ¶

  Now the ugly part; if we just invoke submit() on the form, it does not trigger the form’s “submit” event, which we need.

            form.trigger "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