- Jump To … +
  /home/thiago/workspace-tapestry/tapestry-5/tapestry-beanvalidator/src/main/coffeescript/META-INF/modules/t5/beanvalidator/beanvalidator-validation.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajaxformloop.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/alert.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/autocomplete.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/bootstrap.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/confirm-click.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/datefield.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/events.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/exception-display.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/exception-frame.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/form-fragment.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/forms.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/init.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/localdate.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/messages.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/moment.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/pageinit.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/palette.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/select.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/time-interval.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/tree.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/utils.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/validation.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/zone-refresh.coffee /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/zone.coffee

- # validation.coffee

- ¶

  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/validation

  Support for Tapestry’s built-in set of translators and validators.

      define ["underscore", "./dom", "./events", "./utils", "./messages", "./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 `t5/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

          matches = (input, re) ->
            groups = input.match re

- ¶

  Unlike Java, there isn’t an easy way to match the entire string. This gets the job done.

            return false if groups is null

            groups[0] is input

          emailRE = new RegExp("[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\\.)+[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?")

          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.attr "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 =  (@attr "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 @attr "data-validate-min-length"

            if memo.translated.length < min
              memo.error = (@attr "data-min-length-message") or "TOO SHORT"
              return false

          dom.onDocument events.field.validate, "[data-validate-max-length]", (event, memo) ->
            max = parseInt @attr "data-validate-max-length"

            if memo.translated.length > max
              memo.error = (@attr "data-max-length-message") or "TOO LONG"
              return false

          dom.onDocument events.field.validate, "[data-validate-max]", (event, memo) ->
            max = parseInt @attr "data-validate-max"

            if memo.translated > max
              memo.error = (@attr "data-max-message") or "TOO LARGE"
              return false

          dom.onDocument events.field.validate, "[data-validate-min]", (event, memo) ->
            min = parseInt @attr "data-validate-min"

            if memo.translated < min
              memo.error = (@attr "data-min-message") or "TOO SMALL"
              return false

          dom.onDocument events.field.validate, "[data-validate-email]", (event, memo) ->

            unless (matches memo.translated, emailRE)
              memo.error = (@attr "data-email-message") or "INVALID EMAIL"
              return false

          dom.onDocument events.field.validate, "[data-validate-regexp]", (event, memo) ->

- ¶

  Cache the compiled regular expression.

            re = @meta REGEXP_META

            unless re
              re = new RegExp(@attr "data-validate-regexp")
              @meta REGEXP_META, re

            unless (matches memo.translated, re)
              memo.error = (@attr "data-regexp-message") or "INVALID"
              return false

          dom.onDocument events.field.validate, "[data-expected-status=checked]", (event, memo) ->

            unless memo.value
              memo.error =  (@attr "data-checked-message") or "MUST BE CHECKED"

          dom.onDocument events.field.validate, "[data-expected-status=unchecked]", (event, memo) ->

            if memo.value
              memo.error =  (@attr "data-checked-message") or "MUST NOT BE CHECKED"

- ¶

  Export the number parser, just to be nice (and to support some testing).

          return { parseNumber }