Fixed issue when counting impl/invo

Fixed issue when counting impl/invo

# Grunt build process - Dynamic documentation

    {exec} = require 'child_process'
    fs = require 'fs'
    path = require 'path'
    {Transform} = require 'stream'

    _ = require 'lodash'
    {LineStream} = require 'byline'
    glob = require 'grunt/node_modules/glob'
    Promise = require 'bluebird'

Implement a Transform stream to accumulate hook invocations from a source file.

    class HookInvocations extends Transform

      constructor: ->
        super

        @list = []

      _transform: (chunk, encoding, done) ->
        line = chunk.toString('utf8')
        if matches = line.match /^\#\#\#\# Invoke hook `([^`]+)`/
          @list.push matches[1]

        done()

Implement a Transform stream to accumulate hook implementations from a source
file.

    class HookImplementations extends Transform

      constructor: ->
        super

        @list = []

      _transform: (chunk, encoding, done) ->
        line = chunk.toString('utf8')
        if matches = line.match /^\#\#\#\# Implements hook `([^`]+)`/
          @list.push matches[1]

        done()

Implement a Transform stream fo accumulate TODOs from a source file. Also
caches lines to be able to build context around each TODO item.

    class Todos extends Transform

      @context = 4

      constructor: ->
        super

        @lines = []
        @todos = []

      _transform: (chunk, encoding, done) ->
        @lines.push line = chunk.toString('utf8')
        if matches = line.match /^\#\#\#\#\#\# TODO/
          @todos.push @lines.length - 1

        done()

      withContext: ->

        for todo in @todos

          start = Math.max 0, todo - Todos.context
          end = Math.min @lines.length - 1, todo + Todos.context

          index: todo
          lines: @lines.slice start, end

Gather all source files.

    _allSourceFiles = ->
      new Promise (resolve, reject) ->
        glob(
          '{{client,custom,packages,server}/**/*.litcoffee,*.litcoffee}'
          (error, files) ->
            return reject error if error?
            resolve files
        )

Generate an HTML ID from a string.

    _idFromString = (string) -> string.replace(
      /\//g, ''
    ).replace(
      /[^0-9A-Za-z-]+/g, '-'
    ).replace(
      /\-+/g, '-'
    ).toLowerCase()

Get the source path from a filename. This removes the extension and any /index
portion from the end of the filename.

    _sourcePath = (filename) ->

        dirname = path.dirname filename
        if dirname is '.' then dirname = '' else dirname += '/'
        extname = path.extname filename
        filename = "#{dirname}#{path.basename filename, extname}"

        parts = filename.split '/'
        parts.pop() if parts[parts.length - 1] is 'index'
        return parts.join '/'

Collate a list of files by type (client or server).

    _collateFilesByType = (files) ->

      client = []
      server = []

      for file in files
        parts = file.split '/'

        if parts[0] is 'client'
          client.push file
        else if parts[0] is 'custom' and parts[2] is 'client'
          client.push file
        else if parts[0] is 'packages' and parts[2] is 'client'
          client.push file
        else
          server.push file

      client: client, server: server

Gather statistics for all files.

    fileStatsListPromise = _allSourceFiles().then (allFiles) ->

      allFilesPromises = for type, files of _collateFilesByType allFiles

        typePromises = for file in files

          do (type, file) -> new Promise (resolve, reject) ->

            fstream = fs.createReadStream file
            fstream.pipe lineStream = new LineStream keepEmptyLines: true

Pass all files through the Transform list to parse out relevant information.

            lineStream.pipe hookImplementations = new HookImplementations()
            lineStream.pipe hookInvocations = new HookInvocations()
            lineStream.pipe todos = new Todos()

            fstream.on 'error', reject

            fstream.on 'end', ->

Include all information from Transform streams in the statistics.

              resolve(
                type: type
                file: file
                implementations: hookImplementations.list
                invocations: hookInvocations.list
                todos: todos.withContext()
              )

        Promise.all typePromises

      Promise.all(allFilesPromises).then (fileStatsLists) ->
        _.flatten fileStatsLists

Massage the statistics to help rendering the hooks page.

    fileStatsListPromise.then((fileStatsList) ->

      hooksIndex = {}

      indexes = {}
      keys = ['implementations', 'invocations']

      for fileStats in fileStatsList

        parts = fileStats.file.split '/'
        for remove in ['client', 'packages']
          if ~(index = parts.indexOf remove)
            parts.splice index, 1
        mergeFile = parts.join '/'

        for key in keys
          indexes[key] ?= {}

          for hook in fileStats[key]
            indexes[key][hook] ?= {}

            indexes[key][hook][mergeFile] ?= fullName: fileStats.file
            (indexes[key][hook][mergeFile].types ?= []).push fileStats.type

            hooksIndex[hook] = true

      for key in keys
        for hook, stats of indexes[key]
          for file of stats
            stats[file].types = _.unique stats[file].types ? []

      hooks = (hook for hook of hooksIndex).sort()

      hookFiles = {}
      for hook in hooks
        hookFiles[hook] = try
          fs.readFileSync "docs/hook/#{hook}.md", 'utf8'
        catch error
          ''

      O =
        hookFiles: hookFiles
        hooks: hooks

      O[key] = indexes[key] for key in keys

      return O

Render the hooks page.

    ).then((O) ->

      {hookFiles, hooks} = O

      keys = ['implementation', 'invocation']

      wordingFor =
        implementation: 'implements'
        invocation: 'invoke'

      render = fs.readFileSync 'docs/hooks.template.md', 'utf8'

      for hook in hooks

        render += "## #{hook}\n\n"
        render += hookFiles[hook] + '\n\n' if hookFiles[hook]

        for key in keys
          pluralKey = "#{key}s"

          if O[pluralKey][hook]?

            count = 0
            for file, {types} of O[pluralKey][hook]
              count += types.length

            render += "!!! note \"#{count} #{key}"
            render += 's' if count > 1
            render += '"\n'

            instances = for file, {fullName, types} of O[pluralKey][hook]

Remove packages and file part.

              parts = fullName.split '/'
              parts.shift()
              parts.pop()
              packageName = parts.join '/'

              types = types.map (type) ->
                packageNameParts = packageName.split '/'
                packageNameParts.pop() if packageNameParts[packageNameParts.length - 1] isnt type
                "    * [#{_sourcePath file} (#{type})](packages/##{_idFromString packageNameParts.join '/'}) — [#{key}](source/#{_sourcePath fullName}##{wordingFor[key]}-hook-#{_idFromString hook}) \n"
              types.join ''

            render += instances.join ''

            render += '\n\n'

      new Promise (resolve, reject) ->
        fs.writeFile 'docs/hooks.md', render, (error) ->
          return reject error if error?
          resolve()

    ).done()

