fix broken link

fix broken link

# Yang - evaluable schema expression

This module provides support for basic set of YANG schema modeling
language by using the built-in *extension* syntax to define additional
schema language constructs. The actual YANG language [RFC
6020](http://tools.ietf.org/html/rfc6020) specifications are loaded
inside the [main module](./main.coffee).

This module is the **primary interface** for consumers of this
library.

## Dependencies

    debug  = require('debug')('yang:schema') if process.env.DEBUG?
    fs     = require 'fs'
    path   = require 'path'
    parser = require 'yang-parser'
    indent = require 'indent-string'

    Expression = require './expression'

## Class Yang

    class Yang extends Expression
      @scope:
        extension: '0..n'
        typedef:   '0..n'
        module:    '0..n'
        submodule: '0..n'

## Class-level methods

      @clear: ->
        @module.splice(0,@module.length) if @module?
        @submodule.splice(0,@submodule.length) if @submodule?

### parse (schema)

This class-level routine performs recursive parsing of passed in
statement and sub-statements. It provides syntactic, semantic and
contextual validations on the provided schema and returns the final JS
object tree structure as hierarchical Yang expression instances.

If any validation errors are encountered, it will throw the
appropriate error along with the context information regarding the
error.

      @parse: (schema, opts={}) ->
        opts.compile ?= true
        try
          schema = parser.parse schema if typeof schema is 'string'
        catch e
          e.offset = 50 unless e.offset > 50
          offender = schema.slice e.offset-50, e.offset+50
          offender = offender.replace /\s\s+/g, ' '
          throw @error "invalid YANG syntax detected around: '#{offender}'", offender

        unless schema instanceof Object
          throw @error "must pass in valid YANG schema", schema

        kind = switch
          when !!schema.prf then "#{schema.prf}:#{schema.kw}"
          else schema.kw
        tag = schema.arg if !!schema.arg

        schema = (new this kind, tag).extends schema.substmts.map (x) => @parse x, false
        # perform final scoped constraint validation
        for kind, constraint of schema.scope when constraint in [ '1', '1..n' ]
          unless schema.hasOwnProperty kind
            throw schema.error "constraint violation for required '#{kind}' = #{constraint}"
        schema.compile() if opts.compile
        return schema

For comprehensive overview on currently supported YANG statements,
please refer to
[Compliance Report](../test/yang-compliance-coverage.md) for the latest
[RFC 6020](http://tools.ietf.org/html/rfc6020) YANG specification
compliance.

### compose (data [, opts={}])

This call *accepts* any arbitrary JS object and it will attempt to
convert it into a structural `Yang` expression instance. It will
analyze the passed in JS data and perform best match mapping to an
appropriate YANG schema representation to describe the input
data. This method will not be able to determine conditionals or any
meta-data to further constrain the data, but it should provide a good
starting point with the resulting `Yang` expression instance.

      @compose: (data, opts={}) ->
        unless data?
          throw @error "must supply input 'data' to compose"

        # explict compose
        if opts.kind?
          ext = Yang::lookup.call this, 'extension', opts.kind
          unless ext instanceof Expression
            throw @error "unable to find requested '#{opts.kind}' extension"
          return ext.compose? data, opts

        # implicit compose (dynamic discovery)
        for ext in @extension when ext.compose instanceof Function
          debug? "checking data if #{ext.tag}"
          res = ext.compose data, opts
          return res if res instanceof Yang

This facility is a powerful construct to dynamically generate `Yang`
schema from ordinary JS objects. For additional usage examples, please
refer to [Dynamic Composition](../TUTORIAL.md#dynamic-composition)
section in the [Getting Started Guide](../TUTORIAL.md).

### resolve (from..., name)

This call is internally used by [import](#import-name-opts) to
perform a search within the local filesystem to locate a given YANG
schema module by `name`. It will first check the calling code's local
[package.json](../package.json) to look for a `models: {}`
configuration section to identify where the target module can be
found. If there is an entry defined, it will then follow that
reference - which may be a JS file, YANG schema text file, or another
NPM module. If it is not found within the `models: {}` configuration
block or it fails to load the referenced dependency, it will then
fallback to attempt to locate a YANG schema text file in the same
folder that the `resolve` request was made: `#{name}.yang`.

      @resolve: (from..., name) ->
        return null unless typeof name is 'string'
        dir = from = switch
          when from.length then from[0]
          else path.resolve()
        while not found? and dir not in [ '/', '.' ]
          target = "#{dir}/package.json"
          debug? "[resolve] #{name} in #{target}"
          try
            pkginfo = require(target)
            found = pkginfo.models[name]
          if found?
            dir = path.dirname require.resolve(target)
            debug? "[resolve] #{name} check #{found} in #{dir}"
            unless !!path.extname found
              from = switch
                when found of pkginfo.dependencies
                  path.resolve dir, 'node_modules', found
                else path.resolve dir, found
              if fs.existsSync from
                return @resolve from, name
              else
                found = @resolve found, name
          if not found? and pkginfo?.name is name
            found = path.dirname require.resolve(target)
          dir = path.dirname dir unless found?
        file = switch
          when not found? then path.resolve from, "#{name}.yang"
          else path.resolve dir, found
        debug? "[resolve] checking if #{file} exists"
        return if fs.existsSync file then file else null

### import (name [, opts={}])

This call provides a convenience mechanism for dealing with YANG
schema module dependencies. It performs parsing of the YANG schema
content from the specified `name` and saves the generated `Yang`
expression inside the internal registry. The `name` can be a YANG
module name or a *filename* to the actual schema content (JS or YANG).

Once a given YANG module has been saved inside the registry,
subsequent [parse](#parse-schema) of YANG schema that *import* the
saved module will successfully resolve.

Typical usage scenario for this pattern is to internally define common
modules such as `ietf-yang-types` which can then be *imported* by
other schemas.

It will also return the new `Yang` expression instance (to do with as
you please).

      @require: ->
        console.warn "DEPRECATION: please use .import() instead"
        @import arguments...

      @import: (name, opts={}) ->
        return unless name?
        opts.basedir ?= ''
        extname  = path.extname name
        filename = path.resolve opts.basedir, name
        basedir  = path.dirname filename

        unless !!extname
          return (Yang::match.call this, 'module', name) ? @import (@resolve name), opts

        unless extname is '.yang'
          res = require filename
          unless res instanceof Yang
            throw @error "unable to import '#{name}' from '#{filename}' (not Yang expression)", res
          return res

        try return @use (@parse (fs.readFileSync filename, 'utf-8'), opts)
        catch e
          unless opts.compile and e.name is 'ExpressionError' and e.context.kind in [ 'include', 'import' ]
            console.error "unable to import '#{name}' YANG module from '#{filename}'"
            throw e
          opts.compile = false if e.context.kind is 'include'

          # try to find the dependency module for import
          dependency = @import (@resolve basedir, e.context.tag), opts
          unless dependency?
            e.message = "unable to auto-resolve '#{e.context.tag}' dependency module"
            throw e

          # retry the original request
          debug? "retrying import(#{name})"
          return @import arguments...

Please note that this method will look for the `name` in current
working directory of the script execution if the `name` is a relative
path. It utilizes the [resolve](#resolve-from-name) method and will
attempt to **recursively** resolve any failed `import` dependencies.

While this is a convenient abstraction, it is **recommended** to
directly use the Node.js built-in `require` mechanism (if
available). Using native `require` instead of `Yang.import` will
allow package bundlers such as `browserify` to capture the
dependencies as part of the produced bundle.  It also allows you to
directly load YANG schema files from other NPM modules.

By default, loading the [yang-js](./main.coffee) module will attempt
to associate `.yang` extension inside `require` facility. If
available, it will allow you to `require('./some-dependency.yang')`
and get back a parsed `Yang expression` instance.

## Main constructor

This method can be called directly without the use of `new` keyword
and will internally parse the provided schema and return a `bound
function` which will invoke [eval](#eval-data-opts) when called.

      constructor: (kind, tag, extension) ->
        unless this instanceof Yang
          [ schema, bindings ] = arguments
          schema = Yang.parse schema unless schema instanceof Yang
          return schema.bind bindings

        extension ?= (@lookup 'extension', kind)
        self = super kind, tag, extension
        unless extension instanceof Expression
          @debug "defer processing of custom extension #{kind}"
          @once 'compile:before', (->
            @debug "processing deferred extension #{kind}"
            extension = (@lookup 'extension', kind)
            unless extension instanceof Yang
              throw @error "encountered unknown extension '#{kind}'"
            { @source, @argument } = extension
          ).bind self
        return self

      @property 'datakey',
        get: -> switch
          when @parent instanceof Yang and @parent.kind is 'module' then "#{@parent.tag}:#{@tag}"
          when @parent instanceof Yang and @parent.kind is 'submodule'
            "#{@parent['belongs-to'].tag}:#{@tag}"
          else @tag ? @kind

      @property 'datapath',
        get: -> switch
          when @parent not instanceof Yang then ''
          when @node then @parent.datapath + "/#{@datakey}"
          else @parent.datapath

      error: (msg, context) -> super "[#{@trail}] #{msg}", context

      emit: (event, args...) ->
        @emitter.emit arguments...
        @root.emit event, this if event is 'change' and this isnt @root

## Instance-level methods

### bind (obj)

Every instance of `Yang` expression can be *bound* with control logic
which will be used during [eval](#eval-data-opts) to produce schema
infused **adaptive data object**. This routine is *inherited* from
[Class Expression](./expression.coffee).

This facility can be used to associate default behaviors for any
element in the configuration tree, as well as handler logic for
various YANG statements such as *rpc*, *feature*, etc.

This call will return the original `Yang` expression instance with the
new bindings registered within the `Yang` expression hierarchy.

      # bind() is inherited from Expression

Please refer to [Schema Binding](../TUTORIAL.md#schema-binding)
section of the [Getting Started Guide](../TUTORIAL.md) for usage
examples.

### eval (data, opts={})

Every instance of `Yang` expression can be [eval](#eval-data-opts)
with arbitrary JS data input which will apply the schema against the
provided data and return a schema infused **adaptive** data object.

This is an extremely useful construct which brings out the true power
of YANG for defining and governing arbitrary JS data structures.

Basically, the input `data` will be YANG schema validated and
converted to a schema infused *adaptive data model* that dynamically
defines properties according to the schema expressions.

It currently supports the `opts.adaptive` parameter (default `false`)
which establishes a persistent binding relationship with the governing
`Yang` expression instance. This allows the generated model to
dynamically **adapt** to any changes to the governing `Yang`
expression instance. Refer to below [extends](#extends-schema) section
for additional info on how the schema can be programmatically
modified.

      eval: (data, opts={}) ->
        if opts.adaptive is true
          # TODO: this will break for 'module' which will return Model?
          @once 'change', arguments.callee.bind(this, data, opts)
        super

Please refer to [Working with Models](../TUTORIAL.md#working-with-models)
section of the [Getting Started Guide](../TUTORIAL.md) for special
usage examples for `module` schemas.

### extends (schema...)

Every instance of `Yang` expression can be `extends` with additional
YANG schema string(s) and it will automatically perform
[parse](#parse-schema) of the provided schema text and update itself
accordingly.

This action also triggers an event emitter which will *retroactively*
adapt any previously [eval](#eval-data-opts) produced adaptive data
model instances to react accordingly to the newly changed underlying
schema expression(s).

      # extends() is inherited from Element

      merge: (elem) ->
        unless elem instanceof Yang
          throw @error "cannot merge invalid element into Yang", elem

        switch elem.kind
          when 'type'     then super elem, append: true
          when 'argument' then super elem, replace: true
          else super

Please refer to [Schema Extension](../TUTORIAL.md#schema-extension)
section of the [Getting Started Guide](../TUTORIAL.md) for usage
examples.

      normalizePath: (ypath) ->
        lastPrefix = null
        prefix2module = (root, prefix) ->
          return unless root.kind is 'module'
          switch
            when root.tag is prefix then prefix
            when root.prefix.tag is prefix then root.tag
            else
              for m in root.import ? [] when m.tag is prefix or m.prefix.tag is prefix
                return m.tag
              modules = root.lookup 'module'
              for m in modules when m.tag is prefix or m.prefix.tag is prefix
                return m.tag
              return prefix # return as-is...

        normalizeEntry = (x) =>
          return x unless x? and !!x
          match = x.match /^(?:([._-\w]+):)?([.{[<\w][.,+_\-}():>\]\w]*)$/
          unless match?
            throw @error "invalid path expression '#{x}' found in #{ypath}"
          [ prefix, target ] = [ match[1], match[2] ]
          return switch
            when not prefix? then target
            when prefix is lastPrefix then target
            else
              lastPrefix = prefix
              mname = prefix2module @root, prefix
              "#{mname}:#{target}"
        ypath = ypath.replace /\s/g, ''
        ypath
          .split('/')
          .map normalizeEntry
          .join('/')

### locate (ypath)

This is an internal helper facility used to locate a given schema node
within the `Yang` schema expression tree hierarchy. It supports a
limited version of XPATH-like expression to locate an explicit
element.

      locate: (ypath) ->
        # TODO: figure out how to eliminate duplicate code-block section
        # shared with Element
        return unless ypath?

        @debug "locate enter for '#{ypath}'"
        if typeof ypath is 'string'
          if (/^\//.test ypath) and this isnt @root
            return @root.locate ypath
          [ key, rest... ] = @normalizePath(ypath).split('/').filter (e) -> !!e
        else
          [ key, rest... ] = ypath
        return this unless key? and key isnt '.'

        if key is '..'
          return @parent?.locate rest

        match = key.match /^(?:([._-\w]+):)?([.{[<\w][.,+_\-}():>\]\w]*)$/
        [ prefix, target ] = [ match[1], match[2] ]
        if prefix? and this is @root
          search = [target].concat(rest)
          if (@tag is prefix) or (@lookup 'prefix', prefix)
            @debug "locate (local) '/#{prefix}:#{search.join('/')}'"
            return super search
          for m in @import ? [] when m.tag is prefix or m.prefix.tag is prefix
            @debug "locate (external) '/#{prefix}:#{search.join('/')}'"
            return m.module.locate search
          m = @lookup 'module', prefix
          return m?.locate search

        switch
          when /^{.+}$/.test(target)
            kind = 'grouping'
            tag  = target.replace /^{(.+)}$/, '$1'
          when /^\[.+\]$/.test(target)
            kind = 'feature'
            tag  = target.replace /^\[(.+)\]$/, '$1'
          when /^[^(]+\([^)]*\)$/.test(target)
            target = target.match /^([^(]+)\((.*)\)$/
            [ kind, tag ] = [ target[1], target[2] ]
            tag = undefined unless !!tag
          when /^\<.+\>$/.test(target)
            target = target.replace /^\<(.+)\>$/, '$1'
            [ kind..., tag ]  = target.split ':'
            [ tag, selector ] = tag.split '='
            kind = kind[0] if kind?.length
          else return super [key].concat rest

        match = @match kind, tag
        return switch
          when rest.length is 0 then match
          else match?.locate rest

### match (kind, tag)

This is an internal helper facility used by [locate](#locate-ypath) and
[lookup](./element.litcoffee#lookup-kind-tag) to test whether a given
entity exists in the local schema tree.

      # Yang Expression can support 'tag' with prefix to another module
      # (or itself).
      match: (kind, tag) ->
        return super unless kind? and tag? and typeof tag is 'string'
        res = super
        return res if res?

        [ prefix..., arg ] = tag.split ':'
        return unless prefix.length

        debug? "[match] with #{kind} #{tag}"

        prefix = prefix[0]
        debug? "[match] check if current module's prefix"
        if @root.tag is prefix or @root.prefix?.tag is prefix
          return @root.match kind, arg

        debug? "[match] checking if submodule's parent"
        ctx = @lookup 'belongs-to'
        if ctx?.prefix.tag is prefix
          return ctx.module.match kind, arg

        debug? "[match] check if one of current module's imports"
        imports = @root?.import ? []
        for m in imports when m.prefix.tag is prefix
          debug? m.module
          return m.module.match kind, arg

### toString (opts={})

The current `Yang` expression will covert back to the equivalent YANG
schema text format.

At first glance, this may not seem like a useful facility since YANG
schema text is *generally known* before [parse](#parse-schema) but it
becomes highly relevant when you consider a given `Yang` expression
programatically changing via [extends](#extends-schema).

Currently it supports `space` parameter which can be used to specify
number of spaces to use for indenting YANG statement blocks.  It
defaults to **2** but when set to **0**, the generated output will
omit newlines and other spacing for a more compact YANG output.

      toString: (opts={ space: 2 }) ->
        s = @kind
        if @source.argument?
          s += ' ' + switch @source.argument
            when 'value' then "'#{@tag}'"
            when 'text'
              "\n" + (indent '"'+@tag+'"', ' ', opts.space)
            else @tag
        sub =
          @elements
            .filter (x) => x.parent is this
            .map (x) -> x.toString opts
            .join "\n"
        if !!sub
          s += " {\n" + (indent sub, ' ', opts.space) + "\n}"
        else
          s += ';'
        return s

### toJSON

The current `Yang` expression will convert into a simple JS object
format.

      # toJSON() is inherited from Element

### valueOf

The current 'Yang' expression will convert into a primitive form for
comparision purposes.

      valueOf: ->
        switch @source.argument
          when 'value','text' then @tag.valueOf()
          else this

Please refer to [Schema Conversion](../TUTORIAL.md#schema-conversion)
section of the [Getting Started Guide](../TUTORIAL.md) for usage
examples.

## Export Yang Class

    module.exports = Yang

# Yang - evaluable schema expression

This module provides support for basic set of YANG schema modeling
language by using the built-in *extension* syntax to define additional
schema language constructs. The actual YANG language [RFC
6020](http://tools.ietf.org/html/rfc6020) specifications are loaded
inside the [main module](./main.coffee).

This module is the **primary interface** for consumers of this
library.

## Dependencies

    debug  = require('debug')('yang:schema') if process.env.DEBUG?
    fs     = require 'fs'
    path   = require 'path'
    parser = require 'yang-parser'
    indent = require 'indent-string'

    Expression = require './expression'

## Class Yang

    class Yang extends Expression
      @scope:
        extension: '0..n'
        typedef:   '0..n'
        module:    '0..n'
        submodule: '0..n'

## Class-level methods

      @clear: ->
        @module.splice(0,@module.length) if @module?
        @submodule.splice(0,@submodule.length) if @submodule?

### parse (schema)

This class-level routine performs recursive parsing of passed in
statement and sub-statements. It provides syntactic, semantic and
contextual validations on the provided schema and returns the final JS
object tree structure as hierarchical Yang expression instances.

If any validation errors are encountered, it will throw the
appropriate error along with the context information regarding the
error.

      @parse: (schema, opts={}) ->
        opts.compile ?= true
        try
          schema = parser.parse schema if typeof schema is 'string'
        catch e
          e.offset = 50 unless e.offset > 50
          offender = schema.slice e.offset-50, e.offset+50
          offender = offender.replace /\s\s+/g, ' '
          throw @error "invalid YANG syntax detected around: '#{offender}'", offender

        unless schema instanceof Object
          throw @error "must pass in valid YANG schema", schema

        kind = switch
          when !!schema.prf then "#{schema.prf}:#{schema.kw}"
          else schema.kw
        tag = schema.arg if !!schema.arg

        schema = (new this kind, tag).extends schema.substmts.map (x) => @parse x, false
        # perform final scoped constraint validation
        for kind, constraint of schema.scope when constraint in [ '1', '1..n' ]
          unless schema.hasOwnProperty kind
            throw schema.error "constraint violation for required '#{kind}' = #{constraint}"
        schema.compile() if opts.compile
        return schema

For comprehensive overview on currently supported YANG statements,
please refer to
[Compliance Report](../test/yang-compliance-coverage.md) for the latest
[RFC 6020](http://tools.ietf.org/html/rfc6020) YANG specification
compliance.

### compose (data [, opts={}])

This call *accepts* any arbitrary JS object and it will attempt to
convert it into a structural `Yang` expression instance. It will
analyze the passed in JS data and perform best match mapping to an
appropriate YANG schema representation to describe the input
data. This method will not be able to determine conditionals or any
meta-data to further constrain the data, but it should provide a good
starting point with the resulting `Yang` expression instance.

      @compose: (data, opts={}) ->
        unless data?
          throw @error "must supply input 'data' to compose"

        # explict compose
        if opts.kind?
          ext = Yang::lookup.call this, 'extension', opts.kind
          unless ext instanceof Expression
            throw @error "unable to find requested '#{opts.kind}' extension"
          return ext.compose? data, opts

        # implicit compose (dynamic discovery)
        for ext in @extension when ext.compose instanceof Function
          debug? "checking data if #{ext.tag}"
          res = ext.compose data, opts
          return res if res instanceof Yang

This facility is a powerful construct to dynamically generate `Yang`
schema from ordinary JS objects. For additional usage examples, please
refer to [Dynamic Composition](../TUTORIAL.md#dynamic-composition)
section in the [Getting Started Guide](../TUTORIAL.md).

### resolve (from..., name)

This call is internally used by [import](#import-name-opts) to
perform a search within the local filesystem to locate a given YANG
schema module by `name`. It will first check the calling code's local
[package.json](../package.json) to look for a `models: {}`
configuration section to identify where the target module can be
found. If there is an entry defined, it will then follow that
reference - which may be a JS file, YANG schema text file, or another
NPM module. If it is not found within the `models: {}` configuration
block or it fails to load the referenced dependency, it will then
fallback to attempt to locate a YANG schema text file in the same
folder that the `resolve` request was made: `#{name}.yang`.

      @resolve: (from..., name) ->
        return null unless typeof name is 'string'
        dir = from = switch
          when from.length then from[0]
          else path.resolve()
        while not found? and dir not in [ '/', '.' ]
          target = "#{dir}/package.json"
          debug? "[resolve] #{name} in #{target}"
          try
            pkginfo = require(target)
            found = pkginfo.models[name]
          if found?
            dir = path.dirname require.resolve(target)
            debug? "[resolve] #{name} check #{found} in #{dir}"
            unless !!path.extname found
              from = switch
                when found of pkginfo.dependencies
                  path.resolve dir, 'node_modules', found
                else path.resolve dir, found
              if fs.existsSync from
                return @resolve from, name
              else
                found = @resolve found, name
          if not found? and pkginfo?.name is name
            found = path.dirname require.resolve(target)
          dir = path.dirname dir unless found?
        file = switch
          when not found? then path.resolve from, "#{name}.yang"
          else path.resolve dir, found
        debug? "[resolve] checking if #{file} exists"
        return if fs.existsSync file then file else null

### import (name [, opts={}])

This call provides a convenience mechanism for dealing with YANG
schema module dependencies. It performs parsing of the YANG schema
content from the specified `name` and saves the generated `Yang`
expression inside the internal registry. The `name` can be a YANG
module name or a *filename* to the actual schema content (JS or YANG).

Once a given YANG module has been saved inside the registry,
subsequent [parse](#parse-schema) of YANG schema that *import* the
saved module will successfully resolve.

Typical usage scenario for this pattern is to internally define common
modules such as `ietf-yang-types` which can then be *imported* by
other schemas.

It will also return the new `Yang` expression instance (to do with as
you please).

      @require: ->
        console.warn "DEPRECATION: please use .import() instead"
        @import arguments...

      @import: (name, opts={}) ->
        return unless name?
        opts.basedir ?= ''
        extname  = path.extname name
        filename = path.resolve opts.basedir, name
        basedir  = path.dirname filename

        unless !!extname
          return (Yang::match.call this, 'module', name) ? @import (@resolve name), opts

        unless extname is '.yang'
          res = require filename
          unless res instanceof Yang
            throw @error "unable to import '#{name}' from '#{filename}' (not Yang expression)", res
          return res

        try return @use (@parse (fs.readFileSync filename, 'utf-8'), opts)
        catch e
          unless opts.compile and e.name is 'ExpressionError' and e.context.kind in [ 'include', 'import' ]
            console.error "unable to import '#{name}' YANG module from '#{filename}'"
            throw e
          opts.compile = false if e.context.kind is 'include'

          # try to find the dependency module for import
          dependency = @import (@resolve basedir, e.context.tag), opts
          unless dependency?
            e.message = "unable to auto-resolve '#{e.context.tag}' dependency module"
            throw e

          # retry the original request
          debug? "retrying import(#{name})"
          return @import arguments...

Please note that this method will look for the `name` in current
working directory of the script execution if the `name` is a relative
path. It utilizes the [resolve](#resolve-from-name) method and will
attempt to **recursively** resolve any failed `import` dependencies.

While this is a convenient abstraction, it is **recommended** to
directly use the Node.js built-in `require` mechanism (if
available). Using native `require` instead of `Yang.import` will
allow package bundlers such as `browserify` to capture the
dependencies as part of the produced bundle.  It also allows you to
directly load YANG schema files from other NPM modules.

By default, loading the [yang-js](./main.coffee) module will attempt
to associate `.yang` extension inside `require` facility. If
available, it will allow you to `require('./some-dependency.yang')`
and get back a parsed `Yang expression` instance.

## Main constructor

This method can be called directly without the use of `new` keyword
and will internally parse the provided schema and return a `bound
function` which will invoke [eval](#eval-data-opts) when called.

      constructor: (kind, tag, extension) ->
        unless this instanceof Yang
          [ schema, bindings ] = arguments
          schema = Yang.parse schema unless schema instanceof Yang
          return schema.bind bindings

        extension ?= (@lookup 'extension', kind)
        self = super kind, tag, extension
        unless extension instanceof Expression
          @debug "defer processing of custom extension #{kind}"
          @once 'compile:before', (->
            @debug "processing deferred extension #{kind}"
            extension = (@lookup 'extension', kind)
            unless extension instanceof Yang
              throw @error "encountered unknown extension '#{kind}'"
            { @source, @argument } = extension
          ).bind self
        return self

      @property 'datakey',
        get: -> switch
          when @parent instanceof Yang and @parent.kind is 'module' then "#{@parent.tag}:#{@tag}"
          when @parent instanceof Yang and @parent.kind is 'submodule'
            "#{@parent['belongs-to'].tag}:#{@tag}"
          else @tag ? @kind

      @property 'datapath',
        get: -> switch
          when @parent not instanceof Yang then ''
          when @node then @parent.datapath + "/#{@datakey}"
          else @parent.datapath

      error: (msg, context) -> super "[#{@trail}] #{msg}", context

      emit: (event, args...) ->
        @emitter.emit arguments...
        @root.emit event, this if event is 'change' and this isnt @root

## Instance-level methods

### bind (obj)

Every instance of `Yang` expression can be *bound* with control logic
which will be used during [eval](#eval-data-opts) to produce schema
infused **adaptive data object**. This routine is *inherited* from
[Class Expression](./core/expression.coffee).

This facility can be used to associate default behaviors for any
element in the configuration tree, as well as handler logic for
various YANG statements such as *rpc*, *feature*, etc.

This call will return the original `Yang` expression instance with the
new bindings registered within the `Yang` expression hierarchy.

      # bind() is inherited from Expression

Please refer to [Schema Binding](../TUTORIAL.md#schema-binding)
section of the [Getting Started Guide](../TUTORIAL.md) for usage
examples.

### eval (data, opts={})

Every instance of `Yang` expression can be [eval](#eval-data-opts)
with arbitrary JS data input which will apply the schema against the
provided data and return a schema infused **adaptive** data object.

This is an extremely useful construct which brings out the true power
of YANG for defining and governing arbitrary JS data structures.

Basically, the input `data` will be YANG schema validated and
converted to a schema infused *adaptive data model* that dynamically
defines properties according to the schema expressions.

It currently supports the `opts.adaptive` parameter (default `false`)
which establishes a persistent binding relationship with the governing
`Yang` expression instance. This allows the generated model to
dynamically **adapt** to any changes to the governing `Yang`
expression instance. Refer to below [extends](#extends-schema) section
for additional info on how the schema can be programmatically
modified.

      eval: (data, opts={}) ->
        if opts.adaptive is true
          # TODO: this will break for 'module' which will return Model?
          @once 'change', arguments.callee.bind(this, data, opts)
        super

Please refer to [Working with Models](../TUTORIAL.md#working-with-models)
section of the [Getting Started Guide](../TUTORIAL.md) for special
usage examples for `module` schemas.

### extends (schema...)

Every instance of `Yang` expression can be `extends` with additional
YANG schema string(s) and it will automatically perform
[parse](#parse-schema) of the provided schema text and update itself
accordingly.

This action also triggers an event emitter which will *retroactively*
adapt any previously [eval](#eval-data-opts) produced adaptive data
model instances to react accordingly to the newly changed underlying
schema expression(s).

      # extends() is inherited from Element

      merge: (elem) ->
        unless elem instanceof Yang
          throw @error "cannot merge invalid element into Yang", elem

        switch elem.kind
          when 'type'     then super elem, append: true
          when 'argument' then super elem, replace: true
          else super

Please refer to [Schema Extension](../TUTORIAL.md#schema-extension)
section of the [Getting Started Guide](../TUTORIAL.md) for usage
examples.

      normalizePath: (ypath) ->
        lastPrefix = null
        prefix2module = (root, prefix) ->
          return unless root.kind is 'module'
          switch
            when root.tag is prefix then prefix
            when root.prefix.tag is prefix then root.tag
            else
              for m in root.import ? [] when m.tag is prefix or m.prefix.tag is prefix
                return m.tag
              modules = root.lookup 'module'
              for m in modules when m.tag is prefix or m.prefix.tag is prefix
                return m.tag
              return prefix # return as-is...

        normalizeEntry = (x) =>
          return x unless x? and !!x
          match = x.match /^(?:([._-\w]+):)?([.{[<\w][.,+_\-}():>\]\w]*)$/
          unless match?
            throw @error "invalid path expression '#{x}' found in #{ypath}"
          [ prefix, target ] = [ match[1], match[2] ]
          return switch
            when not prefix? then target
            when prefix is lastPrefix then target
            else
              lastPrefix = prefix
              mname = prefix2module @root, prefix
              "#{mname}:#{target}"
        ypath = ypath.replace /\s/g, ''
        ypath
          .split('/')
          .map normalizeEntry
          .join('/')

### locate (ypath)

This is an internal helper facility used to locate a given schema node
within the `Yang` schema expression tree hierarchy. It supports a
limited version of XPATH-like expression to locate an explicit
element.

      locate: (ypath) ->
        # TODO: figure out how to eliminate duplicate code-block section
        # shared with Element
        return unless ypath?

        @debug "locate enter for '#{ypath}'"
        if typeof ypath is 'string'
          if (/^\//.test ypath) and this isnt @root
            return @root.locate ypath
          [ key, rest... ] = @normalizePath(ypath).split('/').filter (e) -> !!e
        else
          [ key, rest... ] = ypath
        return this unless key? and key isnt '.'

        if key is '..'
          return @parent?.locate rest

        match = key.match /^(?:([._-\w]+):)?([.{[<\w][.,+_\-}():>\]\w]*)$/
        [ prefix, target ] = [ match[1], match[2] ]
        if prefix? and this is @root
          search = [target].concat(rest)
          if (@tag is prefix) or (@lookup 'prefix', prefix)
            @debug "locate (local) '/#{prefix}:#{search.join('/')}'"
            return super search
          for m in @import ? [] when m.tag is prefix or m.prefix.tag is prefix
            @debug "locate (external) '/#{prefix}:#{search.join('/')}'"
            return m.module.locate search
          m = @lookup 'module', prefix
          return m?.locate search

        switch
          when /^{.+}$/.test(target)
            kind = 'grouping'
            tag  = target.replace /^{(.+)}$/, '$1'
          when /^\[.+\]$/.test(target)
            kind = 'feature'
            tag  = target.replace /^\[(.+)\]$/, '$1'
          when /^[^(]+\([^)]*\)$/.test(target)
            target = target.match /^([^(]+)\((.*)\)$/
            [ kind, tag ] = [ target[1], target[2] ]
            tag = undefined unless !!tag
          when /^\<.+\>$/.test(target)
            target = target.replace /^\<(.+)\>$/, '$1'
            [ kind..., tag ]  = target.split ':'
            [ tag, selector ] = tag.split '='
            kind = kind[0] if kind?.length
          else return super [key].concat rest

        match = @match kind, tag
        return switch
          when rest.length is 0 then match
          else match?.locate rest

### match (kind, tag)

This is an internal helper facility used by [locate](#locate-ypath) and
[lookup](./element.litcoffee#lookup-kind-tag) to test whether a given
entity exists in the local schema tree.

      # Yang Expression can support 'tag' with prefix to another module
      # (or itself).
      match: (kind, tag) ->
        return super unless kind? and tag? and typeof tag is 'string'
        res = super
        return res if res?

        [ prefix..., arg ] = tag.split ':'
        return unless prefix.length

        debug? "[match] with #{kind} #{tag}"

        prefix = prefix[0]
        debug? "[match] check if current module's prefix"
        if @root.tag is prefix or @root.prefix?.tag is prefix
          return @root.match kind, arg

        debug? "[match] checking if submodule's parent"
        ctx = @lookup 'belongs-to'
        if ctx?.prefix.tag is prefix
          return ctx.module.match kind, arg

        debug? "[match] check if one of current module's imports"
        imports = @root?.import ? []
        for m in imports when m.prefix.tag is prefix
          debug? m.module
          return m.module.match kind, arg

### toString (opts={})

The current `Yang` expression will covert back to the equivalent YANG
schema text format.

At first glance, this may not seem like a useful facility since YANG
schema text is *generally known* before [parse](#parse-schema) but it
becomes highly relevant when you consider a given `Yang` expression
programatically changing via [extends](#extends-schema).

Currently it supports `space` parameter which can be used to specify
number of spaces to use for indenting YANG statement blocks.  It
defaults to **2** but when set to **0**, the generated output will
omit newlines and other spacing for a more compact YANG output.

      toString: (opts={ space: 2 }) ->
        s = @kind
        if @source.argument?
          s += ' ' + switch @source.argument
            when 'value' then "'#{@tag}'"
            when 'text'
              "\n" + (indent '"'+@tag+'"', ' ', opts.space)
            else @tag
        sub =
          @elements
            .filter (x) => x.parent is this
            .map (x) -> x.toString opts
            .join "\n"
        if !!sub
          s += " {\n" + (indent sub, ' ', opts.space) + "\n}"
        else
          s += ';'
        return s

### toJSON

The current `Yang` expression will convert into a simple JS object
format.

      # toJSON() is inherited from Element

### valueOf

The current 'Yang' expression will convert into a primitive form for
comparision purposes.

      valueOf: ->
        switch @source.argument
          when 'value','text' then @tag.valueOf()
          else this

Please refer to [Schema Conversion](../TUTORIAL.md#schema-conversion)
section of the [Getting Started Guide](../TUTORIAL.md) for usage
examples.

## Export Yang Class

    module.exports = Yang


some code formatting fixes

some code formatting fixes

# Batman Helpers

Batman includes a number of useful, general purpose helper functions and references. They can all be found attached to the `Batman` object and can optionally be exported into the global namespace with a `$` prefix.

## container

`Batman.container` points to either the `window` object if running in the browser, or the `global` object if running in node. This is useful if you want to add something to the global scope in all environments.

## @typeOf(object) : string

`typeOf` determines a more specific type of an `object` than the native `typeof` operator in JavaScript. This is useful for a number of situations like dealing with `Object` promoted strings and numbers, or arrays which look like `object`s to `typeof`. Use `typeOf` when you need more than `"object"` from `typeof`.

_Note_: `typeOf` is substantially slower than `typeof`. `typeOf` works in a somewhat hackish manner by getting the `Object::toString` representation of the object and slicing it to retrieve the name of the constructor.

    test 'typeOf returns "String" for both strings and Object strings', ->
      primitive = "test"
      objectified = new String("test")
      equal typeof primitive, "string"
      equal typeof objectified, "object"
      equal Batman.typeOf(primitive), "String"
      equal Batman.typeOf(objectified), "String"

    test 'typeOf returns Array for arrays', ->
      array = [];
      equal typeof array, "object"
      equal Batman.typeOf(array), "Array"

## @mixin(subject, objects...) : subject

`mixin`, occasionally known elsewhere as `extend` or `merge`, flattens a series of objects onto the subject. Key/value pairs on objects passed as later arguments (arguments with a higher index) take precedence over earlier arguments. Returns the `subject` passed in with the new values.

`mixin` also has special properties that make it different than the canonical `extend` functions:

 1. If the `subject` has a `set` function, `subject.set(key, value)` will be used to apply keys instead of `subject[key] = value`. This means that if the subject is a `Batman.Object`, observers and thus bindings on the object will be notified when other (Batmanified or not) objects are mixed into it.
 2. If a mixed-in `object` has an `initialize` function defined, that function will be called and passed the `subject`. This is useful for custom extension logic, similar to `self.included` in Ruby. For this reason, the keys `initialize` and `uninitialize` are skipped by `mixin`.
 3. `mixin` only iterates over keys for which the `hasOwnProperty` test passes.

_Note_: `mixin` is destructive to (only) the first argument. If you need a non-destructive version of `mixin`, just pass an empty object as the first object, and all keys from the successive arguments will be applied to the empty object.

    test 'mixin merges argument objects', ->
      subject = {}
      deepEqual Batman.mixin(subject, {fit: true}, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject"
      deepEqual subject, {fit: true, fly: true, funky: true}, "the subject is modified destructively"

    test 'mixin merges argument objects', ->
      unmodified = {fit: true}
      deepEqual Batman.mixin({}, unmodified, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject"
      deepEqual unmodified, {fit: true}, "argument objects are untouched allowing non-destructive merge"

    test 'mixed in objects passed as higher indexed arguments take precedence', ->
      subject = {}
      deepEqual Batman.mixin(subject, {x: 1, y: 1}, {x: 2}), {x: 2, y: 1}

## @unmixin(subject, objects...) : subject

`unmixin` "unmerges" the passed objects from the `subject`. If a key exists on any of the `objects` it will be `delete`d from the `subject`. Returns the `subject`.

`unmixin`, similar to `mixin`, supports calling an `uninitialize` function for each of the `objects` being unmixed in. If an `uninitialize` function exists on each

    test 'unmixin removes keys found on the unmixined objects on the subject', ->
      subject = {fit: true, fly: true, funky: true}
      deepEqual Batman.unmixin(subject, {fit: true}, {fly: true}), {funky: true}, "unmixin returns the subject"
      deepEqual subject, {funky: true}, "the subject is destructively modified."

## @functionName(function) : string

`functionName` returns the name of a given function, if any. Works with Internet Explorer 7/8/9, FireFox, Chrome, and Safari.

    test 'functionName returns the name of a given function', ->
      equal Batman.functionName("".toString), 'toString'

## @isChildOf(parent : HTMLElement, child : HTMLElement) : boolean

`isChildOf` is a simple DOM helper which returns a boolean describing if the passed `child` node can be found in the descendants of the passed `parent` node.

## @setImmediate(callback : Function) : object

`setImmediate` (and its sister `clearImmediate`) are a more efficient version of `setTimeout(callback, 0)`. Due to timer resolution issues, setTimeout passed a timeout of 0 doesn't actually execute the function as soon as the JS execution stack has been emptied, but at minimum 4ms and maxmium 25ms after. For this reason Batman provides a cross browser implementation of `setImmediate` which does its best to call the callback immediately after the stack empties. Batman's `setImmediate` polyfill uses the native version if available, `window.postmessage` trickery if supported, and falls back on `setTimeout(->, 0)`.

`setImmediate` returns a handle which can be passed to `clearImmediate` to cancel the future calling of the callback.

## @clearImmediate(handle)

`clearImmediate` stops the calling of a callback in the future when passed its `handle` (which is returned from the `setImmediate` call used to enqueue it).

## @forEach(iterable : object, iterator : Function[, context : Object])

The `forEach` Batman helper is a universal iteration helper. When passed an `iterable` object, the helper will call the `iterator` (optionally in the `context`) for each item in the `iterable`. The `iterable` can be:

 1. something which has its own `forEach`, in which case the `iterator` will just be passed to `iterable.forEach`.
 2. an array like object, in which case a JavaScript `for(;;)` loop will be used to iterate over each entry
 3. or an object, in which case a JavaScript `for-in` loop will be used to iterate over each entry.

The `forEach` helper is useful for iterating over objects when the type of those objects isn't guaranteed.

    test 'forEach iterates over objects with forEach defined', ->
      results = []
      set = new Batman.SimpleSet('a')
      Batman.forEach(set, (x) -> results.push(x))
      deepEqual results, ['a']

    test 'forEach iterates over array like objects', ->
      results = []
      ArrayLike = ->
      ArrayLike:: = []
      imitation = new ArrayLike
      Array::push.call(imitation, "a")
      Array::push.call(imitation, "b")
      Batman.forEach(imitation, (x) -> results.push(x))
      deepEqual results, ['a', 'b']

    test 'forEach iterates over objects', ->
      result = {}
      object = {x: true}
      Batman.forEach(object, (key, val) -> result[key] = val)
      deepEqual result, object

## @objectHasKey(object, key) : boolean

`objectHasKey` returns a boolean describing the presence of the `key` in the passed `object`. `objectHasKey` delegates to the `object`'s `hasKey` function if present, and otherwise just does a check using the JavaScript `in` operator.

    test 'objectHasKey verifies if a key is present in an object', ->
      subject = {fit: true}
      ok Batman.objectHasKey(subject, 'fit')
      equal Batman.objectHasKey(subject, 'flirty'), false

    test 'objectHasKey verifies if a key is present in an object with `hasKey` defined', ->
      subject = new Batman.SimpleHash {fit: true}
      ok Batman.objectHasKey(subject, 'fit')
      equal Batman.objectHasKey(subject, 'flirty'), false

## @contains(object, item) : boolean

`contains` returns a boolean describing if the given `object` has member `item`. Membership in this context is defined as:

 + the result of `object.has(item)` if the `object` has a `has` function defined
 + the result of `item in object` if the `object` is arraylike
 + the result of the Batman.objectHasKey otherwise

_Note_: When passed an object without a `has` function, `contains` will return `true` if the `object` has `item` as a *`key`*, not as a value at any key.

`contains` is useful for checking item membership when the type of the object can't be relied on.

## @get(object, key) : value

`get` is a general purpose function for retrieving the value from a `key` on an `object` of an indeterminate type. This is useful if code needs to work with both `Batman.Object`s and Plain Old JavaScript Objects. `get` has the following semantics:

 + if the `object` has a `get` function defined, return the result of `object.get(key)`
 + if the object does not have a `get` function defined, use an ephemeral `Batman.Property` to retrieve the key. This is equivalent to `object[key]` for single segment `key`s, but if the `key` is multi-segment (example: 'product.customer.name'), `get` will do nested gets until the either `undefined` or the end of the keypath is reached.

    test 'get returns the value at a key on a POJO', ->
      subject = {fit: true}
      equal Batman.get(subject, 'fit'), true
      equal Batman.get(subject, 'flirty'), undefined

    test 'get returns the value at a key on a Batman.Object', ->
      subject = Batman {fit: true}
      equal Batman.get(subject, 'fit'), true
      equal Batman.get(subject, 'flirty'), undefined

    test 'get returns the value at a deep key on a POJO', ->
      subject = {customer: {name: "Joe"}}
      equal Batman.get(subject, 'customer.name'), "Joe"
      equal Batman.get(subject, 'customer.age'), undefined

    test 'get returns the value at a deep key on a Batman.Object', ->
      subject = Batman {customer: {name: "Joe"}}
      equal Batman.get(subject, 'customer.name'), "Joe"
      equal Batman.get(subject, 'customer.age'), undefined

## @getPath(base, segments) : string

`getPath` returns the hash value denoted by the specified path, which consists of an array of nested hash keys. See examples below for more detail.

    test "takes a base and an array of keys and returns the corresponding nested value", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      equal Batman.getPath(@complexObject, ['hash', 'foo', 'bar']), 'nested value'
      equal Batman.getPath(@complexObject, ['hash', 'foo.bar']), 'flat value'
      strictEqual Batman.getPath(@complexObject, ['hash', 'not-foo', 'bar']), undefined

    test "returns just the base if the key array is empty", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(@complexObject, []), @complexObject
      strictEqual Batman.getPath(null, []), null

    test "returns undefined if the base is null-ish", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(null, ['foo']), undefined
      strictEqual Batman.getPath(undefined, ['foo']), undefined

    test "returns falsy values", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(num: 0, ['num']), 0
      strictEqual Batman.getPath(thing: null, ['thing']), null

## @escapeHTML(string) : string

`escapeHTML` takes a string of unknown origin and makes it safe for display on a web page by encoding control characters in HTML into their HTML entities.

*Warning*: Do not rely on `escapeHTML` to purge unsafe data from user submitted content. While `escapeHTML` is applied to every binding's contents by default, it should not be your only line of defence against script injection attacks.

    test 'escapeHTML encodes special characters into HTML entities', ->
      equal Batman.escapeHTML("& < > \" '"), "& < > " '"

# Batman.helpers

`Batman.helpers` is a namespace for Batman's helpful string manipulation helpers.

_Note_: Batman's pluralization functions mirror those of Rails' exactly.

## @ordinalize(value : [number|string]) : string

`ordinalize` converts a given integer into an ordinal string for describing position in a list, like 1st, 2nd, or 20th.

    test 'ordinalize converts numbers to their ordinal form', ->
      equal Batman.helpers.ordinalize(1), "1st"
      equal Batman.helpers.ordinalize("2"), "2nd"
      equal Batman.helpers.ordinalize(1002), "1002nd"
      equal Batman.helpers.ordinalize("1003"), "1003rd"
      equal Batman.helpers.ordinalize(-11), "-11th"
      equal Batman.helpers.ordinalize(-1021), "-1021st"

## @singularize(pluralString : string) : string

`singularize` converts the plural form of a word to a singular form.

    test 'singularize converts plural words to singular words', ->
      equal Batman.helpers.singularize("posts"), "post"
      equal Batman.helpers.singularize("octopi"), "octopus"
      equal Batman.helpers.singularize("sheep"), "sheep"
      equal Batman.helpers.singularize("word"), "word"
      equal Batman.helpers.singularize("CamelOctopi"), "CamelOctopus"

## @pluralize(singularString : string) : string

`pluralize` converts the singular form of a word to the plural form.

    test 'pluralize converts plural words to singular words', ->
      equal Batman.helpers.pluralize("post"), "posts"
      equal Batman.helpers.pluralize("octopus"), "octopi"
      equal Batman.helpers.pluralize("sheep"), "sheep"
      equal Batman.helpers.pluralize("words"), "words"
      equal Batman.helpers.pluralize("CamelOctopus"), "CamelOctopi"

## @camelize(string [, lowercaseFirstLetter = false]) : string

`camelize` converts the passed `string` to UpperCamelCase. If the second argument is passed as `true`, then lowerCamelCase is returned.

    test 'camelize returns the CamelCase version of an under_scored word', ->
      equal Batman.helpers.camelize("batman_object"), "BatmanObject"
      equal Batman.helpers.camelize("batman_object", true), "batmanObject"

## @underscore(string) : string

`underscore` returns the underscored version of a CamelCase word.

    test 'underscore converts CamelCase to under_scores', ->
      equal Batman.helpers.underscore("BatmanObject"), "batman_object"

## @capitalize(string) : string

`capitalize` does a word-wise capitalization of a phrase or word.

    test 'capitalize makes the first letter of each word in the string uppercase', ->
      equal Batman.helpers.capitalize("batmanObject"), "BatmanObject"
      equal Batman.helpers.capitalize("batman object"), "Batman Object"
      equal Batman.helpers.capitalize("AlreadyCapitalized"), "AlreadyCapitalized"

## @trim(string) : string

`trim` trims a string getting rid of extra white space around the string or returning an empty string if it is null.

    test 'trim gets rid of space around a string', ->
      equal Batman.helpers.trim("TrimRight "), "TrimRight"
      equal Batman.helpers.trim(" TrimLeft"), "TrimLeft"
      equal Batman.helpers.trim(" TrimBothSides "), "TrimBothSides"
      equal Batman.helpers.trim("AlreadyTrimmed"), "AlreadyTrimmed"

    test 'trim turns a null or undefined string into an empty string', ->
      equal Batman.helpers.trim(null), ""
      equal Batman.helpers.trim(undefined), ""

## @interpolate(stringOrObject, keys) : string

`interpolate` interpolates a string, filling it in with values matching interpolation keys, similar to printf variants.

    test 'interpolate fills in key values globally in a string', ->
      equal Batman.helpers.interpolate("%{field} must be at least %{count} characters in order for %{field} to be valid", count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

    test 'interpolate fills in key values globally in an object-embedded string', ->
      equal Batman.helpers.interpolate({'other': "%{field} must be at least %{count} characters in order for %{field} to be valid"}, count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

    test 'interpolate fills in key values globally in an object-embedded string, embedded by key count', ->
      equal Batman.helpers.interpolate({3: "%{field} must be at least %{count} characters in order for %{field} to be valid"}, count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

## @humanize(string) : string

`humanize` reformats a string that has a programmatic meaning (camelcased, underscored, "_id" suffixed) to make human-readable by separating concatenated words and/or getting rid of the "_id" suffix.

    test 'humanize replaces underscores and dashes with empty space', ->
      equal Batman.helpers.humanize('underscored_string'), 'Underscored string'
      equal Batman.helpers.humanize('dash-separated-string'), 'Dash separated string'

    test 'humanize splits camel cased words by empty space', ->
      equal Batman.helpers.humanize('CamelCasedString'), 'Camel cased string'

    test 'humanize title-cases the first word', ->
      equal Batman.helpers.humanize('lower case sentence'), 'Lower case sentence'

    test 'humanize lower-cases all words after the first one', ->
      equal Batman.helpers.humanize('Mixed Case SENTENCE'), 'Mixed case sentence'

    test 'humanize gets rid of _id suffix', ->
      equal Batman.helpers.humanize('identifying_string_id'), 'Identifying string'

# Batman Helpers

Batman includes a number of useful, general purpose helper functions and references. They can all be found attached to the `Batman` object and can optionally be exported into the global namespace with a `$` prefix.

## container

`Batman.container` points to either the `window` object if running in the browser, or the `global` object if running in node. This is useful if you want to add something to the global scope in all environments.

## @typeOf(object) : string

`typeOf` determines a more specific type of an `object` than the native `typeof` operator in JavaScript. This is useful for a number of situations like dealing with `Object` promoted strings and numbers, or arrays which look like `object`s to `typeof`. Use `typeOf` when you need more than `"object"` from `typeof`.

_Note_: `typeOf` is substantially slower than `typeof`. `typeOf` works in a somewhat hackish manner by getting the `Object::toString` representation of the object and slicing it to retrieve the name of the constructor.

    test 'typeOf returns "String" for both strings and Object strings', ->
      primitive = "test"
      objectified = new String("test")
      equal typeof primitive, "string"
      equal typeof objectified, "object"
      equal Batman.typeOf(primitive), "String"
      equal Batman.typeOf(objectified), "String"

    test 'typeOf returns Array for arrays', ->
      array = [];
      equal typeof array, "object"
      equal Batman.typeOf(array), "Array"

## @mixin(subject, objects...) : subject

`mixin`, occasionally known elsewhere as `extend` or `merge`, flattens a series of objects onto the subject. Key/value pairs on objects passed as later arguments (arguments with a higher index) take precedence over earlier arguments. Returns the `subject` passed in with the new values.

`mixin` also has special properties that make it different than the canonical `extend` functions:

 1. If the `subject` has a `set` function, `subject.set(key, value)` will be used to apply keys instead of `subject[key] = value`. This means that if the subject is a `Batman.Object`, observers and thus bindings on the object will be notified when other (Batmanified or not) objects are mixed into it.
 2. If a mixed-in `object` has an `initialize` function defined, that function will be called and passed the `subject`. This is useful for custom extension logic, similar to `self.included` in Ruby. For this reason, the keys `initialize` and `uninitialize` are skipped by `mixin`.
 3. `mixin` only iterates over keys for which the `hasOwnProperty` test passes.

_Note_: `mixin` is destructive to (only) the first argument. If you need a non-destructive version of `mixin`, just pass an empty object as the first object, and all keys from the successive arguments will be applied to the empty object.

    test 'mixin merges argument objects', ->
      subject = {}
      deepEqual Batman.mixin(subject, {fit: true}, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject"
      deepEqual subject, {fit: true, fly: true, funky: true}, "the subject is modified destructively"

    test 'mixin merges argument objects', ->
      unmodified = {fit: true}
      deepEqual Batman.mixin({}, unmodified, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject"
      deepEqual unmodified, {fit: true}, "argument objects are untouched allowing non-destructive merge"

    test 'mixed in objects passed as higher indexed arguments take precedence', ->
      subject = {}
      deepEqual Batman.mixin(subject, {x: 1, y: 1}, {x: 2}), {x: 2, y: 1}

## @unmixin(subject, objects...) : subject

`unmixin` "unmerges" the passed objects from the `subject`. If a key exists on any of the `objects` it will be `delete`d from the `subject`. Returns the `subject`.

`unmixin`, similar to `mixin`, supports calling an `uninitialize` function for each of the `objects` being unmixed in. If an `uninitialize` function exists on each

    test 'unmixin removes keys found on the unmixined objects on the subject', ->
      subject = {fit: true, fly: true, funky: true}
      deepEqual Batman.unmixin(subject, {fit: true}, {fly: true}), {funky: true}, "unmixin returns the subject"
      deepEqual subject, {funky: true}, "the subject is destructively modified."

## @functionName(function) : string

`functionName` returns the name of a given function, if any. Works with Internet Explorer 7/8/9, FireFox, Chrome, and Safari.

    test 'functionName returns the name of a given function', ->
      equal Batman.functionName("".toString), 'toString'

## @isChildOf(parent : HTMLElement, child : HTMLElement) : boolean

`isChildOf` is a simple DOM helper which returns a boolean describing if the passed `child` node can be found in the descendants of the passed `parent` node.

## @setImmediate(callback : Function) : object

`setImmediate` (and its sister `clearImmediate`) are a more efficient version of `setTimeout(callback, 0)`. Due to timer resolution issues, setTimeout passed a timeout of 0 doesn't actually execute the function as soon as the JS execution stack has been emptied, but at minimum 4ms and maxmium 25ms after. For this reason Batman provides a cross browser implementation of `setImmediate` which does its best to call the callback immediately after the stack empties. Batman's `setImmediate` polyfill uses the native version if available, `window.postmessage` trickery if supported, and falls back on `setTimeout(->, 0)`.

`setImmediate` returns a handle which can be passed to `clearImmediate` to cancel the future calling of the callback.

## @clearImmediate(handle)

`clearImmediate` stops the calling of a callback in the future when passed its `handle` (which is returned from the `setImmediate` call used to enqueue it).

## @forEach(iterable : object, iterator : Function[, context : Object])

The `forEach` Batman helper is a universal iteration helper. When passed an `iterable` object, the helper will call the `iterator` (optionally in the `context`) for each item in the `iterable`. The `iterable` can be:

 1. something which has its own `forEach`, in which case the `iterator` will just be passed to `iterable.forEach`.
 2. an array like object, in which case a JavaScript `for(;;)` loop will be used to iterate over each entry
 3. or an object, in which case a JavaScript `for-in` loop will be used to iterate over each entry.

The `forEach` helper is useful for iterating over objects when the type of those objects isn't guaranteed.

    test 'forEach iterates over objects with forEach defined', ->
      results = []
      set = new Batman.SimpleSet('a')
      Batman.forEach(set, (x) -> results.push(x))
      deepEqual results, ['a']

    test 'forEach iterates over array like objects', ->
      results = []
      ArrayLike = ->
      ArrayLike:: = []
      imitation = new ArrayLike
      Array::push.call(imitation, "a")
      Array::push.call(imitation, "b")
      Batman.forEach(imitation, (x) -> results.push(x))
      deepEqual results, ['a', 'b']

    test 'forEach iterates over objects', ->
      result = {}
      object = {x: true}
      Batman.forEach(object, (key, val) -> result[key] = val)
      deepEqual result, object

## @objectHasKey(object, key) : boolean

`objectHasKey` returns a boolean describing the presence of the `key` in the passed `object`. `objectHasKey` delegates to the `object`'s `hasKey` function if present, and otherwise just does a check using the JavaScript `in` operator.

    test 'objectHasKey verifies if a key is present in an object', ->
      subject = {fit: true}
      ok Batman.objectHasKey(subject, 'fit')
      equal Batman.objectHasKey(subject, 'flirty'), false

    test 'objectHasKey verifies if a key is present in an object with `hasKey` defined', ->
      subject = new Batman.SimpleHash {fit: true}
      ok Batman.objectHasKey(subject, 'fit')
      equal Batman.objectHasKey(subject, 'flirty'), false

## @contains(object, item) : boolean

`contains` returns a boolean describing if the given `object` has member `item`. Membership in this context is defined as:

 + the result of `object.has(item)` if the `object` has a `has` function defined
 + the result of `item in object` if the `object` is arraylike
 + the result of the Batman.objectHasKey otherwise

_Note_: When passed an object without a `has` function, `contains` will return `true` if the `object` has `item` as a *`key`*, not as a value at any key.

`contains` is useful for checking item membership when the type of the object can't be relied on.

## @get(object, key) : value

`get` is a general purpose function for retrieving the value from a `key` on an `object` of an indeterminate type. This is useful if code needs to work with both `Batman.Object`s and Plain Old JavaScript Objects. `get` has the following semantics:

 + if the `object` has a `get` function defined, return the result of `object.get(key)`
 + if the object does not have a `get` function defined, use an ephemeral `Batman.Property` to retrieve the key. This is equivalent to `object[key]` for single segment `key`s, but if the `key` is multi-segment (example: 'product.customer.name'), `get` will do nested gets until the either `undefined` or the end of the keypath is reached.

    test 'get returns the value at a key on a POJO', ->
      subject = {fit: true}
      equal Batman.get(subject, 'fit'), true
      equal Batman.get(subject, 'flirty'), undefined

    test 'get returns the value at a key on a Batman.Object', ->
      subject = Batman {fit: true}
      equal Batman.get(subject, 'fit'), true
      equal Batman.get(subject, 'flirty'), undefined

    test 'get returns the value at a deep key on a POJO', ->
      subject = {customer: {name: "Joe"}}
      equal Batman.get(subject, 'customer.name'), "Joe"
      equal Batman.get(subject, 'customer.age'), undefined

    test 'get returns the value at a deep key on a Batman.Object', ->
      subject = Batman {customer: {name: "Joe"}}
      equal Batman.get(subject, 'customer.name'), "Joe"
      equal Batman.get(subject, 'customer.age'), undefined

## @getPath(base, segments) : string

`getPath` returns the hash value denoted by the specified path, which consists of an array of nested hash keys. See examples below for more detail.

    test "takes a base and an array of keys and returns the corresponding nested value", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      equal Batman.getPath(@complexObject, ['hash', 'foo', 'bar']), 'nested value'
      equal Batman.getPath(@complexObject, ['hash', 'foo.bar']), 'flat value'
      strictEqual Batman.getPath(@complexObject, ['hash', 'not-foo', 'bar']), undefined

    test "returns just the base if the key array is empty", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(@complexObject, []), @complexObject
      strictEqual Batman.getPath(null, []), null

    test "returns undefined if the base is null-ish", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(null, ['foo']), undefined
      strictEqual Batman.getPath(undefined, ['foo']), undefined

    test "returns falsy values", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(num: 0, ['num']), 0
      strictEqual Batman.getPath(thing: null, ['thing']), null

## @escapeHTML(string) : string

`escapeHTML` takes a string of unknown origin and makes it safe for display on a web page by encoding control characters in HTML into their HTML entities.

*Warning*: Do not rely on `escapeHTML` to purge unsafe data from user submitted content. While `escapeHTML` is applied to every binding's contents by default, it should not be your only line of defence against script injection attacks.

    test 'escapeHTML encodes special characters into HTML entities', ->
      equal Batman.escapeHTML("& < > \" '"), "& < > " '"

# Batman.helpers

`Batman.helpers` is a namespace for Batman's helpful string manipulation helpers.

_Note_: Batman's pluralization functions mirror those of Rails' exactly.

## @ordinalize(value : [number|string]) : string

`ordinalize` converts a given integer into an ordinal string for describing position in a list, like 1st, 2nd, or 20th.

    test 'ordinalize converts numbers to their ordinal form', ->
      equal Batman.helpers.ordinalize(1)     , "1st"
      equal Batman.helpers.ordinalize("2")     , "2nd"
      equal Batman.helpers.ordinalize(1002)  , "1002nd"
      equal Batman.helpers.ordinalize("1003")  , "1003rd"
      equal Batman.helpers.ordinalize(-11)   , "-11th"
      equal Batman.helpers.ordinalize(-1021) , "-1021st"

## @singularize(pluralString : string) : string

`singularize` converts the plural form of a word to a singular form.

    test 'singularize converts plural words to singular words', ->
      equal Batman.helpers.singularize("posts"), "post"
      equal Batman.helpers.singularize("octopi"), "octopus"
      equal Batman.helpers.singularize("sheep"), "sheep"
      equal Batman.helpers.singularize("word"), "word"
      equal Batman.helpers.singularize("CamelOctopi"), "CamelOctopus"

## @pluralize(singularString : string) : string

`pluralize` converts the singular form of a word to the plural form.

    test 'pluralize converts plural words to singular words', ->
      equal Batman.helpers.pluralize("post"), "posts"
      equal Batman.helpers.pluralize("octopus"), "octopi"
      equal Batman.helpers.pluralize("sheep"), "sheep"
      equal Batman.helpers.pluralize("words"), "words"
      equal Batman.helpers.pluralize("CamelOctopus"), "CamelOctopi"

## @camelize(string, [lowercaseFirstLetter = false]) : string

`camelize` converts the passed `string` to UpperCamelCase. If the second argument is passed as `true`, then lowerCamelCase is returned.

    test 'camelize returns the CamelCase version of an under_scored word', ->
      equal Batman.helpers.camelize("batman_object"), "BatmanObject"
      equal Batman.helpers.camelize("batman_object", true), "batmanObject"

## @underscore(string) : string

`underscore` returns the underscored version of a CamelCase word.

    test 'underscore converts CamelCase to under_scores', ->
      equal Batman.helpers.underscore("BatmanObject"), "batman_object"

## @capitalize(string) : string

`capitalize` does a word-wise capitalization of a phrase or word.

    test 'capitalize makes the first letter of each word in the string uppercase', ->
      equal Batman.helpers.capitalize("batmanObject"), "BatmanObject"
      equal Batman.helpers.capitalize("batman object"), "Batman Object"
      equal Batman.helpers.capitalize("AlreadyCapitalized"), "AlreadyCapitalized"

## @trim(string) : string

`trim` trims a string getting rid of extra white space around the string or returning an empty string if it is null.

    test 'trim gets rid of space around a string', ->
      equal Batman.helpers.trim("TrimRight "), "TrimRight"
      equal Batman.helpers.trim(" TrimLeft"), "TrimLeft"
      equal Batman.helpers.trim(" TrimBothSides "), "TrimBothSides"
      equal Batman.helpers.trim("AlreadyTrimmed"), "AlreadyTrimmed"

    test 'trim turns a null or undefined string into an empty string', ->
      equal Batman.helpers.trim(null), ""
      equal Batman.helpers.trim(undefined), ""

## @interpolate(stringOrObject, keys) : string

`interpolate` interpolates a string, filling it in with values matching interpolation keys, similar to printf variants.

    test 'interpolate fills in key values globally in a string', ->
      equal Batman.helpers.interpolate("%{field} must be at least %{count} characters in order for %{field} to be valid", count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

    test 'interpolate fills in key values globally in an object-embedded string', ->
      equal Batman.helpers.interpolate({'other': "%{field} must be at least %{count} characters in order for %{field} to be valid"}, count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

    test 'interpolate fills in key values globally in an object-embedded string, embedded by key count', ->
      equal Batman.helpers.interpolate({3: "%{field} must be at least %{count} characters in order for %{field} to be valid"}, count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

## @humanize(string) : string

`humanize` reformats a string that has a programmatic meaning (camelcased, underscored, "_id" suffixed) to make human-readable by separating concatenated words and/or getting rid of the "_id" suffix.

    test 'humanize replaces underscores and dashes with empty space', ->
      equal Batman.helpers.humanize('underscored_string'), 'Underscored string'
      equal Batman.helpers.humanize('dash-separated-string'), 'Dash separated string'

    test 'humanize splits camel cased words by empty space', ->
      equal Batman.helpers.humanize('CamelCasedString'), 'Camel cased string'

    test 'humanize title-cases the first word', ->
      equal Batman.helpers.humanize('lower case sentence'), 'Lower case sentence'

    test 'humanize lower-cases all words after the first one', ->
      equal Batman.helpers.humanize('Mixed Case SENTENCE'), 'Mixed case sentence'

    test 'humanize gets rid of _id suffix', ->
      equal Batman.helpers.humanize('identifying_string_id'), 'Identifying string'


clean up requests markdown

clean up requests markdown

# Batman.Request
 Request is an abstraction around XHR requests.  It can be used with a variety of platforms, and is implemented in the platform layer files.

### Platform Request Implmentation Libraries:
  Depending on the platform library the underlying request implementation will change:

 - `batman.jquery.js`: jQuery XHR
 - `batman.solo.js`: Reqwest.js

 _note_ For the tests there are some mocked responses, in a normal non testing situation you would not use these

### How to use Batman Request

Batman.Request can be used a variety of ways.  This is the underlying library that the `rest_storage_adapater` uses to communicate.

In other cases where you want to communicate with a server that isn't your default storage_adapter you would use it like:

    test "Send a simple Batman.Request", ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse( "GET", "http://batmanjs.org", ->{})
      new Batman.Request
        url: "http://batmanjs.org"
        method: "GET"
        success: (result) ->
          ok true

### Lifecycle events

There are a variety of lifecycle events that get fired in a `Request`.  Each of these are passed in as parameters in the options object in the `Request` constructor.

1) `loading()`: fired before `send()` has been called.

    test "Demonstrate the loading event", 1, ->
       new Batman.Request
         url: "http:://batmanjs.org"
         loading: ->
           ok true, "After send but before the request is made"

2) `loaded()`: fired after the request is complete

     test "Demo the loaded event", 1, ->
       Batman.Request.setupMockedResponse()
       Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {})
       new Batman.Request
         url: "http://batmanjs.org"
         loaded: ->
           ok true, "Loaded"

3) `error( xhr )`: fired when an error is detected,  `xhr` is the platform defined xhr object with a property named `request` being the `Batman.Request` object.

    test "Demo the error event", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> status: 400)
      new Batman.Request
        url: "http://batmanjs.org"
        error: (xhr) ->
          ok true, "Error"

4) `success ( response )`: fired when a successful request has been made, `response` is the body of the request response.

    test "Demonstrate the success event", 1,  ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> response: "foo")
      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          equal "foo", response

## .status
The response status, only set after `error` or `success`

    test "Demo the error event", 1, ->
     Batman.Request.setupMockedResponse()
     Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> status: 400)
     new Batman.Request
       url: "http://batmanjs.org"
       error: (xhr) ->
         equal 400, @status

## .response
The response object

    test "Demonstrate the response object", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> response: "foo" )

      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          equal "foo", JSON.parse(@response)

## .responseHeaders : object
An key/value object with the response headers

    test "responseHeaders", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> responseHeaders: { FOO: "bar" } )

      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          deepEqual @responseHeaders,{ FOO: "bar" }

## @Request(options : object)

Constructs a new `Batman.Request` with the supplied options

### `options`

1) `url : String`: A string containing the URL to which the request is sent.  Mandatory

2) `method : String`: The type of request to make eg. `GET`, `POST`.  Default is `GET`

    test "method test", ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("POST", "http://batmanjs.org", -> response : { FOO: "BAR" })
      new Batman.Request
        url: "http://batmanjs.org"
        method: "POST"
        data: { some: "data" }
        success: (response) ->
          ok true

3) `data`: Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests.

4) `contentType`: the content type to send, defaults to `application/x-www-form-urlencoded`

    test "contentType default", ->
       req = new Batman.Request
       equal req.contentType, "application/x-www-form-urlencoded"

5) `autosend`: controls whether the request will be sent automatically, deafults to `true`

    test "autosend test", ->
      response_sent = false
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        success: (response) ->
          response_sent = true
      ok !response_sent
      req.send()
      ok response_sent

6) `username`: username for authentication

7) `password`: password for authentication

8) `type`: The type of data you're expecting from the server, defaults to `json`

## ::send([data])
If `autosend` is `false`, this will initiate the request.  The data object passed in will take priority over the one specified in the `options` object

    test "request.send()", ->
      beforeResponse = (req, data) ->
        deepEqual data, { other: "thing" }
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {beforeResponse: beforeResponse} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        data: { some: "data" }
      req.send( { other: "thing" } )

# Batman.Request
 Request is an abstraction around XHR requests.  It can be used with a variety of platforms, and is implemented in the platform layer files.

### Platform Request Implmentation Libraries:
  Depending on the platform library the underlying request implementation will change:

 - `batman.jquery.js`: jQuery XHR
 - `batman.solo.js`: Reqwest.js

 _note_ For the tests there are some mocked responses, in a normal non testing situation you would not use these

### How to use Batman Request

  Batman.Request can be used a variety of ways.  This is the underlying library that the `rest_storage_adapater` uses to communicate.

  In other cases where you want to communicate with a server that isn't your default storage_adapter you would use it like:

    test "Send a simple Batman.Request", ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse( "GET", "http://batmanjs.org", ->{})
      new Batman.Request
        url: "http://batmanjs.org"
        method: "GET"
        success: (result) ->
          ok true

### Lifecycle events
 There are a variety of lifecycle events that get fired in a `Request`.  Each of these are passed in as parameters in the options object in the `Request` constructor.

 1. `loading()`: fired before `send()` has been called.

    test "Demonstrate the loading event", 1, ->
       new Batman.Request
         url: "http:://batmanjs.org"
         loading: ->
           ok true, "After send but before the request is made"

 2. `loaded()`: fired after the request is complete

     test "Demo the loaded event", 1, ->
       Batman.Request.setupMockedResponse()
       Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {})
       new Batman.Request
         url: "http://batmanjs.org"
         loaded: ->
           ok true, "Loaded"

 3. `error( xhr )`: fired when an error is detected,  `xhr` is the platform defined xhr object with a property named `request` being the `Batman.Request` object.

    test "Demo the error event", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> status: 400)
      new Batman.Request
        url: "http://batmanjs.org"
        error: (xhr) ->
          ok true, "Error"

 4. `success ( response )`: fired when a successful request has been made, `response` is the body of the request response.

    test "Demonstrate the success event", 1,  ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> response: "foo")
      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          equal "foo", response

## .status
  The response status, only set after `error` or `success`

    test "Demo the error event", 1, ->
     Batman.Request.setupMockedResponse()
     Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> status: 400)
     new Batman.Request
       url: "http://batmanjs.org"
       error: (xhr) ->
         equal 400, @status

## .response
  The response object

    test "Demonstrate the response object", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> response: "foo" )

      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          equal "foo", JSON.parse(@response)

## .responseHeaders : object
  An key/value object with the response headers

    test "responseHeaders", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> responseHeaders: { FOO: "bar" } )

      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          deepEqual @responseHeaders,{ FOO: "bar" }

## @Request(options : object)

  Constructs a new `Batman.Request` with the supplied options

  `options` description
  1. `url : String`: A string containing the URL to which the request is sent.  Mandatory
  2. `method : String`: The type of request to make eg. `GET`, `POST`.  Default is `GET`

    test "method test", ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("POST", "http://batmanjs.org", -> response : { FOO: "BAR" })
      new Batman.Request
        url: "http://batmanjs.org"
        method: "POST"
        data: { some: "data" }
        success: (response) ->
          ok true

  3. `data`: Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests.
  4. `contentType`: the content type to send, defaults to `application/x-www-form-urlencoded`

    test "contentType default", ->
       req = new Batman.Request
       equal req.contentType, "application/x-www-form-urlencoded"

  5. `autosend`: controls whether the request will be sent automatically, deafults to `true`

    test "autosend test", ->
      response_sent = false
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        success: (response) ->
          response_sent = true
      ok !response_sent
      req.send()
      ok response_sent

  6. `username`: username for authentication
  7. `password`: password for authentication
  8. `type`: The type of data you're expecting from the server, defaults to `json`

## ::send([data])
  If `autosend` is `false`, this will initiate the request.  The data object passed in will take priority over the one specified in the `options` object

    test "request.send()", ->
      beforeResponse = (req, data) ->
        deepEqual data, { other: "thing" }
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {beforeResponse: beforeResponse} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        data: { some: "data" }
      req.send( { other: "thing" } )


added purge, getCreateTime, and getLastUpdateTime

# glg-hummingbird
Polymer element to maintain hummingbird typeahead lists in browsers' localStorage

    hummingbird = require 'hummingbird'
    # shim for chrome file system access
    window.requestFileSystem = window.requestFileSystem ? window.webkitRequestFileSystem

    Polymer 'glg-hummingbird',

## Events
### HB_RESULTS
This event is emitted when there are new hummingbird results returned where the payload is the results array.

### NO_HB_INDEX
This event is emitted when attempting to load a non-existent hummingbird index from persistence.

### FS_ERROR
This event is emitted when there is an error interacting with the file system where the payload is the error object.

## Methods
### search
Method for retrieving results from a humminbird index
* query (required) - a string to be tokenized and searched

      search: (query) ->
        hbOptions =
          scoreThreshold: @scoreThreshold ? 0
          secondarySortField: @secondarySortField
          secondarySortOrder: @secondarySortOrder
          howMany: @howMany ? 10
          startPos: @startPos ? 0

        #lookup by fuzzy name match
        @idx.search query, (results) =>
          @fire 'HB_RESULTS', results
        , hbOptions

### jump
Method for retrieving a single result from a hummingbird index by ID
* id (required) - the ID of the document to be returned

      jump: (id) ->
        #lookup by ID
        @idx.jump id, (results) =>
          @fire 'HB_RESULTS', results

### upsert
Method to insert new entries into the index or update existing entries

      upsert: (doc) ->
        unless doc?.name? and doc?.id?
          console.error "glg-hb: every hummingbird document must have a minimum of 'name' and 'id' properties"
        else
          @idx.add doc

### persist
Method to persist hummingbird index to localStorage

      persist: () ->
        window.requestFileSystem window.TEMPORARY, null
        , (fs) =>
          # success handler
          fs.root.getFile @indexName, {create: true}, (fileEntry) =>
            fileEntry.createWriter (fileWriter) =>
              fileWriter.onerror = @__fileErrorHandler()
              fileWriter.onwriteend = (evt) ->
                # Do we care to share success?
                return
              try
                fileWriter.write new Blob([JSON.stringify(@idx.toJSON())], {type: 'text/plain'})
              catch err
                console.error "glg-hb: unable to persist #{@indexName} index: #{JSON.stringify err}"
            , @__fileErrorHandler()
          , @__fileErrorHandler()
        , @__fileErrorHandler()

### purge
Method to delete persisted file from disk

      purge: () ->
        window.requestFileSystem window.TEMPORARY, null, (fs) =>
          fs.root.getFile @indexName, { create: false }, (fileEntry) =>
            fileEntry.remove =>
              console.log "glg-hb: purged persisted #{@indexName} index."
            , @__fileErrorHandler()
          , @__fileErrorHandler()
        , @__fileErrorHandler()

### getLastUpdateTime
Method that returns the timestamp for the last time the index was updated (including create, add, or remove, but not persist or load)

      getLastUpdateTime: () ->
        @idx.lastUpdate

### getCreateTime
Method that returns the timestamp for the index creation (not persist or load)

      getCreateTime: () ->
        @idx.createTime

### __fileErrorHandler
Internal method to handle various filesystem errors

      __fileErrorHandler: () ->
        #closure to maintain reference to Polymer object in scope
        (err) =>
          switch err.code
            when FileError.QUOTA_EXCEEDED_ERR
              console.error 'glg-hb: QUOTA_EXCEEDED_ERR'
              @fire 'FS_ERROR', err
            when FileError.NOT_FOUND_ERR
              console.debug "glg-hb: no persisted index: #{JSON.stringify err}"
              @fire 'NO_HB_INDEX'
            when FileError.SECURITY_ERR
              console.error 'glg-hb: SECURITY_ERR'
              @fire 'FS_ERROR', err
            when FileError.INVALID_MODIFICATION_ERR
              console.error 'glg-hb: INVALID_MODIFICATION_ERR'
              @fire 'FS_ERROR', err
            when FileError.INVALID_STATE_ERR
              console.error 'glg-hb: INVALID_STATE_ERR'
              @fire 'FS_ERROR', err
            else
              console.error 'glg-hb: unknown file system error'
              @fire 'FS_ERROR', err

## Polymer Lifecycle

      created: ->
        # create an empty index
        @idx = new hummingbird()
        window.requestFileSystem window.TEMPORARY, null
        , (fs) =>
          # success handler
          fs.root.getFile @indexName, {}, (fileEntry) =>
            fileEntry.file (file) =>
              reader = new FileReader()
              reader.onerror = @__fileErrorHandler()
              reader.onloadend = (evt) ->
                # @result is the text of the file read
                if @result? and @result isnt ''
                  try
                    # if we found a persisted index, replace our empty index with it
                    _this.idx = hummingbird.Index.load JSON.parse(@result)
                  catch err
                    console.error "glg-hb: unable to load persisted #{_this.indexName} index: #{JSON.stringify err}"
                else
                  console.warn "glg-hb: found an empty file when trying to load persisted #{_this.indexName} index."
                  _this.__fileErrorHandler() {code:FileError.NOT_FOUND_ERR}
              reader.readAsText file
            , @__fileErrorHandler()
          , @__fileErrorHandler()
        , @__fileErrorHandler()

      ready: ->

      attached: ->

      domReady: ->

      detached: ->

# glg-hummingbird
Polymer element to maintain hummingbird typeahead lists in browsers' localStorage

    hummingbird = require 'hummingbird'
    # shim for chrome file system access
    window.requestFileSystem = window.requestFileSystem ? window.webkitRequestFileSystem

    Polymer 'glg-hummingbird',

## Events
### HB_RESULTS
This event is emitted when there are new hummingbird results returned where the payload is the results array.

### NO_HB_INDEX
This event is emitted when attempting to load a non-existent hummingbird index from persistence.

### FS_ERROR
This event is emitted when there is an error interacting with the file system where the payload is the error object.

## Methods
### search
Method for retrieving results from a humminbird index
* query (required) - a string to be tokenized and searched

      search: (query) ->
        hbOptions =
          scoreThreshold: @scoreThreshold ? 0
          secondarySortField: @secondarySortField
          secondarySortOrder: @secondarySortOrder
          howMany: @howMany ? 10
          startPos: @startPos ? 0

        #lookup by fuzzy name match
        @idx.search query, (results) =>
          @fire 'HB_RESULTS', results
        , hbOptions

### jump
Method for retrieving a single result from a hummingbird index by ID
* id (required) - the ID of the document to be returned

      jump: (id) ->
        #lookup by ID
        @idx.jump id, (results) =>
          @fire 'HB_RESULTS', results

### upsert
Method to insert new entries into the index or update existing entries

      upsert: (doc) ->
        unless doc?.name? and doc?.id?
          console.error "glg-hb: every hummingbird document must have a minimum of 'name' and 'id' properties"
        else
          @idx.add doc

### persist
Method to persist hummingbird index to localStorage

      persist: () ->
        window.requestFileSystem window.TEMPORARY, null
        , (fs) =>
          # success handler
          fs.root.getFile @indexName, {create: true}, (fileEntry) =>
            fileEntry.createWriter (fileWriter) =>
              fileWriter.onerror = @__fileErrorHandler()
              fileWriter.onwriteend = (evt) ->
                # Do we care to share success?
                return
              try
                fileWriter.write new Blob([JSON.stringify(@idx.toJSON())], {type: 'text/plain'})
              catch err
                console.error "glg-hb: unable to persist #{@indexName} index: #{JSON.stringify err}"
            , @__fileErrorHandler()
          , @__fileErrorHandler()
        , @__fileErrorHandler()

### __fileErrorHandler
Internal method to handle various filesystem errors

      __fileErrorHandler: () ->
        #closure to maintain reference to Polymer object in scope
        (err) =>
          switch err.code
            when FileError.QUOTA_EXCEEDED_ERR
              console.error 'glg-hb: QUOTA_EXCEEDED_ERR'
              @fire 'FS_ERROR', err
            when FileError.NOT_FOUND_ERR
              console.debug "glg-hb: no persisted index: #{JSON.stringify err}"
              @fire 'NO_HB_INDEX'
            when FileError.SECURITY_ERR
              console.error 'glg-hb: SECURITY_ERR'
              @fire 'FS_ERROR', err
            when FileError.INVALID_MODIFICATION_ERR
              console.error 'glg-hb: INVALID_MODIFICATION_ERR'
              @fire 'FS_ERROR', err
            when FileError.INVALID_STATE_ERR
              console.error 'glg-hb: INVALID_STATE_ERR'
              @fire 'FS_ERROR', err
            else
              console.error 'glg-hb: unknown file system error'
              @fire 'FS_ERROR', err

## Polymer Lifecycle

      created: ->
        # create an empty index
        @idx = new hummingbird()
        window.requestFileSystem window.TEMPORARY, null
        , (fs) =>
          # success handler
          fs.root.getFile @indexName, {}, (fileEntry) =>
            fileEntry.file (file) =>
              reader = new FileReader()
              reader.onerror = @__fileErrorHandler()
              reader.onloadend = (evt) ->
                # @result is the text of the file read
                if @result? and @result isnt ''
                  try
                    # if we found a persisted index, replace our empty index with it
                    _this.idx = hummingbird.Index.load JSON.parse(@result)
                  catch err
                    console.error "glg-hb: unable to load persisted #{_this.indexName} index: #{JSON.stringify err}"
                else
                  console.warn "glg-hb: found an empty file when trying to load persisted #{_this.indexName} index."
                  _this.__fileErrorHandler() {code:FileError.NOT_FOUND_ERR}
              reader.readAsText file
            , @__fileErrorHandler()
          , @__fileErrorHandler()
        , @__fileErrorHandler()

      ready: ->

      attached: ->

      domReady: ->

      detached: ->