cache yang.source

cache yang.source

# 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')
    fs     = require 'fs'
    path   = require 'path'
    parser = require 'yang-parser'
    indent = require 'indent-string'

    Expression = require './expression'
    XPath = require './xpath'

## 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 unless schema.arg is false

        schema = (new this kind, tag).extends schema.substmts.map (x) => @parse x, compile: 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 used 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
`yang: { resolve: {} }` 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 `yang: { resolve:
{} }` 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 != path.dirname dir
          target = path.resolve dir, "package.json"
          debug "[resolve] #{name} in #{target}"
          try
            pkginfo = JSON.parse(fs.readFileSync(target))
            found = pkginfo.yang?.resolve?[name] ? pkginfo.models[name]
          if found?
            dir = path.dirname target
            debug "[resolve] #{name} check #{found} in #{dir}"
            unless !!path.extname found
              from = null
              if (found of (pkginfo.dependencies ? {})) or (found of (pkginfo.peerDependencies ? {}))
                # due to npm changes, the dependency may be at
                # higher in the directory tree instead of being at
                # subdirectory
                debug "[resolve] check #{found} package for #{name}"
                pkgdir = dir
                while not from? and pkgdir != path.dirname pkgdir
                  pkgloc = path.resolve(pkgdir, 'node_modules', found)
                  debug "[resolve] look for #{found} package in #{pkgloc}"
                  if fs.existsSync pkgloc
                    from = pkgloc
                  pkgdir = path.dirname pkgdir unless from?
              else
                from = 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 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

## 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
        return super

      @property 'source',
        get: ->
          @state.source ?= @lookup 'extension', @kind
          @state.source or {}
        set: (value) -> @state.source = value

      @property 'datakey',
        get: -> switch
          when @parent instanceof Yang and @parent.kind is 'module'
            "#{@parent.tag}:#{@tag}"
          when @parent instanceof Yang and @parent.kind is 'submodule'
            "#{@parent['belongs-to'].tag}:#{@tag}"
          when @node and @external and not @state.relative
            "#{@origin.root.tag}:#{@tag}"
          else @tag ? @kind

      @property 'external',
        get: -> @origin? and @origin.root isnt @root and @origin.root.kind is 'module'

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

      debug: -> debug "[#{@uri}]", arguments...
      emit: (event, args...) ->
        @emitter.emit arguments...
        @root.emit event, this if event is 'change' and this isnt @root

## Instance-level methods

### compile (obj)

      compile: ->
        unless @source instanceof Expression
          throw @error "encountered unknown extension '#{@kind}'"
        super

### 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, ctx, 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.

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