Render the TODOs page.

    fileStatsListPromise.then((fileStatsList) ->

      render = fs.readFileSync 'docs/todos.template.md', 'utf8'

      for fileStats in fileStatsList

Keep track of used IDs, it will be necessary to link to the correct location
hash in the case of multiple TODO items with the same wording.

        idMap = {}

        for todo in fileStats.todos

          render += '\n'

          id = ''
          for line, index in todo.lines
            render += '>'

If this is the line with the TODO, parse the ID from the TODO item text, and
render it as h2 (TODO are h6) to increase visibility.

            if index is Todos.context
              id = _idFromString(line).slice 1, -1

              render += line.slice 4
            else
              render += " #{line}"
            render += '\n'

          render += '\n'

          filename = _sourcePath fileStats.file

Keep track of ID usage and modify the location hash for subsequent uses.

          if idMap[id]?
            idMap[id] += 1
            id += "_#{idMap[id]}"
          else
            idMap[id] = 0

          render += "###### the above found in [#{fileStats.file}:#{todo.index}](source/#{filename}##{id})\n"

      new Promise (resolve, reject) ->
        fs.writeFile 'docs/todos.md', render, (error) ->
          return reject error if error?
          resolve()

    ).done()

Add all the source files to a generated mkdocs.yml

    _allSourceFiles().then (files) ->

      yml = fs.readFileSync 'docs/mkdocs.template.yml', 'utf8'

      for file in files
        parts = file.split '/'

        for i in [0...parts.length]
          try
            fs.mkdirSync "docs/source/#{parts.slice(0, i).join '/'}"
          catch error

        fs.createReadStream(file).pipe(
          fs.createWriteStream("docs/source/#{file}")
        )

Add them under the 'Source code' path.

        yml += "- [source/#{file}, 'Source code', '#{file}']\n"

      fs.writeFileSync 'mkdocs.yml', yml

      return

Render the packages page.

    fileStatsListPromise.then((fileStatsList) ->

Sort by package name first.

      newList = []

      for fileStats in fileStatsList

        parts = fileStats.file.split '/'
        continue unless ~['custom', 'packages'].indexOf parts[0]

        sourcePath = _sourcePath fileStats.file

        parts = sourcePath.split '/'
        pkg = parts.join '/'
        fileStats.pkg = pkg.split('/').slice(1).join '/'

        newList.push fileStats

      newList.sort (l, r) ->
        return -1 if l.type is 'client' and r.type is 'server'
        return 1 if l.type is 'server' and r.type is 'client'
        if l.pkg < r.pkg then -1 else if l.pkg > r.pkg then 1 else 0

    ).then((fileStatsList) ->

      render = fs.readFileSync 'docs/packages.template.md', 'utf8'
      render += '\n'

      type = null

      for fileStats in fileStatsList

        if fileStats.type isnt type
          type = fileStats.type

          render += if type is 'client'
            '## Client-side'
          else
            '## Server-side'

          render += '\n\n'

        pkgParts = fileStats.pkg.split '/'
        isSubpackage = pkgParts.length isnt 1 and pkgParts.pop() isnt 'client'

Link to the package.

        sourcePath = _sourcePath fileStats.file

        if isSubpackage
          render += '> '
          parts = fileStats.pkg.split '/'
          parentPkg = parts.shift()
          subPkg = parts.join '/'
          render += "## [#{parentPkg}/#{subPkg}](source/#{sourcePath})"
        else
          render += "## [#{fileStats.pkg}](source/#{sourcePath})"

Naively parse out the file description. It must be wrapped in asterisks, i.e.
italicized in markdown.

###### TODO: This 'chunk' parsing should be done with a Transform like the others.

        data = fs.readFileSync fileStats.file, 'utf8'
        chunks = data.split '\n\n'

        title = chunks[0]
        if 35 is title.charCodeAt 0
          render += '\n\n'
          render += '> ' if isSubpackage
          render += "#{title.substr 2}"

        render += '\n\n'

        description = chunks[1]
        if 42 is description.charCodeAt 0 and 42 is description.charCodeAt description.length - 1
          render += '> ' if isSubpackage
          render += "#{description}\n\n"

        if fileStats.implementations.length > 0
          render += '> ' if isSubpackage
          render += '!!! note "Implements hooks"\n'
          render += fileStats.implementations.map((hook) ->
            "    * [#{hook}](hooks/##{_idFromString hook}) — [implementation](source/#{sourcePath}#implements-hook-#{hook.toLowerCase()})\n"
          ).join ''
          render += '\n\n'

        if fileStats.invocations.length > 0
          render += '> ' if isSubpackage
          render += '!!! note "Invokes hooks"\n'
          render += fileStats.invocations.map((hook) ->
            "    * [#{hook}](hooks/##{_idFromString hook}) — [invocation](source/#{sourcePath}#invoke-hook-#{hook.toLowerCase()})\n"
          ).join ''
          render += '\n\n'

###### TODO: We should do some preprocessing hre with a transform, namely linking the hook headers to their respective documentation.

      fs.writeFileSync 'docs/packages.md', render

    )

# Grunt build process - Dynamic documentation

    {exec} = require 'child_process'
    fs = require 'fs'
    path = require 'path'
    {Transform} = require 'stream'

    _ = require 'lodash'
    {LineStream} = require 'byline'
    glob = require 'grunt/node_modules/glob'
    Promise = require 'bluebird'

Implement a Transform stream to accumulate hook invocations from a source file.

    class HookInvocations extends Transform

      constructor: ->
        super

        @list = []

      _transform: (chunk, encoding, done) ->
        line = chunk.toString('utf8')
        if matches = line.match /^\#\#\#\# Invoke hook `([^`]+)`/
          @list.push matches[1]

        done()

Implement a Transform stream to accumulate hook implementations from a source
file.

    class HookImplementations extends Transform

      constructor: ->
        super

        @list = []

      _transform: (chunk, encoding, done) ->
        line = chunk.toString('utf8')
        if matches = line.match /^\#\#\#\# Implements hook `([^`]+)`/
          @list.push matches[1]

        done()

Implement a Transform stream fo accumulate TODOs from a source file. Also
caches lines to be able to build context around each TODO item.

    class Todos extends Transform

      @context = 4

      constructor: ->
        super

        @lines = []
        @todos = []

      _transform: (chunk, encoding, done) ->
        @lines.push line = chunk.toString('utf8')
        if matches = line.match /^\#\#\#\#\#\# TODO/
          @todos.push @lines.length - 1

        done()

      withContext: ->

        for todo in @todos

          start = Math.max 0, todo - Todos.context
          end = Math.min @lines.length - 1, todo + Todos.context

          index: todo
          lines: @lines.slice start, end

Gather all source files.

    _allSourceFiles = ->
      new Promise (resolve, reject) ->
        glob(
          '{{client,custom,packages,server}/**/*.litcoffee,*.litcoffee}'
          (error, files) ->
            return reject error if error?
            resolve files
        )

