state = require './state-function'
State = require './state'

{ O } = state

module.exports =


A State defines a StateEventEmitter for each of its event types.

For the purposes of StateEventEmitters, a callback is normally a function, but may also take the form of a string, which, when the client state emits the event type associated with this emitter, will be interpreted as an implicit command for the emitting state to change to the target state named by the string.

class StateEventEmitter

  { isArray } = O

  guid = 0
  constructor: ( @state ) ->
    @items = {}
    @length = 0



Retrieves a bound callback associated with the provided id as returned by add.

  get: ( id ) ->
    @items[ id ]


Returns an array of all callbacks bound to this emitter.

  getAll: ->
    value for own key, value of @items


Adds or replaces a callback bound to a specific key.

  • id : string | number
  • callback : function | string
  set: ( id, callback ) ->
    { items } = this
    @length += 1 unless id of items
    items[ id ] = callback


Retrieves the id string associated with the bound callback.

  key: ( callback ) ->
    return key if value is callback for own key, value of @items


Returns the set of id strings associated with all bound callbacks.

  keys: ->
    key for own key of @items


Binds a callback and optional context object.

  add: ( callback, context ) ->
    id = guid += 1
    @items[ id ] = if context? then [ callback, context ] else callback
    @length += 1

  @::on = @::bind = @::add


Unbinds a callback, identified either by its numeric key or direct reference.

  • id : string | number | function
  remove: ( id ) ->
    { items } = this
    callback = items[ if typeof id is 'function' then @key id else id ]
    return no unless callback
    delete items[ id ]
    @length -= 1

  @::off = @::unbind = @::remove


Removes all callbacks, and returns the number removed.

  empty: ->
    return 0 unless n = @length
    @items = {}
    @length = 0


Invokes all bound callbacks with the provided array of args. Callbacks registered with an explicit context are invoked with that stored context. Callbacks that are state-bound functions are invoked in the context of the provided autostate, while unbound functions are invoked in the context of its owner.

  emit: ( args, autostate = @state ) ->
    throw TypeError unless owner = autostate?.owner

    for own key, item of @items
      fn = context = null

Interpret a string or State as an order to transition to the implicated State after all the callbacks have been invoked.

      if typeof item is 'string' or item instanceof State
        eventualTarget = item

      if typeof item is 'function' then fn = item
      else if isArray item then [ fn, context ] = item

Unbox any state-bound functions.

      else if item?.type is 'state-bound-function'
        { fn } = item
        context or = autostate

      fn.apply context or owner, args

    @state.change eventualTarget if eventualTarget

  @::trigger = @::emit
  destroy: ->
    do @empty
    @state = @items = null