Perform schema correctness validation for the passed in `data`.  This
differs from [eval](#eval-data-opts) in that the final data returned
is not infused as dynamic data model using Property instances. It
should be used to perform a sanity check on the data if it will not
be programmatically modified.

For example, [Property](./property.litcoffee) instance uses `validate`
when dealing with non-configurable data nodes `config false`.

      validate: (data, ctx={}) ->
        # XXX - for now, we just do the full apply...
        return @apply data, ctx

        @compile() unless @resolved
        @debug "validating data to schema"

        try @predicate?.call this, data
        catch e
          throw @error "predicate validation error: #{e}", data

        if @kind is 'list' and Array.isArray data
          data = data.map (item) =>
            if Array.isArray item
              throw @error "list item cannot be another array"
            @validate item
        else
          # TODO: consider appending '..' property here?
          data[node.datakey] = node.validate data[node.datakey] for node in @nodes when data?

        unless @nodes.length
          data = this.apply data, ctx
        else
          data = attr.apply data, ctx for attr in @attrs

        return data

### 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, ''
        res = XPath.split(ypath).map(normalizeEntry).join('/')
        res = '/' + res if /^\//.test ypath
        return res

### 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
        @debug key
        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

        @debug "checking #{target}"
        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

        @debug "matching #{kind} #{tag}"
        match = @match kind, tag
        return switch
          when rest.length is 0 then match
          when Array.isArray match then match.map((x) -> x.locate rest).filter(Boolean)
          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 "[match] checking #{m.tag}"
          return m.module?.match kind, arg

        @debug "[match] check if one of available modules"
        module = @lookup 'module', prefix
        return module.match kind, arg if module?

### 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 @argument?
          s += ' ' + switch @argument
            when 'value' then switch
              when Array.isArray(@tag) then "'#{@tag.join(' ')}'"
              else "'#{@tag}'"
            when 'text'
              "\n" + (indent '"'+@tag+'"', ' ', opts.space)
            else @tag
        sub =
          @children
            .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 @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')
    fs     = require 'fs'
    path   = require 'path'
    parser = require 'yang-parser'
    indent = require 'indent-string'

    Expression = require './expression'
    XPath = require './xpath'

## 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 unless schema.arg is false

        schema = (new this kind, tag).extends schema.substmts.map (x) => @parse x, compile: 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 used 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
`yang: { resolve: {} }` 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 `yang: { resolve:
{} }` 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 != path.dirname dir
          target = path.resolve dir, "package.json"
          debug "[resolve] #{name} in #{target}"
          try
            pkginfo = JSON.parse(fs.readFileSync(target))
            found = pkginfo.yang?.resolve?[name] ? pkginfo.models[name]
          if found?
            dir = path.dirname target
            debug "[resolve] #{name} check #{found} in #{dir}"
            unless !!path.extname found
              from = null
              if (found of (pkginfo.dependencies ? {})) or (found of (pkginfo.peerDependencies ? {}))
                # due to npm changes, the dependency may be at
                # higher in the directory tree instead of being at
                # subdirectory
                debug "[resolve] check #{found} package for #{name}"
                pkgdir = dir
                while not from? and pkgdir != path.dirname pkgdir
                  pkgloc = path.resolve(pkgdir, 'node_modules', found)
                  debug "[resolve] look for #{found} package in #{pkgloc}"
                  if fs.existsSync pkgloc
                    from = pkgloc
                  pkgdir = path.dirname pkgdir unless from?
              else
                from = 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 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

## 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
        return super

      @property 'source',
        get: -> @state.source or (@lookup 'extension', @kind) or {}
        set: (value) -> @state.source = value

      @property 'datakey',
        get: -> switch
          when @parent instanceof Yang and @parent.kind is 'module'
            "#{@parent.tag}:#{@tag}"
          when @parent instanceof Yang and @parent.kind is 'submodule'
            "#{@parent['belongs-to'].tag}:#{@tag}"
          when @node and @external and not @state.relative
            "#{@origin.root.tag}:#{@tag}"
          else @tag ? @kind

      @property 'external',
        get: -> @origin? and @origin.root isnt @root and @origin.root.kind is 'module'

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

      debug: -> debug "[#{@uri}]", arguments...
      emit: (event, args...) ->
        @emitter.emit arguments...
        @root.emit event, this if event is 'change' and this isnt @root

## Instance-level methods

### compile (obj)

      compile: ->
        unless @source instanceof Expression
          throw @error "encountered unknown extension '#{@kind}'"
        super

### 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, ctx, 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.

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

Perform schema correctness validation for the passed in `data`.  This
differs from [eval](#eval-data-opts) in that the final data returned
is not infused as dynamic data model using Property instances. It
should be used to perform a sanity check on the data if it will not
be programmatically modified.

For example, [Property](./property.litcoffee) instance uses `validate`
when dealing with non-configurable data nodes `config false`.

      validate: (data, ctx={}) ->
        # XXX - for now, we just do the full apply...
        return @apply data, ctx

        @compile() unless @resolved
        @debug "validating data to schema"

        try @predicate?.call this, data
        catch e
          throw @error "predicate validation error: #{e}", data

        if @kind is 'list' and Array.isArray data
          data = data.map (item) =>
            if Array.isArray item
              throw @error "list item cannot be another array"
            @validate item
        else
          # TODO: consider appending '..' property here?
          data[node.datakey] = node.validate data[node.datakey] for node in @nodes when data?

        unless @nodes.length
          data = this.apply data, ctx
        else
          data = attr.apply data, ctx for attr in @attrs

        return data

### 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, ''
        res = XPath.split(ypath).map(normalizeEntry).join('/')
        res = '/' + res if /^\//.test ypath
        return res

### 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
        @debug key
        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

        @debug "checking #{target}"
        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

        @debug "matching #{kind} #{tag}"
        match = @match kind, tag
        return switch
          when rest.length is 0 then match
          when Array.isArray match then match.map((x) -> x.locate rest).filter(Boolean)
          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 "[match] checking #{m.tag}"
          return m.module?.match kind, arg

        @debug "[match] check if one of available modules"
        module = @lookup 'module', prefix
        return module.match kind, arg if module?

### 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 @argument?
          s += ' ' + switch @argument
            when 'value' then switch
              when Array.isArray(@tag) then "'#{@tag.join(' ')}'"
              else "'#{@tag}'"
            when 'text'
              "\n" + (indent '"'+@tag+'"', ' ', opts.space)
            else @tag
        sub =
          @children
            .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 @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


make compareClockwise more flexible

make compareClockwise more flexible

This function can be used as a comparator to sort a number of nodes
adjacent to some given point ``m`` in clockwise order relative to some
reference point ``p``.

Imagine you are traversing the edges of a planar graph. You arive at ``m``
("mid point") via some other point ``p`` ("parent"), and you now want to
visit the other points ("children") connected to ``m`` in clockwise order.
This HOF answers the question given ``p`` and ``m`` as above, which of two
given children ``a`` and ``b`` should be visit first. In particular, you
can use it with something like ``Array.prototype.sort`` to sort an
array of children.

The implementation works like this:

Imagine a line through the `p` and `m`. Either both children are on
the same side, or they are on different sides.  If they are on different
sides, the one on the left side comes first.

    ccw = require("./ccw")
    identity = (x)->x

    module.exports = (p,m,vpos=identity)->
      p=vpos p
      m=vpos m
      (a,b)->

        a = vpos a
        b = vpos b
        # positive values indicate counter clockwise movement
        # i.e. left side of parent.
        ca = ccw(p,m,a)
        cb = ccw(p,m,b)

        if ca*cb < 0 # different sign, ergo different sides
          if ca > 0 or cb < 0 then -1 else 1

Otherwise, i.e. if both children are on the same side, we need another
comparison.

        else ccw(m,a,b) # negative --> clockwise --> a is left

This function can be used as a comparator to sort a number of nodes
adjacent to some given point ``m`` in clockwise order relative to some
reference point ``p``.

Imagine you are traversing the edges of a planar graph. You arive at ``m``
("mid point") via some other point ``p`` ("parent"), and you now want to
visit the other points ("children") connected to ``m`` in clockwise order.
This HOF answers the question given ``p`` and ``m`` as above, which of two
given children ``a`` and ``b`` should be visit first. In particular, you
can use it with something like ``Array.prototype.sort`` to sort an
array of children.

The implementation works like this:

Imagine a line through the `p` and `m`. Either both children are on
the same side, or they are on different sides.  If they are on different
sides, the one on the left side comes first.

    ccw = require("./ccw")

    module.exports = (p,m)->(a,b)->

      # positive values indicate counter clockwise movement
      # i.e. left side of parent.
      ca = ccw(p,m,a)
      cb = ccw(p,m,b)

      if ca*cb < 0 # different sign, ergo different sides
        if ca > 0 or cb < 0 then -1 else 1

Otherwise, i.e. if both children are on the same side, we need another
comparison.

      else ccw(m,a,b) # negative --> clockwise --> a is left


oops, remove duplicated stuff

oops, remove duplicated stuff

# Batman.Set

`Set` is an observable, `Batman.Object` wrapper around `SimpleSet`. `Set` also extends [`Batman.Enumerable`](/docs/api/batman.enumerable.html), which provides [many useful methods](/docs/api/batman.enumerable.html).

### SimpleSet vs Set

`SimpleSet` and `Set` are two distinct classes in Batman. `SimpleSet` implements the basic set semantics, but it is *not* a `Batman.Object`, so properties on it (like its `length` or `toArray`) cannot be bound. `Set` is a `Batman.Object`, so it can be observed, and thus plays nicely with the accessor system. Use a `SimpleSet` only when you know nothing will need to be observed on the set you are creating, which usually isn't a valid assumption. If it is in fact valid, consider using a native array as well, as iteration and membership checks will be faster.

## constructor(items...)

When creating a `Set`, items forming the initial set can be passed as separate arguments to the constructor

    test 'new Set constructor can be called without arguments', ->
      set = new Batman.Set
      deepEqual set.toArray(), []

    test 'new Set constructor can be passed items to add to the set.', ->
      set = new Batman.Set('a', 'b', 'c')
      deepEqual set.toArray().sort(), ['a', 'b', 'c']

## length : number

A count of the items in a `Set` can be found at its `length` property.

## isEmpty : boolean

Observable property for `isEmpty()`

## toArray : Array

Observable property for `toArray()`. Whenever items are added or removed on the set, the `toArray` property will change. This is the mechanism by which Batman's view bindings get notified of collection updates.

    test 'observers on the toArray property fire when the set changes', ->
      results = null
      set = new Batman.Set('a', 'b', 'c')
      set.observe('toArray', (newArray) -> results = newArray.sort())
      deepEqual set.add('d'), ['d']
      deepEqual results, ['a', 'b', 'c', 'd']
      deepEqual set.remove('b'), ['b']
      deepEqual results, ['a', 'c', 'd']

## has(item) : Boolean

`has` returns a boolean describing if the given `item` is a member of the set.

_Note_: Using `has(item)` in an accessor body will register the set `has` is called upon as a source of the property being calculated. This so that whenever the set changes, the property will be recalculated, because the set may now have or not have the item in question.

    test 'Set::has indicates if an item is a member of the set or not.', ->
      set = new Batman.Set('a', 'b', 'c')
      ok set.has('a')
      equal set.has('d'), false

    test 'Set::has registers the set as a source of an accessor', ->
      class Team extends Batman.Object
        constructor: ->
          @awards = new Batman.Set()

        @accessor 'bestEver?', -> @get('awards').has('Stanley Cup')

      result = null
      team = new Team
      team.observeAndFire 'bestEver?', (status) -> result = status
      team.get('awards').add 'Eastern Conference Champs'
      equal result, false
      team.get('awards').add 'Stanley Cup'
      equal result, true

## add(items...)

`add` adds 0 or more new items to the set. `add` returns an array of the items which have been newly added to the set, which is to say the intersection of the argument items and the set's complement before addition.

`add` fires the `itemsWereAdded` event with the list of items newly added to the set if that list has length greater than 0. This is to say the event will not be fired if the items passed to add were all already members of the set.

    test 'Set::add adds an item to the set', ->
      set = new Batman.Set()
      equal set.has('a'), false
      deepEqual set.add('a'), ['a']
      equal set.has('a'), true

    test 'Set::add returns only the new items that weren\'t previously in the set', ->
      set = new Batman.Set('a', 'b')
      deepEqual set.add('b','c','d').sort(), ['c', 'd']
      deepEqual set.toArray().sort(), ['a', 'b', 'c', 'd']

    test 'Set::add fires the itemsWereAdded event with the items newly added to the set', ->
      results = null
      set = new Batman.Set('a', 'b')
      set.on('itemsWereAdded', (item) -> results = item)
      set.add('b','c','d')
      deepEqual results.sort(), ['c','d']

    test 'Set::add does not fire the itemsWereAdded event if the added items were already in the set.', ->
      results = undefined
      set = new Batman.Set('a', 'b')
      set.on('itemsWereAdded', (items) -> results = items)
      set.add('a', 'b')
      equal typeof results, 'undefined'

## remove(items...)

`remove` removes 0 or more items from the set. `remove` returns an array of the items which were successfully removed from the set, which is to say the intersection of the argument items and the set itself before removal.

`remove` fires the `itemsWereRemoved` event with the list of removed items if that list has length greater than 0. This is to say the event will not be fired if none of the passed items were members of the set.

    test 'Set::remove removes an item from the set', ->
      set = new Batman.Set('a')
      equal set.has('a'), true
      deepEqual set.remove('a'), ['a']
      equal set.has('a'), false

    test 'Set::remove returns only the new items that were previously in the set', ->
      set = new Batman.Set('a', 'b')
      deepEqual set.remove('b','c','d').sort(), ['b']
      deepEqual set.toArray(), ['a']

    test 'Set::remove fires the itemsWereRemoved event with the items removed to the set', ->
      results = null
      set = new Batman.Set('a', 'b', 'c')
      set.on('itemsWereRemoved', (items) -> results = items)
      set.remove('b','c')
      deepEqual results.sort(), ['b','c']

    test 'Set::remove does not fire the itemsWereRemoved event if the removed items were not already members of the set.', ->
      results = undefined
      set = new Batman.Set('a', 'b')
      set.on('itemsWereRemoved', (items) -> results = items)
      set.remove('c', 'd')
      equal typeof results, 'undefined'

## find(testFunction : function) : [Object]

`find` returns the first item within the set for which the `testFunction` called with the item returns `true`, or `undefined` if no item passes the test.

_Note_: `find` returns the first item the test passes for, but since set iteration has no specified order, no guarantee can be made about which item will be returned if more than one item in the set passes the test. However, set iteration order is stable, so `find` called on the same set twice should return the same item.

    test 'Set::find returns the first item for which the test function passes', ->
      set = new Batman.Set(1, 2, 3)
      equal set.find((x) -> x % 2 == 0), 2

    test 'Set::find returns undefined if no items pass the test function', ->
      set = new Batman.Set(1, 2, 3)
      equal typeof set.find((x) -> x > 5), 'undefined'

## forEach(iteratorFunction : function[, context: Object])

`forEach` calls the `iteratorFunction` with each item in the set, optionally executing the `iteratorFunction` in the passed context. Returns `undefined`.

_Note_: Set iteration order is not defined, thus Set client code cannot rely on one item being iterated over before another, regardless of when the Set's items were added. If you need an ordered set, Batman provides `SetSort` for exactly this, while including the added benefit of observability. If you need a one time ordering of a set, you can get the array representation with `toArray` and then use vanilla JavaScript `sort` on that array.

_Note_: Using `forEach()` in an accessor body will register the set iterated over as a source of the property being calculated. This so that whenever the set changes, the property will be recalculated. This can become an issue if you iterate over a set and modify the set's items, as when the property recalculates that modification will potentially happen on items it's happened on previously.

    test 'Set::forEach iterates over each item in the set', ->
      sum = 0
      set = new Batman.Set(1,2,3)
      set.forEach (x) -> sum += x
      equal sum, 6

    test 'Set::forEach iterates over each item in the set optionally in the provided context', ->
      context = {sum: 0}
      set = new Batman.Set(1,2,3)
      set.forEach((x) ->
        @sum += x
      , context)
      equal context.sum, 6

    test 'Set::forEach registers the set as a source if called in an accessor body', ->
      class Team extends Batman.Object
        constructor: ->
          @players = new Batman.Set()
        @accessor 'willWinTheCup?', ->
          sedinCount = 0
          @players.forEach (player) ->
            sedinCount++ if player.split(' ')[1] == 'Sedin'
          sedinCount >= 2

      result = null
      team = new Team()
      team.observeAndFire 'willWinTheCup?', (status) -> result = status
      equal team.get('willWinTheCup?'), false
      team.get('players').add 'Henrik Sedin'
      equal result, false
      team.get('players').add 'Daniel Sedin'
      equal result, true

## isEmpty() : boolean

`isEmpty` returns a boolean: `true` if the set has no items, and `false` if it has any items.

_Note_: Using `isEmpty()` in an accessor body will register the set `isEmpty` is called on as a source of the property being calculated, so that whenever the set changes the property will be recalculated.

    test 'Set::isEmpty returns true if the set has no items', ->
      set = new Batman.Set()
      ok set.isEmpty()
      set.add('a')
      equal set.isEmpty(), false

    test 'Set::isEmpty registers the set as a source of an accessor', ->
      class Team extends Batman.Object
        constructor: ->
          @games = new Batman.Set()
        @accessor 'seasonStarted?', -> !@games.isEmpty()

      team = new Team
      equal team.get('seasonStarted?'), false
      team.get('games').add({win: true})
      equal team.get('seasonStarted?'), true

## clear() : Array

`clear` removes all items from a set. Returns an array of all the items in the set.

`clear` will fire the `itemsWereRemoved` event once with all the items in the set.

_Note_: Set iteration order is not defined, so the order of the array of items returned by `clear` is undefined.

    test 'Set::clear empties the set', ->
      set = new Batman.Set('a', 'b', 'c')
      equal set.isEmpty(), false
      deepEqual set.clear().sort(), ['a', 'b', 'c']
      ok set.isEmpty()

    test 'Set::clear fires the itemsWereRemoved event with all the items in the set', ->
      result = null
      set = new Batman.Set('a', 'b', 'c')
      set.on('itemsWereRemoved', (items) -> result = items)
      set.clear()
      deepEqual result.sort(), ['a', 'b', 'c']

## replace(collection : Enumerable) : Array

`replace` removes all the items in a set and then adds all the items found in another `collection`. The other collection must have a `toArray` function which returns an array representation of the collection. Returns the array of items added.

`replace` will fire the `itemsWereRemoved` event once with all the items in the set, and then the `itemsWereAdded` event once with the items from the incoming collection.

    test 'Set::replace empties the set and then adds items from a different collection', ->
      set = new Batman.Set('a', 'b', 'c')
      secondSet = new Batman.Set('d', 'e', 'f')
      set.replace(secondSet)
      deepEqual set.toArray().sort(), ['d', 'e', 'f']

    test 'Set::replace fires the itemsWereRemoved event with all the items in the set', ->
      result = null
      set = new Batman.Set('a', 'b', 'c')
      set.on('itemsWereRemoved', (items) -> result = items)
      set.replace(new Batman.SimpleSet())
      deepEqual result.sort(), ['a', 'b', 'c']

    test 'Set::replace fires the itemsWereAdded event with all the items in the incoming set', ->
      result = null
      set = new Batman.Set()
      set.on('itemsWereAdded', (items) -> result = items)
      set.replace(new Batman.SimpleSet('a', 'b', 'c'))
      deepEqual result.sort(), ['a', 'b', 'c']

## toArray() : Array

`toArray` returns an array representation of the set.

_Note_: Set iteration order is not defined, so the order in which the set's items appear in the array is not defined. It is however stable, so the order of the items in two successive `toArray` calls where the set was not modified in between should be the same.

_Note_: `toArray` is also an observable property.

_Note_: Using `toArray()` in an accessor body will register the set `toArray` is called on as a source of the property being calculated, so that whenever the set changes the property will be recalculated.

    test 'Set::toArray returns an array representation of the set', ->
      set = new Batman.Set()
      deepEqual set.toArray(), []
      set.add('a', 'b', 'c')
      deepEqual set.toArray().sort(), ['a', 'b', 'c']

## merge(collections... : Enumerable) : Set

`merge` adds all the items in a set and all the items in the passed `collections` to a new set and returns it. A `collection` is an object which has a `forEach` function. `merge` is a non-destructive collection union, so the set `merge` is called on and each `collection` passed to `merge` are unaffected by the call.

_Note_: Be careful about using `merge` within accessors. Calling `merge` in an accessor function body will register the set `merge` is called upon as a source of the property being calculated, which means when the set changes, that accessor will be recalculated. This means the O(n * m) merge will occur again each time, and return an entirely new `Set` instance. If the previously returned `Set` instance is retained after recalculation, this is a big memory leak. Instead of merging in accessors, try to use a `SetUnion` or a `SetIntersection`.

    test 'Set::merge returns a new set with the items of the original set and the passed set', ->
      abc = new Batman.Set('a', 'b', 'c')
      def = new Batman.Set('d', 'e', 'f')
      equal Batman.typeOf(set = abc.merge(def)), 'Object'
      deepEqual set.toArray().sort(), ['a', 'b', 'c', 'd', 'e', 'f']

## indexedBy(key : String) : SetIndex

`indexedBy` returns a hash of sets which buckets all the items in the callee set by the value of a particular `key`. The value of the passed `key` is `get`ted from each object in the set, and then a hash of each value to a set of the items with that value at the `key` is built. This hash of sets is a smart object called a `SetIndex` which will continue to observe the set and the value of the `key` on each item in the set to ensure the set index remains up to date. `SetIndex` also has a friend named `UniqueSetIndex` which will give you a hash of items instead of a hash of sets with items for easy access if you know each item's value at the `key` is unique.

    test 'Set::indexedBy returns a new SetIndex with the items bucketed by the value of the key', ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'), Batman(colour: 'blue'))
      index = set.indexedBy('colour')
      ok index.get('blue') instanceof Batman.Set
      equal index.get('blue').get('length'), 2
      equal index.get('green').get('length'), 1

    test 'Set::indexedBy returns a new SetIndex which observes the set for new additions and stays up to date', ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'))
      index = set.indexedBy('colour')
      equal index.get('blue').get('length'), 1
      newItem = Batman(colour: 'blue')
      set.add(newItem)
      equal index.get('blue').get('length'), 2
      ok index.get('blue').has(newItem)
      set.remove(newItem)
      equal index.get('blue').get('length'), 1

    test 'Set::indexedBy returns a new SetIndex which observes the items in the set for changes to the observed key', ->
      itemA = Batman(colour: 'blue')
      itemB = Batman(colour: 'green')
      set = new Batman.Set(itemA, itemB)
      index = set.indexedBy('colour')
      equal index.get('blue').get('length'), 1
      equal index.get('green').get('length'), 1
      itemA.set('colour', 'green')
      equal index.get('blue').get('length'), 0
      equal index.get('green').get('length'), 2

`SetIndex`es can be created by calling the `indexedBy` function on the `Set`, as well as by `get`ting a `key` on the `indexedBy` property.

    test "Set::get('indexedBy.someKey') returns a new SetIndex for 'someKey'", ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'))
      index = set.get('indexedBy.colour')
      equal index.get('blue').get('length'), 1

## indexedByUnique(key : String) : UniqueSetIndex

`indexedByUnique` returns a hash of items keyed by the value of the given `key` on each item from the callee set. The value of the passed `key` is `get`ted from each object in the set, and then a hash of each value to an item with that value at the `key` is built. This hash of items is a smart object called a `UniqueSetIndex` which will continue to observe the set and the value of the `key` on each item in the set to ensure the index remains up to date. Note that the semantics for which item ends up in the hash if two items have the same value for the `key` are undefined, so it is wise to only use `UniqueSetIndex`es on keys who's values are going to be unique in the set. If the values are not going to be unique, you may be interested in `SetIndex` and `Set::indexedBy`.

    test 'Set::indexedByUnique returns a new UniqueSetIndex with the items hashed by the value of the key', ->
      greenItem = Batman(colour: 'green')
      blueItem = Batman(colour: 'blue')
      set = new Batman.Set(greenItem, blueItem)
      index = set.indexedByUnique('colour')
      ok blueItem == index.get('blue')
      ok greenItem == index.get('green')
      equal undefined, index.get('red')

    test 'Set::indexedByUnique returns a new UniqueSetIndex which observes the set for new additions and stays up to date', ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'))
      index = set.indexedByUnique('colour')
      newItem = Batman(colour: 'red')
      set.add(newItem)
      ok newItem == index.get('red')
      set.remove(newItem)
      equal undefined, index.get('red')

    test 'Set::indexedByUnique returns a new UniqueSetIndex which observes the items in the set for changes to the observed key', ->
      itemA = Batman(colour: 'blue')
      itemB = Batman(colour: 'green')
      set = new Batman.Set(itemA, itemB)
      index = set.indexedByUnique('colour')
      equal index.get('blue')?, true
      equal index.get('green')?, true
      equal index.get('red')?, false
      itemA.set('colour', 'red')
      equal index.get('blue')?, false
      equal index.get('green')?, true
      equal index.get('red')?, true

`UniqueSetIndex`es can be created by calling the `indexedByUnique` function on the `Set`, as well as by `get`ting a `key` on the `indexedByUnique` property.

    test "Set::get('indexedByUnique.someKey') returns a new UniqueSetIndex for 'someKey'", ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'))
      index = set.get('indexedByUnique.colour')
      equal 'blue', index.get('blue').get('colour')

## sortedBy(key: String [, order: String]) : SetSort

`sortedBy` returns a `Set` like object containing all the items of the callee set but with a defined iteration order (unlike `Set`). The iteration order is defined as the alpha numeric sorting of the values of the passed `key` gotten from each item. The direction of the sort can be controlled with the `order` argument, which defaults to `asc` (short for ascending) or can be passed as `desc` (short for descending). This `Set` like object is a `SetSort` which encapsulates the logic to get the values from each item at the passed `key` and traverse the `Set` in the values sorted order.

`SetSort`s are useful for getting a transform of a `Set` which sorted, but also because the sort stays up to date as items are added or removed to the sorted set, or the value at the `key` changes on any of the items in the set.

    test 'Set::sortedBy returns a new SetSort who can be iterated in the sorted order of the value of the key on each item', ->
      set = new Batman.Set(Batman(place: 3, name: 'Harry'), Batman(place: 1, name: 'Tom'), Batman(place: 2, name: 'Camilo'))
      sort = set.sortedBy('place')
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Tom', 'Camilo', 'Harry']

    test 'Set::sortedBy returns a new SetSort which observes the callee set for additions or removals and puts new items in the sorted order', ->
      set = new Batman.Set(Batman(place: 3, name: 'Harry'), Batman(place: 1, name: 'Tom'), Batman(place: 2, name: 'Camilo'))
      sort = set.sortedBy('place')
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Tom', 'Camilo', 'Harry']
      burke = Batman(place: 1.5, name: 'Burke')
      set.add(burke)
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Tom', 'Burke', 'Camilo', 'Harry']

    test 'Set::sortedBy returns a new SetSort which observes each item in the callee set for changes to the sort key', ->
      harry = Batman(place: 2, name: 'Harry')
      tom = Batman(place: 1, name: 'Tom')
      set = new Batman.Set(harry, tom)
      sort = set.sortedBy('place')
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Tom', 'Harry']
      tom.set('place', 3)
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Harry', 'Tom']

`SetSort`s can be created by calling the `sortedBy` function on the `Set`, as well as by `get`ting a `key` on the `sortedBy` property. Note that with this instantiation form you can't pass an order to the `SetSort`.

    test "Set::get('sortedBy.someKey') returns a new SetSort onn 'someKey'", ->
      set = new Batman.Set(Batman(place: 3, name: 'Harry'), Batman(place: 1, name: 'Tom'), Batman(place: 2, name: 'Camilo'))
      sort = set.get('sortedBy.place')
      equal 'Harry', sort.get('toArray')[2].get('name')

# Batman.SetIndex

`Batman.SetIndex` is a grouped collection of items derived from a [`Batman.Set`]() filled with [`Batman.Object`]()s. It extends `Batman.Object` and [`Batman.Enumerable`](), so it inherits methods from them, too. In short, a `SetIndex` tracks its base `Set` and contains "buckets" of items from that `Set`, grouped by the provided key.

```coffeescript
batarang = new Batman.Object(name: "Batarang", type: "ranged")
grappleGun = new Batman.Object(name: "Grapple Gun", type: "ranged")
fists = new Batman.Object(name: "Fists", type: "melee")

weapons = new Batman.Set(batarang, grappleGun, fists)
weaponsByType = new Batman.SetIndex(set, 'type')

weaponsByType.get('ranged.length') # => 2
weaponsByType.get('melee.length')  # => 1
weaponsByType.get('ranged').map (weapon) -> weapon.get('name')  # => ["Batarang", "GrappleGun"]
weaponsByType.toArray()                                         # => ["ranged", "melee"]
```

## ::constructor(base : Set, key : String ) : SetIndex

A `SetIndex` is made with a `base` and a `key`. Items in the `base` set will be grouped according to their value for `key`. The resulting `SetIndex` observes its `base`, so any items added to the `base` are also added (and indexed) in the `SetIndex`

## ::get(value : String) : Set

Returns a `Batman.Set` of items whose indexed `key` matches `value`. It returns an empty set if no items match `value`, but if any matching items are added to the `base` set, they will also be added to this set.

## ::toArray() : Array

Returns an array with the distinct values of `key` provided to the constructor.

## ::forEach(func)

Calls `func(key, group)` for each group in the SetIndex.

# Batman.UniqueSetIndex

`Batman.UniqueSetIndex` extends `SetIndex` but adds a new consideration: its index contains _first matching item_ for each value of the `key` (rather than all matching items).

```coffeescript
batarang = new Batman.Object(name: "Batarang", type: "ranged")
grappleGun = new Batman.Object(name: "Grapple Gun", type: "ranged")
fists = new Batman.Object(name: "Fists", type: "melee")

weapons = new Batman.Set(batarang, grappleGun, fists)
weaponsByUniqueType = new Batman.UniqueSetIndex(set, 'type')
weaponsByUniqueType.get('ranged.name') # => "Batarang"
```

## ::get(value : String) : Object

Returns the first matching member whose indexed `key` is equal to `value`.

# Batman.SetSort
# Batman.SetUnion
# Batman.SetIntersection
# Batman.SetComplement


Added missing initial values for new turnLeft/turnRight controller axes.

This exports an object, where keys are control names and values are numbers from
0 to 1 representing state.  These are not, however, clamped to this range.

    module.exports =
        left: 0
        right: 0
        turnLeft: 0
        turnRight: 0
        forward: 0
        backward: 0
        up: 0
        down: 0

This exports an object, where keys are control names and values are numbers from
0 to 1 representing state.  These are not, however, clamped to this range.

    module.exports =
        left: 0
        right: 0
        forward: 0
        backward: 0
        up: 0
        down: 0


add some sugar

add some sugar

# [posib.](http://posib.krkn.be)

## **/server/models/brick/rich.js** - *RichBrick class*

coded by leny @ krkn

started at 04/02/14

* * *

RichBrick class.

A *RichBrick* is an editable text block tag.

The following tags are RichBricks : `p`

An unique RichBricks can represent subsequent `p` tags.

* * *

    "use strict"

    root = "#{ __dirname }/../.."

    cheerio = require "cheerio"

    config = require "#{ root }/core/config.js"

    Brick = require "#{ root }/models/brick.js"

    module.exports = class RichBrick extends Brick

### _create()

        _create: ->
            content: if @node.html().trim() then "
#{ @node.html().trim() }
" else "
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
"

### _render()

        _render: ->
            oAttributes = @node.attr()
            ( @nodes = cheerio @get "content" ).each ->
                cheerio( @ ).attr sAttribute, sValue for sAttribute, sValue of oAttributes
            @node.replaceWith @nodes

### _clean()

As the *RichBrick* class can rely multiple nodes, the default Brick._clean() method isnt adapted.

        _clean: ->
            @nodes.each ->
                cheerio( @ ).removeAttr "data-posib-ref"

# [posib.](http://posib.krkn.be)

## **/server/models/brick/rich.js** - *RichBrick class*

coded by leny @ krkn

started at 04/02/14

* * *

RichBrick class.

A *RichBrick* is an editable text block tag.

The following tags are RichBricks : `p`

An unique RichBricks can represent subsequent `p` tags.

* * *

    "use strict"

    root = "#{ __dirname }/../.."

    cheerio = require "cheerio"

    config = require "#{ root }/core/config.js"

    Brick = require "#{ root }/models/brick.js"

    module.exports = class RichBrick extends Brick

### _create()

        _create: ->
            content: if @node.html().trim() then "
#{ @node.html().trim() }
" else "
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
"

### _render()

        _render: ->
            oAttributes = @node.attr()
            ( @nodes = cheerio @get "content" ).each ->
                for sAttribute, sValue of oAttributes
                    cheerio( @ ).attr sAttribute, sValue
            @node.replaceWith @nodes

### _clean()

As the *RichBrick* class can rely multiple nodes, the default Brick._clean() method isnt adapted.

        _clean: ->
            @nodes.each ->
                cheerio( @ ).removeAttr "data-posib-ref"


Ajax with polling

Ajax with polling

    _self = this

    POLL_TYPE =
     progress: true

##Response class

    class Response
     constructor: (data, port, options) ->
      @id = data.id
      @port = port
      @options = options
      @queue = []
      @fresh = true

     progress: (progress, data) ->
      @queue.push method: 'progress', data: data, options: {progress: progress}
      @_handleQueue()

     success: (data) ->
      @queue.push method: 'success', data: data, options: {}
      @_handleQueue()

     fail: (data) ->
      @queue.push method: 'fail', data: data, options: {}
      @_handleQueue()

     setOptions: (options) ->
      @options = options
      @fresh = true
      @_handleQueue()

     _handleQueue: ->
      return unless @queue.length > 0

      d = @queue[0]

      if @port.isStreaming
       @port.respond this, d.method, d.data, d.options, @options
       @queue.shift()
      else if @fresh
       @port.respond this, d.method, d.data, d.options, @options
       @queue.shift()
       @fresh = false

##Call class

    class Call
     constructor: (@id, @method, @data, @callbacks, @options) -> null

     handle: (data, options) ->
      if not @callbacks[options.status]?
       return if options.status is 'progress'
       throw new Error "No callback registered #{@method} #{options.status}"
      @callbacks[options.status] data, options

##Port base class

    class Port
     constructor: ->
      @handlers = {}
      @callsCache = {}
      @callsCounter = 0
      @id = parseInt Math.random() * 1000
      @responses = {}
      @wrappers =
       send: []
       respond: []
       handleCall: []
       handleResponse: []

     isStreaming: true

     onerror: (msg, options) ->
      console.log msg, options

     wrap: (wrapper) ->
      for key, f of wrapper
       if @wrappers[key]?
        @wrappers[key].push f
       else
        this[key] = f

###Send RPC call

     send: (method, data, callbacks, options = {}) ->
      if (typeof callbacks) is 'function'
       callbacks =
        success: callbacks

      for f in @wrappers.send
       return unless f.apply this, [method, data, callbacks, options]

      @_send @_createCall method, data, callbacks, options

###Respond to a RPC call

     respond: (response, status, data, options = {}, portOptions = {}) ->
      for f in @wrappers.respond
       return unless f.apply this, [response, status, data, options, portOptions]

      @_respond (@_createResponse response, status, data, options), portOptions
      if not POLL_TYPE[status]?
       delete @responses[response.id]

###Create Call object
This is a private function

     _createCall: (method, data, callbacks, options) ->
      call = new Call "#{@id}-#{@callsCounter}", method, data, callbacks, options
      @callsCounter++
      @callsCache[call.id] = call

      params =
       type: 'call'
       id: call.id
       method: call.method
       data: call.data

      params[k] = v for k, v of options
      return params

###Create Response object

     _createResponse: (response, status, data, options) ->
      params =
       type: 'response'
       id: response.id
       status: status
       data: data

      params[k] = v for k, v of options

      return params

###Add handler

     on: (method, callback) ->
      @handlers[method] = callback

###Handle incoming message

     _handleMessage: (data, options) ->
      switch data.type
       when 'response'
        @_handleResponse data, options
       when 'call'
        @_handleCall data, options
       when 'poll'
        @_handlePoll data, options

     _handleCall: (data, options) ->
      for f in @wrappers.handleCall
       return unless f.apply this, arguments
      if not @handlers[data.method]?
       throw new Error "Unknown method: #{data.method}"
      @responses[data.id] = new Response data, this, options
      @handlers[data.method] data.data, data, @responses[data.id]

     _handleResponse: (data, options) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       throw new Error "Response without call: #{data.id}"
      @callsCache[data.id].handle data.data, data

     _handlePoll: (data, options) ->
      throw new Error "Poll not implemented"

##WorkerPort class
Used for browser and worker

    class WorkerPort extends Port
     constructor: (worker) ->
      super()
      @worker = worker
      @worker.onmessage = @_onMessage.bind this
      @worker.onerror = @_onError.bind this

     _send: (data) ->  @worker.postMessage data
     _respond: (data) -> @worker.postMessage data

     _onMessage: (e) ->
      data = e.data
      @_handleMessage data

     _onError: (e) -> console.log e

##SocketPort class

    class SocketPort extends Port
     constructor: (socket) ->
      super()
      @socket = socket
      @socket.on 'message', @_onMessage.bind this

     _send: (data) ->  @socket.emit 'message', data
     _respond: (data) -> @worker.emit 'message', data

     _onMessage: (e) ->
      data = e.data
      @_handleMessage data

##ServerSocketPort class

    class ServerSocketPort extends Port
     constructor: (server) ->
      super()
      @server = server
      @server.on 'connection', @_onConnection.bind this

     _onConnection: (socket) ->
      new SocketPort handlers: @handlers, socket

##AJAX class

    class AjaxHttpPort extends Port
     constructor: (options) ->
      super()
      @host = options.host ? 'localhost'
      @port = options.port ? 80
      @path = options.path ? '/'

     isStreaming: false

     _onRequest: (xhr) ->
      return unless xhr.readyState is 4
      status = xhr.status
      if ((not status and xhr.responseText?) or
          (status >= 200 and status < 300) or
          (status is 304))
       try
        jsonData = JSON.parse xhr.responseText
       catch e
        @onerror 'ParseError', e
        return

       @_handleMessage jsonData, xhr: xhr
      else
       @onerror 'xhr error'

     _respond: (data, options) ->
      throw new Error 'AJAX cannot respond'

     _send: (data) ->
      data = JSON.stringify data
      xhr = new XMLHttpRequest
      xhr.open 'POST', "http://#{@host}:#{@port}#{@path}"
      xhr.onreadystatechange = =>
       @_onRequest xhr
      xhr.setRequestHeader 'Accept', 'application/json'
      #xhr.setRequestHeader 'Content-Type', 'application/json'
      xhr.send data

     _handleResponse: (data, options) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       throw new Error "Response without call: #{data.id}"
      call = @callsCache[data.id]
      call.handle data.data, data
      if POLL_TYPE[data.status]?
       params =
        type: 'poll'
        id: call.id
       params[k] = v for k, v of call.options
       @_send params

##NodeHttpPort class

    class NodeHttpPort extends Port
     constructor: (options, http) ->
      super()
      @host = options.host ? 'localhost'
      @port = options.port ? 80
      @path = options.path ? '/'
      @http = http
      @_createHttpOptions()

     isStreaming: false

     _createHttpOptions: ->
      @httpOptions =
       hostname: @host
       port: @port
       path: @path
       method: 'POST'
       headers:
        accept: 'application/json'
       'content-type': 'application/json'

     _onRequest: (res) ->
      data = ''
      #console.log 'STATUS: ' + res.statusCode
      #console.log 'HEADERS: ' + JSON.stringify res.headers
      res.setEncoding 'utf8'
      res.on 'data', (chunk) ->
       data += chunk
      #console.log 'result', res
      res.on 'end', =>
       try
        jsonData = JSON.parse data
       catch e
        console.log 'ParseError', e
        return

       @_handleMessage jsonData, response: res

     _respond: (data, options) ->
      data = JSON.stringify data
      res = options.response
      res.setHeader 'content-length', data.length
      res.write data
      res.end()

     _send: (data) ->
      data = JSON.stringify data
      options = @httpOptions
      options.headers['content-length'] = data.length

      req = @http.request options, @_onRequest.bind this
      delete options.headers['content-length']
      req.on 'error', (e) ->
       console.log 'error', e

      req.write data
      req.end()

     _handleResponse: (data, options) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       throw new Error "Response without call: #{data.id}"
      call = @callsCache[data.id]
      call.handle data.data, data
      if POLL_TYPE[data.status]?
       params =
        type: 'poll'
        id: call.id
       params[k] = v for k, v of call.options
       @_send params

##NodeHttpServerPort class

    class NodeHttpServerPort extends Port
     constructor: (options, http) ->
      super()
      @port = options.port
      @http = http

     isStreaming: false

     _onRequest: (req, res) ->
      data = ''
      res.setHeader 'content-type', 'application/json'

      req.on 'data', (chunk) ->
       data += chunk
      req.on 'end', =>
       try
        jsonData = JSON.parse data
       catch e
        console.log 'ParseError', e, data
        return

       @_handleMessage jsonData, response: res

     _respond: (data, options) ->
      data = JSON.stringify data
      res = options.response
      res.setHeader 'content-length', data.length
      res.write data
      res.end()

     listen: ->
      @server = @http.createServer @_onRequest.bind this
      @server.listen @port

     _handlePoll: (data, options) ->
      for f in @wrappers.handleCall
       return unless f.apply this, arguments
      if not @responses[data.id]?
       throw new Error "Poll without response: #{data.id}"
      @responses[data.id].setOptions options

#IO Module

    IO =
     addPort: (name, port) ->
      IO[name] = port

     ports:
      WorkerPort: WorkerPort
      SocketPort: SocketPort
      AjaxHttpPort: AjaxHttpPort
      NodeHttpPort: NodeHttpPort
      NodeHttpServerPort: NodeHttpServerPort
      ServerSocketPort: ServerSocketPort

     setup_____: (options) ->
      options.workers ?= []
      switch options.platform
       when 'ui'
        for worker in options.workers
         IO.addPort worker.name, new WorkerPort worker.js
        for socket in options.sockets
         IO.addPort socket.name, new SocketPort socket.url
       when 'node'
        for socket in options.listenSockets
         IO.addPort socket.name, new SocketListenPort socket.port
        for socket in options.sockets
         IO.addPort socket.name, new SocketPort socket.url
       when 'worker'
        IO.addPort 'UI', new WorkerListenPort _self

    if exports?
     module.exports = IO
    else
     _self.IO = IO

    _self = this

    POLL_TYPE =
     progress: true

##Response class

    class Response
     constructor: (data, port, options) ->
      @id = data.id
      @port = port
      @options = options
      @queue = []
      @fresh = true

     progress: (progress, data) ->
      @queue.push method: 'progress', data: data, options: {progress: progress}
      @_handleQueue()

     success: (data) ->
      @queue.push method: 'success', data: data, options: {}
      @_handleQueue()

     fail: (data) ->
      @queue.push method: 'fail', data: data, options: {}
      @_handleQueue()

     setOptions: (options) ->
      @options = options
      @fresh = true
      @_handleQueue()

     _handleQueue: ->
      return unless @queue.length > 0

      d = @queue[0]

      if @port.isStreaming
       @port.respond this, d.method, d.data, d.options, @options
       @queue.shift()
      else if @fresh
       @port.respond this, d.method, d.data, d.options, @options
       @queue.shift()
       @fresh = false

##Call class

    class Call
     constructor: (@id, @method, @data, @callbacks, @options) -> null

     handle: (data, options) ->
      if not @callbacks[options.status]?
       return if options.status is 'progress'
       throw new Error "No callback registered #{@method} #{options.status}"
      @callbacks[options.status] data, options

##Port base class

    class Port
     constructor: ->
      @handlers = {}
      @callsCache = {}
      @callsCounter = 0
      @id = parseInt Math.random() * 1000
      @responses = {}
      @wrappers =
       send: []
       respond: []
       handleCall: []
       handleResponse: []

     isStreaming: true

     onerror: (msg, options) ->
      console.log msg, options

     wrap: (wrapper) ->
      for key, f of wrapper
       if @wrappers[key]?
        @wrappers[key].push f
       else
        this[key] = f

###Send RPC call

     send: (method, data, callbacks, options = {}) ->
      if (typeof callbacks) is 'function'
       callbacks =
        success: callbacks

      for f in @wrappers.send
       return unless f.apply this, [method, data, callbacks, options]

      @_send @_createCall method, data, callbacks, options

###Respond to a RPC call

     respond: (response, status, data, options = {}, portOptions = {}) ->
      for f in @wrappers.respond
       return unless f.apply this, [response, status, data, options, portOptions]

      @_respond (@_createResponse response, status, data, options), portOptions
      if not POLL_TYPE[status]?
       delete @responses[response.id]

###Create Call object
This is a private function

     _createCall: (method, data, callbacks, options) ->
      call = new Call "#{@id}-#{@callsCounter}", method, data, callbacks, options
      @callsCounter++
      @callsCache[call.id] = call

      params =
       type: 'call'
       id: call.id
       method: call.method
       data: call.data

      params[k] = v for k, v of options
      return params

###Create Response object

     _createResponse: (response, status, data, options) ->
      params =
       type: 'response'
       id: response.id
       status: status
       data: data

      params[k] = v for k, v of options

      return params

###Add handler

     on: (method, callback) ->
      @handlers[method] = callback

###Handle incoming message

     _handleMessage: (data, options) ->
      switch data.type
       when 'response'
        @_handleResponse data, options
       when 'call'
        @_handleCall data, options
       when 'poll'
        @_handlePoll data, options

     _handleCall: (data, options) ->
      for f in @wrappers.handleCall
       return unless f.apply this, arguments
      if not @handlers[data.method]?
       throw new Error "Unknown method: #{data.method}"
      @responses[data.id] = new Response data, this, options
      @handlers[data.method] data.data, data, @responses[data.id]

     _handleResponse: (data, options) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       throw new Error "Response without call: #{data.id}"
      @callsCache[data.id].handle data.data, data

     _handlePoll: (data, options) ->
      throw new Error "Poll not implemented"

##WorkerPort class
Used for browser and worker

    class WorkerPort extends Port
     constructor: (worker) ->
      super()
      @worker = worker
      @worker.onmessage = @_onMessage.bind this
      @worker.onerror = @_onError.bind this

     _send: (data) ->  @worker.postMessage data
     _respond: (data) -> @worker.postMessage data

     _onMessage: (e) ->
      data = e.data
      @_handleMessage data

     _onError: (e) -> console.log e

##SocketPort class

    class SocketPort extends Port
     constructor: (socket) ->
      super()
      @socket = socket
      @socket.on 'message', @_onMessage.bind this

     _send: (data) ->  @socket.emit 'message', data
     _respond: (data) -> @worker.emit 'message', data

     _onMessage: (e) ->
      data = e.data
      @_handleMessage data

##ServerSocketPort class

    class ServerSocketPort extends Port
     constructor: (server) ->
      super()
      @server = server
      @server.on 'connection', @_onConnection.bind this

     _onConnection: (socket) ->
      new SocketPort handlers: @handlers, socket

##NodeHttpPort class

    class NodeHttpPort extends Port
     constructor: (options, http) ->
      super()
      @host = options.host ? 'localhost'
      @port = options.port ? 80
      @path = options.path ? '/'
      @http = http
      @_createHttpOptions()

     isStreaming: false

     _createHttpOptions: ->
      @httpOptions =
       hostname: @host
       port: @port
       path: @path
       method: 'POST'
       headers:
        accept: 'application/json'
       'content-type': 'application/json'

     _onRequest: (res) ->
      data = ''
      #console.log 'STATUS: ' + res.statusCode
      #console.log 'HEADERS: ' + JSON.stringify res.headers
      res.setEncoding 'utf8'
      res.on 'data', (chunk) ->
       data += chunk
      #console.log 'result', res
      res.on 'end', =>
       try
        jsonData = JSON.parse data
       catch e
        console.log 'ParseError', e
        return

       @_handleMessage jsonData, response: res

     _respond: (data, options) ->
      data = JSON.stringify data
      res = options.response
      res.setHeader 'content-length', data.length
      res.write data
      res.end()

     _send: (data) ->
      data = JSON.stringify data
      options = @httpOptions
      options.headers['content-length'] = data.length

      req = @http.request options, @_onRequest.bind this
      delete options.headers['content-length']
      req.on 'error', (e) ->
       console.log 'error', e

      req.write data
      req.end()

     _handleResponse: (data, options) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       throw new Error "Response without call: #{data.id}"
      call = @callsCache[data.id]
      call.handle data.data, data
      if POLL_TYPE[data.status]?
       params =
        type: 'poll'
        id: call.id
       params[k] = v for k, v of call.options
       @_send params

##NodeHttpServerPort class

    class NodeHttpServerPort extends Port
     constructor: (options, http) ->
      super()
      @port = options.port
      @http = http

     isStreaming: false

     _onRequest: (req, res) ->
      data = ''
      res.setHeader 'content-type', 'application/json'

      req.on 'data', (chunk) ->
       data += chunk
      req.on 'end', =>
       try
        jsonData = JSON.parse data
       catch e
        console.log 'ParseError', e
        return

       @_handleMessage jsonData, response: res

     _respond: (data, options) ->
      data = JSON.stringify data
      res = options.response
      res.setHeader 'content-length', data.length
      res.write data
      res.end()

     listen: ->
      @server = @http.createServer @_onRequest.bind this
      @server.listen @port

     _handlePoll: (data, options) ->
      for f in @wrappers.handleCall
       return unless f.apply this, arguments
      if not @responses[data.id]?
       throw new Error "Poll without response: #{data.id}"
      @responses[data.id].setOptions options

#IO Module

    IO =
     addPort: (name, port) ->
      IO[name] = port

     ports:
      WorkerPort: WorkerPort
      SocketPort: SocketPort
      NodeHttpPort: NodeHttpPort
      NodeHttpServerPort: NodeHttpServerPort
      ServerSocketPort: ServerSocketPort

     setup_____: (options) ->
      options.workers ?= []
      switch options.platform
       when 'ui'
        for worker in options.workers
         IO.addPort worker.name, new WorkerPort worker.js
        for socket in options.sockets
         IO.addPort socket.name, new SocketPort socket.url
       when 'node'
        for socket in options.listenSockets
         IO.addPort socket.name, new SocketListenPort socket.port
        for socket in options.sockets
         IO.addPort socket.name, new SocketPort socket.url
       when 'worker'
        IO.addPort 'UI', new WorkerListenPort _self

    if exports?
     module.exports = IO
    else
     _self.IO = IO