Generate an HTML ID from a string.

    _idFromString = (string) -> string.replace(
      /\//g, ''
    ).replace(
      /[^0-9A-Za-z-]+/g, '-'
    ).replace(
      /\-+/g, '-'
    ).toLowerCase()

Get the source path from a filename. This removes the extension and any /index
portion from the end of the filename.

    _sourcePath = (filename) ->

        dirname = path.dirname filename
        if dirname is '.' then dirname = '' else dirname += '/'
        extname = path.extname filename
        filename = "#{dirname}#{path.basename filename, extname}"

        parts = filename.split '/'
        parts.pop() if parts[parts.length - 1] is 'index'
        return parts.join '/'

Collate a list of files by type (client or server).

    _collateFilesByType = (files) ->

      client = []
      server = []

      for file in files
        parts = file.split '/'

        if parts[0] is 'client'
          client.push file
        else if parts[0] is 'custom' and parts[2] is 'client'
          client.push file
        else if parts[0] is 'packages' and parts[2] is 'client'
          client.push file
        else
          server.push file

      client: client, server: server

Gather statistics for all files.

    fileStatsListPromise = _allSourceFiles().then (allFiles) ->

      allFilesPromises = for type, files of _collateFilesByType allFiles

        typePromises = for file in files

          do (type, file) -> new Promise (resolve, reject) ->

            fstream = fs.createReadStream file
            fstream.pipe lineStream = new LineStream keepEmptyLines: true

Pass all files through the Transform list to parse out relevant information.

            lineStream.pipe hookImplementations = new HookImplementations()
            lineStream.pipe hookInvocations = new HookInvocations()
            lineStream.pipe todos = new Todos()

            fstream.on 'error', reject

            fstream.on 'end', ->

Include all information from Transform streams in the statistics.

              resolve(
                type: type
                file: file
                implementations: hookImplementations.list
                invocations: hookInvocations.list
                todos: todos.withContext()
              )

        Promise.all typePromises

      Promise.all(allFilesPromises).then (fileStatsLists) ->
        _.flatten fileStatsLists

Massage the statistics to help rendering the hooks page.

    fileStatsListPromise.then((fileStatsList) ->

      hooksIndex = {}

      indexes = {}
      keys = ['implementations', 'invocations']

      for fileStats in fileStatsList

        parts = fileStats.file.split '/'
        for remove in ['client', 'packages']
          if ~(index = parts.indexOf remove)
            parts.splice index, 1
        mergeFile = parts.join '/'

        for key in keys
          indexes[key] ?= {}

          for hook in fileStats[key]
            indexes[key][hook] ?= {}

            indexes[key][hook][mergeFile] ?= fullName: fileStats.file
            (indexes[key][hook][mergeFile].types ?= []).push fileStats.type

            hooksIndex[hook] = true

      for key in keys
        for hook, stats of indexes[key]
          for file of stats
            stats[file].types = _.unique stats[file].types ? []

      hooks = (hook for hook of hooksIndex).sort()

      hookFiles = {}
      for hook in hooks
        hookFiles[hook] = try
          fs.readFileSync "docs/hook/#{hook}.md", 'utf8'
        catch error
          ''

      O =
        hookFiles: hookFiles
        hooks: hooks

      O[key] = indexes[key] for key in keys

      return O

Render the hooks page.

    ).then((O) ->

      {hookFiles, hooks} = O

      keys = ['implementation', 'invocation']

      wordingFor =
        implementation: 'implements'
        invocation: 'invoke'

      render = fs.readFileSync 'docs/hooks.template.md', 'utf8'

      for hook in hooks

        render += "## #{hook}\n\n"
        render += hookFiles[hook] + '\n\n' if hookFiles[hook]

        for key in keys
          pluralKey = "#{key}s"

          if O[pluralKey][hook]?

            count = Object.keys(O[pluralKey][hook]).length

            render += "!!! note \"#{count} #{key}"
            render += 's' if count > 1
            render += '"\n'

            instances = for file, {fullName, types} of O[pluralKey][hook]

Remove packages and file part.

              parts = fullName.split '/'
              parts.shift()
              parts.pop()
              packageName = parts.join '/'

              types = types.map (type) ->
                packageNameParts = packageName.split '/'
                packageNameParts.pop() if packageNameParts[packageNameParts.length - 1] isnt type
                "    * [#{_sourcePath file} (#{type})](packages/##{_idFromString packageNameParts.join '/'}) — [#{key}](source/#{_sourcePath fullName}##{wordingFor[key]}-hook-#{_idFromString hook}) \n"
              types.join ''

            render += instances.join ''

            render += '\n\n'

      new Promise (resolve, reject) ->
        fs.writeFile 'docs/hooks.md', render, (error) ->
          return reject error if error?
          resolve()

    ).done()

Render the TODOs page.

    fileStatsListPromise.then((fileStatsList) ->

      render = fs.readFileSync 'docs/todos.template.md', 'utf8'

      for fileStats in fileStatsList

Keep track of used IDs, it will be necessary to link to the correct location
hash in the case of multiple TODO items with the same wording.

        idMap = {}

        for todo in fileStats.todos

          render += '\n'

          id = ''
          for line, index in todo.lines
            render += '>'

If this is the line with the TODO, parse the ID from the TODO item text, and
render it as h2 (TODO are h6) to increase visibility.

            if index is Todos.context
              id = _idFromString(line).slice 1, -1

              render += line.slice 4
            else
              render += " #{line}"
            render += '\n'

          render += '\n'

          filename = _sourcePath fileStats.file

Keep track of ID usage and modify the location hash for subsequent uses.

          if idMap[id]?
            idMap[id] += 1
            id += "_#{idMap[id]}"
          else
            idMap[id] = 0

          render += "###### the above found in [#{fileStats.file}:#{todo.index}](source/#{filename}##{id})\n"

      new Promise (resolve, reject) ->
        fs.writeFile 'docs/todos.md', render, (error) ->
          return reject error if error?
          resolve()

    ).done()

Add all the source files to a generated mkdocs.yml

    _allSourceFiles().then (files) ->

      yml = fs.readFileSync 'docs/mkdocs.template.yml', 'utf8'

      for file in files
        parts = file.split '/'

        for i in [0...parts.length]
          try
            fs.mkdirSync "docs/source/#{parts.slice(0, i).join '/'}"
          catch error

        fs.createReadStream(file).pipe(
          fs.createWriteStream("docs/source/#{file}")
        )

Add them under the 'Source code' path.

        yml += "- [source/#{file}, 'Source code', '#{file}']\n"

      fs.writeFileSync 'mkdocs.yml', yml

      return

Render the packages page.

    fileStatsListPromise.then((fileStatsList) ->

Sort by package name first.

      newList = []

      for fileStats in fileStatsList

        parts = fileStats.file.split '/'
        continue unless ~['custom', 'packages'].indexOf parts[0]

        sourcePath = _sourcePath fileStats.file

        parts = sourcePath.split '/'
        # parts.pop() if parts[parts.length - 1] is 'client'
        pkg = parts.join '/'
        fileStats.pkg = pkg.split('/').slice(1).join '/'

        newList.push fileStats

      newList.sort (l, r) ->
        return -1 if l.type is 'client' and r.type is 'server'
        return 1 if l.type is 'server' and r.type is 'client'
        if l.pkg < r.pkg then -1 else if l.pkg > r.pkg then 1 else 0

    ).then((fileStatsList) ->

      render = fs.readFileSync 'docs/packages.template.md', 'utf8'
      render += '\n'

      type = null

      for fileStats in fileStatsList

        if fileStats.type isnt type
          type = fileStats.type

          render += if type is 'client'
            '## Client-side'
          else
            '## Server-side'

          render += '\n\n'

        pkgParts = fileStats.pkg.split '/'
        isSubpackage = pkgParts.length isnt 1 and pkgParts.pop() isnt 'client'

Link to the package.

        sourcePath = _sourcePath fileStats.file

        if isSubpackage
          render += '> '
          parts = fileStats.pkg.split '/'
          parentPkg = parts.shift()
          subPkg = parts.join '/'
          render += "## [#{parentPkg}/#{subPkg}](source/#{sourcePath})"
        else
          render += "## [#{fileStats.pkg}](source/#{sourcePath})"

Naively parse out the file description. It must be wrapped in asterisks, i.e.
italicized in markdown.

###### TODO: This 'chunk' parsing should be done with a Transform like the others.

        data = fs.readFileSync fileStats.file, 'utf8'
        chunks = data.split '\n\n'

        title = chunks[0]
        if 35 is title.charCodeAt 0
          render += '\n\n'
          render += '> ' if isSubpackage
          render += "#{title.substr 2}"

        render += '\n\n'

        description = chunks[1]
        if 42 is description.charCodeAt 0 and 42 is description.charCodeAt description.length - 1
          render += '> ' if isSubpackage
          render += "#{description}\n\n"

        if fileStats.implementations.length > 0
          render += '> ' if isSubpackage
          render += '!!! note "Implements hooks"\n'
          render += fileStats.implementations.map((hook) ->
            "    * [#{hook}](hooks/##{_idFromString hook}) — [implementation](source/#{sourcePath}#implements-hook-#{hook.toLowerCase()})\n"
          ).join ''
          render += '\n\n'

        if fileStats.invocations.length > 0
          render += '> ' if isSubpackage
          render += '!!! note "Invokes hooks"\n'
          render += fileStats.invocations.map((hook) ->
            "    * [#{hook}](hooks/##{_idFromString hook}) — [invocation](source/#{sourcePath}#invoke-hook-#{hook.toLowerCase()})\n"
          ).join ''
          render += '\n\n'

###### TODO: We should do some preprocessing hre with a transform, namely linking the hook headers to their respective documentation.

      fs.writeFileSync 'docs/packages.md', render

    )


listen

listen

#Weya.Base.js

    @Weya = {} unless @Weya?
    Weya = @Weya

##Weya.Base
Introduces class level function initialize and include.

    class Base
     _initialize: []
     on: {}

Extend Events

     @extend: ->
      events = @::on
      @::on = {}
      @::on[k] = v for k, v of events

Add event listeners

     @listen: (name, func) ->
      @::on[name] = func

Getter

     @get: (name, func) ->
      @::__defineGetter__ name, func

Setter

     @set: (name, func) ->
      @::__defineSetter__ name, func

     constructor: ->
      @_init.apply @, arguments

####Register initialize functions.
All initializer funcitons in subclasses will be called with the constructor
arguments.

     @initialize: (func) ->
      @::_initialize = @::_initialize.slice()
      @::_initialize.push func

     _init: ->
      for init in @_initialize
       init.apply @, arguments

      return

####Include objects.
You can include objects by registering them with @include. This tries to
solve the problem of single inheritence.

     @include: (obj) ->
      for k, v of obj
       switch k
        when 'initialize'
         @::_initialize.push v
        when 'on'
         for event, listener of v
          @::on[event] = listener
        else
         @::[k] = v

     @initialize ->
      events = @on
      @on = {}
      for k, v of events
       @on[k] = v.bind this

    Weya.Base = Base

    if module?
     module.exports = Weya.Base

#Weya.Base.js

    @Weya = {} unless @Weya?
    Weya = @Weya

##Weya.Base
Introduces class level function initialize and include.

    class Base
     _initialize: []
     on: {}

     @extend: ->
      @::_initialize = @::_initialize.slice()
      events = @::on
      @::on = {}
      @::on[k] = v for k, v of events

     @get: (name, func) ->
      @::__defineGetter__ name, func

     @set: (name, func) ->
      @::__defineSetter__ name, func

     constructor: ->
      @_init.apply @, arguments

####Register initialize functions.
All initializer funcitons in subclasses will be called with the constructor
arguments.

     @initialize: (func) ->
      inits = @::_initialize
      @::_initialize = []
      for v in inits
       @::_initialize.push v
      @::_initialize.push func

     _init: ->
      for init in @_initialize
       init.apply @, arguments

      return

####Include objects.
You can include objects by registering them with @include. This tries to
solve the problem of single inheritence.

     @include: (obj) ->
      for k, v of obj
       switch k
        when 'initialize'
         @::_initialize.push v
        when 'on'
         for event, listener of v
          @::on[event] = listener
        else
         @::[k] = v

     @initialize ->
      events = @on
      @on = {}
      for k, v of events
       @on[k] = v.bind this

    Weya.Base = Base

    if module?
     module.exports = Weya.Base


spotfix

spotfix

## [RootState](#root-state)

    class RootState extends State

      { VIRTUAL, ABSTRACT, CONCLUSIVE, FINAL } = this
      { VIA_NONE, VIA_PROTO } = this

      DEFAULT_OPTIONS = {}

### [Constructor](#root-state--constructor)

      constructor: ( owner, expression, options ) ->
        @owner = owner or = {}
        unless expression instanceof StateExpression
          expression = new StateExpression expression
        if options?
        then if typeof options is 'string' then options = initialState: options
        else options = DEFAULT_OPTIONS

Assign to `owner` an **accessor** to its state implementation.

        @accessorName = accessorName = options.name or 'state'
        owner[ accessorName ] = createAccessor owner, accessorName, this

A root state’s `name` is by definition the empty-string.

        super owner, @name = '', expression

Determine the initial state, and set the `current` state to that.

        current = if options.initialState?
        then this.query options.initialState
        else this.initialSubstate() or this

The initial state may be `abstract`, in which case currency must be forwarded
to its default `concrete` substate.

        if current.attributes & ABSTRACT
          current = current.defaultSubstate() or current

The previous redirections may have left `current` pointing to a protostate, in
which case a virtual state must be created in `this` root’s tree.

        if current.root isnt this then current = current.virtualize this

The authoritative property reference to the owner’s `current` state.

        @_current = current

While a `Transition` is underway, `_current` will be a reference to that
transition. To further distinguish this condition, an identical reference is
also held at `_transition`.

        @_transition = null

### [Private functions](#root-state--private)

#### [createAccessor](#root-state--private--create-accessor)

Returns the `accessor` function that will serve as an owner object’s interface
to its state implementation.

      createAccessor = ( owner, name, root ) ->

        accessor = ( input, args... ) ->
          current = root._current

          if this is owner
            return current unless input?
            return current.change input.call this if typeof input is 'function'
            if typeof input is 'string' and
                ( match = input.match rxTransitionArrow ) and
                  method = transitionArrowMethods[ match[1] ]
              return if args.length
              then current[ method ].apply current, [ match[2] ].concat args
              else current[ method ] match[2]
            return current.query.apply current, arguments

Calling the accessor of a prototype means that `this` requires its own accessor
and root state. Creating a new `RootState` will have the desired side-effect of
also creating the object’s new accessor, to which the call is then forwarded.

          else if owner.isPrototypeOf(this) and not O.hasOwn.call this, name
            new RootState this, null, { name, initialState: current.path() }
            return @[ name ].apply this, arguments

        accessor.isAccessor = true

        if O.env.debug
          accessor.toString = ->
            "[accessor] -> #{ root._current.path() }"

        accessor

#### [evaluateGuard](#root-state--private--evaluate-guard)

Returns the boolean result of a `guard` function in the `context` of a `State`,
as evaluated `against` another `State`. Defaults to `true` if no guard exists.

      evaluateGuard = ( context, guard, against ) ->
        guard = context.guard guard if typeof guard is 'string'
        return true unless guard

        args = O.slice.call arguments, 1
        for own key, value of guard
          valueIsFn = typeof value is 'function'
          selectors = O.trim( key ).split /\s*,+\s*/
          for selector in selectors when context.query selector, against
            result = if valueIsFn then value.apply context, args else value
            break
          break unless result
        !!result

#### [getTransitionExpression](#root-state--private--get-transition-expression)

Finds the appropriate transition expression for the given `target` and `origin`
states. If no matching transitions are defined in either state or any of their
ancestors, a generic actionless transition expression for the pair is returned.

      getTransitionExpression: do ->

        search = ( target, origin, subject, ceiling ) ->
          while subject and subject isnt ceiling
            for own key, expr of subject.transitions()
              return expr if (
                not ( guards = expr.guards ) or (
                  not ( admit = guards.admit ) or
                  O.isEmpty( admit ) or
                  evaluateGuard.call origin, admit, target, origin
                ) and (
                  not ( release = guards.release ) or
                  O.isEmpty( release ) or
                  evaluateGuard.call target, release, origin, target
                )
              ) and (
                if expr.target then subject.query expr.target, target
                else subject is target
              ) and ( not expr.origin or subject.query expr.origin, origin )
            break unless ceiling?
            subject = subject.superstate

        ( target, origin = @_current ) ->
          ( search target, origin, target ) or
          ( search target, origin, origin unless origin is target ) or
          ( search target, origin, target.superstate, @root ) or
          ( search target, origin, @root ) or
          ( unless target.isIn origin
            search target, origin, origin.superstate, origin.common target ) or
          new TransitionExpression

### [Prototype methods](#root-state--prototype-methods)

#### [change](#root-state--prototype--change)

Attempts to execute a state transition. Handles asynchronous transitions,
generation of appropriate events, and construction of any necessary temporary
virtual states. Respects guards supplied in both the origin and `target`
states.

The `target` parameter may be either a `State` object within the tree of this
`RootState`, or a string that resolves to a likewise targetable `State` when
evaluated from the context of the most recently current state.

The `options` parameter is an optional map that may include:

  * `args` : `Array` — arguments to be passed to a transition’s `action`.
  * `success` : `Function` — callback to be executed upon successful completion
    of the transition.
  * `failure` : `Function` — callback to be executed if the transition
    attempt is blocked by a guard.

      change: ( target, options ) ->
        { root, owner } = this
        current = @_current
        transition = @_transition

The `origin` is defined as the controller’s most recently current state that is
not a [`Transition`][].

        origin = transition?.origin or current

Departures are not allowed from a state that is `final`.

        return null if origin.attributes & FINAL

Ensure that `target` is a valid `State`.

        unless target instanceof State
          target = if target then origin.query target else root
        return null unless target
        targetOwner = target.owner
        return null if owner isnt targetOwner and
          not targetOwner.isPrototypeOf owner

Resolve `options` to an object if necessary.

        unless options then options = DEFAULT_OPTIONS
        else if O.isArray options then options = args: options

A transition cannot target an abstract state directly, so `target` must be
reassigned to the appropriate concrete substate.

        while target.attributes & ABSTRACT
          return null unless target = target.defaultSubstate()

If any guards are in place for the given `origin` and `target`, both of those
states must consent to the transition.

        unless options.forced
          released = evaluateGuard origin, 'release', target
          admitted = evaluateGuard target, 'admit', origin
          unless released and admitted
            options.failure?.call? this
            return null

If `target` is a protostate, then the protostate must be virtualized locally
and `target` must be reassigned to the new virtual state.

        target = target.virtualize this unless target?.root is root

A reference is held to the previously current state, or abortive transition.

        source = current

The upcoming transition will start from its `source` and proceed within the
`domain` of the least common ancestor between `source` and `target`.

        domain = source.common target

Conclusivity is enforced by checking each state that will be exited against the
`conclusive` attributes.

        s = source; until s is domain
          return null if s.attributes & CONCLUSIVE
          s = s.superstate

If a previously initiated transition is still underway, it needs to be notified
that it will not finish.

        transition?.abort()

Retrieve the appropriate transition expression for this origin/target pairing.
If none is defined, then an actionless default transition will be created and
applied, causing the callback to return immediately.

        @_transition = transition = new Transition target, source,
          @getTransitionExpression target, origin

Preparation for the transition begins by emitting a `depart` event on the
`source` state.

        source.emit 'depart', transition, VIA_PROTO
        @_transition = transition = null if transition.aborted

Enter into the transition state.

        if transition
          @_current = transition
          transition.emit 'enter', VIA_NONE
          @_transition = transition = null if transition.aborted

Walk up to the top of the domain, emitting `exit` events for each state along
the way.

        s = source; while transition and s isnt domain
          s.emit 'exit', transition, VIA_PROTO
          transition.superstate = s = s.superstate
          @_transition = transition = null if transition.aborted

A callback will be invoked from `transition.end()` to conclude the transition.

        transition?.callback = ->
          transition = null if transition.aborted

Trace a path from `target` up to `domain`, then walk down it, emitting `enter`
events for each state along the way.

          if transition
            s = target; pathToState = []; while s isnt domain
              pathToState.push s
              s = s.superstate
          s = domain; while transition and substate = pathToState.pop()
            transition.superstate = substate
            substate.emit 'enter', transition, VIA_PROTO
            transition = null if transition.aborted
            s = substate

Exit from the transition state.

          if transition
            transition.emit 'exit', VIA_NONE
            transition = null if transition.aborted

Terminate the transition with an `arrive` event on the targeted state.

          if transition
            @_current = target
            target.emit 'arrive', transition, VIA_PROTO

Any virtual states that were previously active may now be discarded.

            s = origin; while s.attributes & VIRTUAL
              ss = s.superstate
              do s.destroy
              s = ss

Now complete, the `Transition` instance can be discarded.

            do transition.destroy
            @_transition = transition = null

            options.success?.call? this

            return target

          return null

At this point the transition is attached to the `domain` state and is ready to
proceed.

        return transition?.start.apply( transition, options.args ) or @_current

## [RootState](#root-state)

    class RootState extends State

      { VIRTUAL, ABSTRACT, CONCLUSIVE, FINAL } = this
      { VIA_NONE, VIA_PROTO } = this

      DEFAULT_OPTIONS = {}

### [Constructor](#root-state--constructor)

      constructor: ( owner, expression, options ) ->
        @owner = owner or = {}
        unless expression instanceof StateExpression
          expression = new StateExpression expression
        if options?
        then if typeof options is 'string' then options = initialState: options
        else options = DEFAULT_OPTIONS

Assign to `owner` an **accessor** to its state implementation.

        @accessorName = accessorName = options.name or 'state'
        owner[ accessorName ] = createAccessor owner, accessorName, this

A root state’s `name` is by definition the empty-string.

        super owner, @name = '', expression

Determine the initial state, and set the `current` state to that.

        current = if options.initialState?
        then this.query options.initialState
        else this.initialSubstate() or this

The initial state may be `abstract`, in which case currency must be forwarded
to its default `concrete` substate.

        if current.attributes & ABSTRACT
          current = current.defaultSubstate() or current

The previous redirections may have left `current` pointing to a protostate, in
which case a virtual state must be created in `this` root’s tree.

        if current.root isnt this then current = current.virtualize this

The authoritative property reference to the owner’s `current` state.

        @_current = current

While a `Transition` is underway, `_current` will be a reference to that
transition. To further distinguish this condition, an identical reference is
also held at `_transition`.

        @_transition = null

### [Private functions](#root-state--private)

#### [createAccessor](#root-state--private--create-accessor)

Returns the `accessor` function that will serve as an owner object’s interface
to its state implementation.

      createAccessor = ( owner, name, root ) ->

        accessor = ( input, args... ) ->
          current = root._current

          if this is owner
            return current unless input?
            return current.change input.call this if typeof input is 'function'
            if typeof input is 'string' and
                ( match = input.match rxTransitionArrow ) and
                  method = transitionArrowMethods[ match[1] ]
              return if args.length
              then current[ method ].apply current, [ match[2] ].concat args
              else current[ method ] match[2]
            return current.query.apply current, arguments

Calling the accessor of a prototype means that `this` requires its own accessor
and root state. Creating a new `RootState` will have the desired side-effect of
also creating the object’s new accessor, to which the call is then forwarded.

          else if owner.isPrototypeOf(this) and not O.hasOwn.call this, name
            new RootState this, null, { name, initialState: current.path() }
            return @[ name ].apply this, arguments

        accessor.isAccessor = true

        if O.env.debug
          accessor.toString = ->
            "[accessor] -> #{ root._current.path() }"

        accessor

#### [evaluateGuard](#root-state--private--evaluate-guard)

Returns the boolean result of a `guard` function in the `context` of a `State`,
as evaluated `against` another `State`. Defaults to `true` if no guard exists.

      evaluateGuard = ( context, guard, against ) ->
        guard = context.guard guard if typeof guard is 'string'
        return true unless guard

        args = O.slice.call arguments, 1
        for own key, value of guard
          valueIsFn = typeof value is 'function'
          selectors = O.trim( key ).split /\s*,+\s*/
          for selector in selectors when context.query selector, against
            result = if valueIsFn then value.apply context, args else value
            break
          break unless result
        !!result

#### [getTransitionExpression](#root-state--private--get-transition-expression)

Finds the appropriate transition expression for the given `target` and `origin`
states. If no matching transitions are defined in either state or any of their
ancestors, a generic actionless transition expression for the pair is returned.

      getTransitionExpression: do ->

        search = ( target, origin, subject, ceiling ) ->
          while subject and subject isnt ceiling
            for own key, expr of subject.transitions()
              return expr if (
                not ( guards = expr.guards ) or (
                  not ( admit = guards.admit ) or
                  O.isEmpty( admit ) or
                  evaluateGuard.call origin, admit, target, origin
                ) and (
                  not ( release = guards.release ) or
                  O.isEmpty( release ) or
                  evaluateGuard.call target, release, origin, target
                )
              ) and (
                if expr.target then subject.query expr.target, target
                else subject is target
              ) and ( not expr.origin or subject.query expr.origin, origin )
            break unless ceiling?
            subject = subject.superstate

        ( target, origin = @_current ) ->
          ( search target, origin, target ) or
          ( search target, origin, origin unless origin is target ) or
          ( search target, origin, target.superstate, @root ) or
          ( search target, origin, @root ) or
          ( unless target.isIn origin
            search target, origin, origin.superstate, origin.common target ) or
          new TransitionExpression

### [Prototype methods](#root-state--prototype-methods)

#### [change](#root-state--prototype--change)

Attempts to execute a state transition. Handles asynchronous transitions,
generation of appropriate events, and construction of any necessary temporary
virtual states. Respects guards supplied in both the origin and `target`
states.

The `target` parameter may be either a `State` object within the tree of this
`RootState`, or a string that resolves to a likewise targetable `State` when
evaluated from the context of the most recently current state.

The `options` parameter is an optional map that may include:

  * `args` : `Array` — arguments to be passed to a transition’s `action`.
  * `success` : `Function` — callback to be executed upon successful completion
    of the transition.
  * `failure` : `Function` — callback to be executed if the transition
    attempt is blocked by a guard.

      change: ( target, options ) ->
        { root, owner } = this
        current = @_current
        transition = @_transition

The `origin` is defined as the controller’s most recently current state that is
not a [`Transition`][].

        origin = transition?.origin or current

Departures are not allowed from a state that is `final`.

        return null if origin.attributes & FINAL

Ensure that `target` is a valid [`State`][].

        unless target instanceof State
          target = if target then origin.query target else root
        return null unless target
        targetOwner = target.owner
        return null if owner isnt targetOwner and
          not targetOwner.isPrototypeOf owner

Resolve `options` to an object if necessary.

        unless options then options = DEFAULT_OPTIONS
        else if O.isArray options then options = args: options

A transition cannot target an abstract state directly, so `target` must be
reassigned to the appropriate concrete substate.

        while target.attributes & ABSTRACT
          return null unless target = target.defaultSubstate()

If any guards are in place for the given `origin` and `target`, both of those
states must consent to the transition.

        unless options.forced
          released = evaluateGuard origin, 'release', target
          admitted = evaluateGuard target, 'admit', origin
          unless released and admitted
            options.failure?.call? this
            return null

If `target` is a protostate, then the protostate must be virtualized locally
and `target` must be reassigned to the new virtual state.

        target = target.virtualize this unless target?.root is root

A reference is held to the previously current state, or abortive transition.

        source = current

The upcoming transition will start from its `source` and proceed within the
`domain` of the least common ancestor between `source` and `target`.

        domain = source.common target

Conclusivity is enforced by checking each state that will be exited against the
`conclusive` attributes.

        s = source; until s is domain
          return null if s.attributes & CONCLUSIVE
          s = s.superstate

If a previously initiated transition is still underway, it needs to be notified
that it will not finish.

        transition?.abort()

Retrieve the appropriate transition expression for this origin/target pairing.
If none is defined, then an actionless default transition will be created and
applied, causing the callback to return immediately.

        @_transition = transition = new Transition target, source,
          @getTransitionExpression target, origin

Preparation for the transition begins by emitting a `depart` event on the
`source` state.

        source.emit 'depart', transition, VIA_PROTO
        @_transition = transition = null if transition.aborted

Enter into the transition state.

        if transition
          @_current = transition
          transition.emit 'enter', VIA_NONE
          @_transition = transition = null if transition.aborted

Walk up to the top of the domain, emitting `exit` events for each state along
the way.

        s = source; while transition and s isnt domain
          s.emit 'exit', transition, VIA_PROTO
          transition.superstate = s = s.superstate
          @_transition = transition = null if transition.aborted

A callback will be invoked from `transition.end()` to conclude the transition.

        transition?.callback = ->
          transition = null if transition.aborted

Trace a path from `target` up to `domain`, then walk down it, emitting `enter`
events for each state along the way.

          if transition
            s = target; pathToState = []; while s isnt domain
              pathToState.push s
              s = s.superstate
          s = domain; while transition and substate = pathToState.pop()
            transition.superstate = substate
            substate.emit 'enter', transition, VIA_PROTO
            transition = null if transition.aborted
            s = substate

Exit from the transition state.

          if transition
            transition.emit 'exit', VIA_NONE
            transition = null if transition.aborted

Terminate the transition with an `arrive` event on the targeted state.

          if transition
            @_current = target
            target.emit 'arrive', transition, VIA_PROTO

Any virtual states that were previously active may now be discarded.

            s = origin; while s.attributes & VIRTUAL
              ss = s.superstate
              do s.destroy
              s = ss

Now complete, the `Transition` instance can be discarded.

            do transition.destroy
            @_transition = transition = null

            options.success?.call? this

            return target

          return null

At this point the transition is attached to the `domain` state and is ready to
proceed.

        return transition?.start.apply( transition, options.args ) or @_current


move loading of global libraries into celestrium

## API

this is used to populate the listed singleton plugins with their single instance

other plugins may access this instance through requiring the module.

### Example

```coffeescript
require ["pluginPath"], (plugin) -> plugin.getInstance().foo()`
```

### Example Usage

Now in your `main` coffeescript file, you can do something like this.

```coffeescript
requirejs ["celestrium"], (Celestrium) ->
  Celestrium.init
    "core/workspace":
      "el": document.querySelector "#workspace"
```

only plugins which instantiate the `singleton` class should be used with `init`

## Code

Load the global libraries contained within celestrium here.
This offloads this responsibility from the user.

    globalLibs = [
      'lib/jquery',
      'lib/jquery.typeahead',
      'lib/underscore',
      'lib/backbone',
      'lib/d3',
    ]

The actual module returns only an object with an init method.

    define [], () -> init: (singletonPlugins, callback) ->

Include this nested require call so as to load the above libraries.
Now, these libraries are available globally per the norm.

This is probably bad practice and less modular, but is practical.

      require globalLibs, () ->

Now actually require the different plugins.
Note that, they should all be required at once and the callback called within the require callback so as to ensure the callback is only call onced these plugin instances have been initialized.

        pluginPaths = _.keys(singletonPlugins)
        require pluginPaths, (plugins...) ->
          i = 0
          for plugin in plugins
            args = singletonPlugins[pluginPaths[i]]
            plugin.init args
            i += 1
          callback()

## API

this is used to populate the listed singleton plugins with their single instance

other plugins may access this instance through requiring the module.

### Example

```coffeescript
require ["pluginPath"], (plugin) -> plugin.getInstance().foo()`
```

### Example Usage

Now in your `main` coffeescript file, you can do something like this.

```coffeescript
requirejs ["celestrium"], (Celestrium) ->
  Celestrium.init
    "core/workspace":
      "el": document.querySelector "#workspace"
```

only plugins which instantiate the `singleton` class should be used with `init`

## Code

    define [], () -> init: (singletonPlugins, callback) ->
      pluginPaths = _.keys(singletonPlugins)
      require pluginPaths, (plugins...) ->
        i = 0
        for plugin in plugins
          args = singletonPlugins[pluginPaths[i]]
          plugin.init args
          i += 1
        callback()


polling

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

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

    _self = this

##Response class

    class Response
     constructor: (data, port, options) ->
      @id = data.id
      @port = port
      @options = options

     progress: (progress, data) ->
      @port.respond this, 'progress', data, progress: progress, @options

     success: (data) ->
      @port.respond this, 'success', data, {}, @options

     fail: (data) ->
      @port.respond this, 'fail', data, {}, @options

##Call class

    class Call
     constructor: (@id, @method, @data, @callbacks) -> 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
      @wrappers =
       send: []
       respond: []
       handleCall: []
       handleResponse: []

     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

###Create Call object
This is a private function

     _createCall: (method, data, callbacks, options) ->
      call = new Call @callsCounter, method, data, callbacks
      @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

     _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}"
      @handlers[data.method] data.data, data, new Response data, this, options

     _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

##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()

     _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()

##NodeHttpServerPort class

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

     _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

#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


change the property prefixes

change the property prefixes

# Batman Request
 Request is an abstraction around XHR requests.  It can be used with a variety of platforms, and is implemented 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.node.js`: "querystring" and "url"
 - `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 : PlainObject
  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 : PlainObject )

  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.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.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.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 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.node.js`: "querystring" and "url"
 - `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

## Request::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

## Request::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)

## Request::responseHeaders : PlainObject
  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 : PlainObject )

  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.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.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`

## Request::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.addMockedResponse("GET", "http://batmanjs.org", -> {beforeResponse: beforeResponse} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        data: { some: "data" }
      req.send( { other: "thing" } )


Fix a <

Fix a <

Match Schedule
==============

The match schedule, as with the day schedule, is mainly managed within the basic
data module - this module defines only useful utilities.

Constants
---------

We define two constants within this module -- *MATCH_PREROLL*, and
*MATCH_LENGTH*.

*MATCH_PREROLL* is the amount of time, in seconds, before the start
time of a match during which it should be considered active. Its
current value is 180 seconds, which is 30 seconds of swap time and
60 seconds boot period.

    MATCH_PREROLL = 30 + 60

*MATCH_LENGTH* is simply the length of a match in seconds, including
setup and tear-down time - presently 5 minutes.

    MATCH_LENGTH = 60*5

*MATCH_POSTROLL* is the amount of time, in seconds, between the
start time of a match and when it is considered to have ended. It
is defined in terms of *MATCH_PREROLL* and *MATCH_LENGTH*.

    MATCH_POSTROLL = MATCH_LENGTH - MATCH_PREROLL

Match Tracking
--------------

The *firstMatchAfter* function takes as its argument a match schedule,
and a *reference point*, being a JavaScript *Date* object. It finds
the first match in the schedule whose consideration period begins
at or after the reference point. It may also take an optional
*feasibility condition*, to further filter the matches considered.

    firstMatchAfter = (schedule, referencePoint, feasilibilty) ->

If the feasibility condition is unspecified, we use a default
condition which simply accepts all matches.

        unless feasibility?
            feasibility = (match) -> yes

We convert the reference point into a UNIX timestamp for our use.

        referenceUNIX = referencePoint.valueOf() * 0.001

We then use Underscore's *find* function to find the first satisfying
match.

        _.find schedule, (match) ->
            return no unless feasibility(match, referencePoint)
            match.start - MATCH_PREROLL >= referenceUNIX

Current Match
-------------

We provide the current match (nullable) via the *CurrentMatch*
property. It is a map on the *Data.matchSchedule* property, and the
clock.

First, we define the data source.

    currentMatchSource =
        Bacon.combineTemplate
            schedule: Data.matchSchedule
            clock: Clock

We then define the stream itself, as a map over the input source.

    currentMatch = currentMatchSource.map (data) ->

We find the first match active now, using *firstMatchAfter*. A
feasibility condition is passed to constrain matches to only those
which have not ended.

        firstMatchAfter data.schedule, data.clock, (match, ref) ->
            not (match.start + MAX_POSTROLL > ref)

The public property simply removes the duplicates from that stream.

    window.CurrentMatch = currentMatch.skipDuplicates()

Next Match
----------

We also provide the next match (nullable) via the *NextMatch*
property. It is defined similarly to *CurrentMatch*, except with
the additional feasibility condition of not being the current match.

We first define the data source.

    nextMatchSource =
        Bacon.combineTemplate
            schedule: Data.matchSchedule
            clock: Clock
            currentMatch: CurrentMatch

We then define the stream itself, again as a map over the source.

    nextMatch = nextMatchSource.map (data) ->

As with the current match, we use *firstMatchAfter*, with a feasibility
condition this time specifying that the match is not the current
match.

        firstMatchAfter data.schedule, data.clock, (match) ->
            match.key isnt data.currentMatch?.key

We then publish it by removing duplicates.

    window.NextMatch = nextMatch.skipDuplicates()

Match Schedule
==============

The match schedule, as with the day schedule, is mainly managed within the basic
data module - this module defines only useful utilities.

Constants
---------

We define two constants within this module -- *MATCH_PREROLL*, and
*MATCH_LENGTH*.

*MATCH_PREROLL* is the amount of time, in seconds, before the start
time of a match during which it should be considered active. Its
current value is 180 seconds, which is 30 seconds of swap time and
60 seconds boot period.

    MATCH_PREROLL = 30 + 60

*MATCH_LENGTH* is simply the length of a match in seconds, including
setup and tear-down time - presently 5 minutes.

    MATCH_LENGTH = 60*5

*MATCH_POSTROLL* is the amount of time, in seconds, between the
start time of a match and when it is considered to have ended. It
is defined in terms of *MATCH_PREROLL* and *MATCH_LENGTH*.

    MATCH_POSTROLL = MATCH_LENGTH - MATCH_PREROLL

Match Tracking
--------------

The *firstMatchAfter* function takes as its argument a match schedule,
and a *reference point*, being a JavaScript *Date* object. It finds
the first match in the schedule whose consideration period begins
at or after the reference point. It may also take an optional
*feasibility condition*, to further filter the matches considered.

    firstMatchAfter = (schedule, referencePoint, feasilibilty) ->

If the feasibility condition is unspecified, we use a default
condition which simply accepts all matches.

        unless feasibility?
            feasibility = (match) -> yes

We convert the reference point into a UNIX timestamp for our use.

        referenceUNIX = referencePoint.valueOf() * 0.001

We then use Underscore's *find* function to find the first satisfying
match.

        _.find schedule, (match) ->
            return no unless feasibility(match, referencePoint)
            match.start - MATCH_PREROLL <= referenceUNIX

Current Match
-------------

We provide the current match (nullable) via the *CurrentMatch*
property. It is a map on the *Data.matchSchedule* property, and the
clock.

First, we define the data source.

    currentMatchSource =
        Bacon.combineTemplate
            schedule: Data.matchSchedule
            clock: Clock

We then define the stream itself, as a map over the input source.

    currentMatch = currentMatchSource.map (data) ->

We find the first match active now, using *firstMatchAfter*. A
feasibility condition is passed to constrain matches to only those
which have not ended.

        firstMatchAfter data.schedule, data.clock, (match, ref) ->
            not (match.start + MAX_POSTROLL > ref)

The public property simply removes the duplicates from that stream.

    window.CurrentMatch = currentMatch.skipDuplicates()

Next Match
----------

We also provide the next match (nullable) via the *NextMatch*
property. It is defined similarly to *CurrentMatch*, except with
the additional feasibility condition of not being the current match.

We first define the data source.

    nextMatchSource =
        Bacon.combineTemplate
            schedule: Data.matchSchedule
            clock: Clock
            currentMatch: CurrentMatch

We then define the stream itself, again as a map over the source.

    nextMatch = nextMatchSource.map (data) ->

As with the current match, we use *firstMatchAfter*, with a feasibility
condition this time specifying that the match is not the current
match.

        firstMatchAfter data.schedule, data.clock, (match) ->
            match.key isnt data.currentMatch?.key

We then publish it by removing duplicates.

    window.NextMatch = nextMatch.skipDuplicates()