Fast detection of normally closed windows.

Windows when closed normally receive a window "unload" event.  A
window isn't able to write to the database at this point (it will
receive no more events in the event loop), but it is able to write to
local storage and/or send a message to the shared web worker.

This isn't 100% reliable (windows can crash without having a chance to
process the unload event), but it does provide for a fast path for
detecting dead windows when they've been closed normally.  Pings are
still used to detect crashed windows.

    @Offline or= {}
    {Fanout, Result} = awwx
    broadcast = Offline._broadcast
    {defer} = awwx.Error
    {withContext} = awwx.Context
    db = Offline._database

    Offline._windows = {}
    Offline._windows.windowsAreDead = windowsAreDead = new Fanout()

    now = -> +(new Date())

## Running in a shared web worker

    if Agent?

TODO once we get going we don't need to report that no windows are
dead (we could call `windowsAreDead` only when the `windowIds` array
wasn't empty). But do we need to the first time in order to know that
it's safe to clear out old subscriptions?  For now always call
`windowsAreDead`, even with an empty array.

      deadWindows = (windowIds) ->
        Agent.windowIsDead(windowId) for windowId in windowIds
        windowsAreDead(windowIds)
        return

      checking = false
      lastPing = null
      testingWindows = {}

      Agent.addMessageHandler 'goodbye', (port, data) ->
        deadWindows([data.windowId])
        return

      Agent.addMessageHandler 'pong', (port, data) ->
        delete testingWindows?[data.windowId]
        return

      checkPongs = ->
        deadWindows(_.keys(testingWindows))
        checking = false
        testingWindows = null

      check = ->
        withContext "window check", ->
          return if checking or lastPing? and now() - lastPing < 9000
          checking = true

          db.transaction((tx) ->
            db.readAllWindowIds(tx)
          )
          .then((windowIds) ->
            testingWindows = {}
            testingWindows[windowId] = true for windowId in Agent.windowIds
            testingWindows[windowId] = true for windowId in windowIds
            lastPing = now()
            for port in Agent.ports
              port.postMessage({msg: 'ping'})
            Meteor.setTimeout checkPongs, 4000
            return
          )
          return

      Meteor.startup ->
        check()
        Meteor.setInterval check, 10000

## Running in a browser window

    else

      Offline._windows.thisWindowId = thisWindowId = Random.id()

      Offline._windows.nowAgent = nowAgent = new Fanout()
      # nowAgent.listen -> Meteor._debug "now the agent"

      testingWindows = null
      lastPing = null
      checking = false

      deadWindows = (deadWindowIds) ->
        withContext "deadWindows", ->
          db.transaction((tx) ->
            db.readAgentWindow(tx)
            .then((agentWindowId) ->
              if not agentWindowId? or _.contains(deadWindowIds, agentWindowId)
                becomeTheAgentWindow(tx)
              else
                Result.completed()
            )
          )
          .then(->
            windowsAreDead(deadWindowIds)
          )

      currentlyTheAgent = false

      Offline._windows.currentlyTheAgent = -> currentlyTheAgent

      becomeTheAgentWindow = (tx) ->
        withContext "becomeTheAgentWindow", ->
          currentlyTheAgent = true
          defer -> nowAgent()
          db.writeAgentWindow(tx, thisWindowId)

On startup, if there isn't an agentWindow in the database or if the
agent window is marked as closed in local storage, we can become the
agent window right away.

      startupCheck = ->
        withContext "startupCheck", ->
          db.transaction((tx) ->
            db.readAgentWindow(tx)
            .then((agentWindowId) ->
              if not agentWindowId?
                becomeTheAgentWindow(tx)
              else
                closedAgentWindow = localStorage.getItem(
                  '/awwx/offline-data/agentWindowClosed'
                )
                if closedAgentWindow is agentWindowId
                  becomeTheAgentWindow(tx)
            )
            .then(->
              localStorage.removeItem(
                '/awwx/offline-data/agentWindowClosed'
              )
              return
            )
          )

      check = ->
        withContext "window check", ->
          return if checking
          checking = true
          db.transaction((tx) ->
            db.readAllWindowIds(tx)
          )
          .then((windowIds) ->
            if lastPing? and now() - lastPing < 9000
              return

            testingWindows = {}
            for windowId in windowIds
              unless windowId is thisWindowId
                testingWindows[windowId] = true
            broadcast 'ping'
            Result.delay(4000).then(-> windowIds)
          )
          .then((windowIds) ->
            deadWindows(_.keys(testingWindows))
          )
          .then(->
            checking = false
          )

      Meteor.startup ->
        withContext "windows startup", ->

          return if Offline._disableStartupForTesting

          if Offline._usingSharedWebWorker

            db.transaction((tx) ->
              db.ensureWindow(tx, thisWindowId)
            )

          else

            broadcast.listen 'ping', ->
              withContext "listen ping", ->
                broadcast 'pong', thisWindowId
                return

            broadcast.listen 'pong', (windowId) ->
              withContext "listen pong", ->
                if testingWindows?
                  delete testingWindows[windowId]
                return

            broadcast.listen 'goodbye', (windowId) ->
              deadWindows([windowId])

            db.transaction((tx) ->
              db.ensureWindow(tx, thisWindowId)
            )
            .then(->
              startupCheck()
            )
            .then(->
              check()
              Meteor.setInterval check, 10000
            )
            return

When a window is closing and gets an unload event, it doesn't survive
long enough to write anything to the database (it gets no more ticks
of the event loop).  But we are able to write to local storage.

      unload = ->
        withContext "unload", ->
          if Offline._usingSharedWebWorker
            Offline._sharedWebWorker.post {
              msg: 'goodbye',
              windowId: thisWindowId
            }
          else
            if currentlyTheAgent
              localStorage.setItem(
                '/awwx/offline-data/agentWindowClosed',
                thisWindowId
              )
            broadcast 'goodbye', thisWindowId
          return

      if window.addEventListener?
        window.addEventListener('unload', unload, false)

    @Offline or= {}
    {Fanout, Result} = awwx
    broadcast = Offline._broadcast
    {defer} = awwx.Error
    {withContext} = awwx.Context
    db = Offline._database

    Offline._windows = {}
    Offline._windows.windowsAreDead = windowsAreDead = new Fanout()

    now = -> +(new Date())

## Running in a shared web worker

    if Agent?

TODO once we get going we don't need to report that no windows are
dead (we could call `windowsAreDead` only when the `windowIds` array
wasn't empty). But do we need to the first time in order to know that
it's safe to clear out old subscriptions?  For now always call
`windowsAreDead`, even with an empty array.

      deadWindows = (windowIds) ->
        Agent.windowIsDead(windowId) for windowId in windowIds
        windowsAreDead(windowIds)
        return

      checking = false
      lastPing = null
      testingWindows = {}

      Agent.addMessageHandler 'pong', (port, data) ->
        delete testingWindows?[data.windowId]
        return

      checkPongs = ->
        deadWindows(_.keys(testingWindows))
        checking = false
        testingWindows = null

      check = ->
        withContext "window check", ->
          return if checking or lastPing? and now() - lastPing < 9000
          checking = true

          db.transaction((tx) ->
            db.readAllWindowIds(tx)
          )
          .then((windowIds) ->
            testingWindows = {}
            testingWindows[windowId] = true for windowId in Agent.windowIds
            testingWindows[windowId] = true for windowId in windowIds
            lastPing = now()
            for port in Agent.ports
              port.postMessage({msg: 'ping'})
            Meteor.setTimeout checkPongs, 4000
            return
          )
          return

      Meteor.startup ->
        check()
        Meteor.setInterval check, 10000

## Running in a browser window

    else

      Offline._windows.thisWindowId = thisWindowId = Random.id()

      Offline._windows.nowAgent = nowAgent = new Fanout()
      Offline._windows.noLongerAgent = noLongerAgent = new Fanout()

      # nowAgent.listen -> Meteor._debug "now the agent"
      # noLongerAgent.listen -> Meteor._debug "no longer the agent"

TODO not used yet

      unload = ->
        withContext "unload", ->
          broadcast 'goodbye', thisWindowId
          return

TODO old IE?  But will we even be loading this code in a browser that
doesn't have a supported database...?

      window.addEventListener('unload', unload, false)

      testingWindows = null
      lastPing = null
      checking = false

      deadWindows = (deadWindowIds) ->
        withContext "deadWindows", ->
          db.transaction((tx) ->
            db.readAgentWindow(tx)
            .then((agentWindowId) ->
              if not agentWindowId? or _.contains(deadWindowIds, agentWindowId)
                becomeTheAgentWindow(tx)
              else
                Result.completed()
            )
          )
          .then(->
            windowsAreDead(deadWindowIds)
          )

      currentlyTheAgent = false

      Offline._windows.currentlyTheAgent = -> currentlyTheAgent

      becomeTheAgentWindow = (tx) ->
        withContext "becomeTheAgentWindow", ->
          currentlyTheAgent = true
          defer -> nowAgent()
          db.writeAgentWindow(tx, thisWindowId)
          .then(->
            broadcast 'newAgent', thisWindowId
            return
          )

      notTheAgent = ->
        withContext "notTheAgent", ->
          return unless currentlyTheAgent
          currentlyTheAgent = false
          noLongerAgent()
          return

TODO if no one is the agent tab, we could become the agent
tab immediately.

      check = ->
        withContext "window check", ->
          return if checking
          checking = true
          db.transaction((tx) ->
            db.readAllWindowIds(tx)
          )
          .then((windowIds) ->
            if lastPing? and now() - lastPing < 9000
              return

            testingWindows = {}
            for windowId in windowIds
              unless windowId is thisWindowId
                testingWindows[windowId] = true
            broadcast 'ping'
            Result.delay(4000).then(-> windowIds)
          )
          .then((windowIds) ->
            deadWindows(_.keys(testingWindows))
          )
          .then(->
            checking = false
          )

      Meteor.startup ->
        withContext "windows startup", ->

          return if Offline._disableStartupForTesting

          if Offline._usingSharedWebWorker

            db.transaction((tx) ->
              db.ensureWindow(tx, thisWindowId)
            )

          else

            broadcast.listen 'ping', ->
              withContext "listen ping", ->
                broadcast 'pong', thisWindowId
                return

            broadcast.listen 'pong', (windowId) ->
              withContext "listen pong", ->
                if testingWindows?
                  delete testingWindows[windowId]
                return

            broadcast.listen 'newAgent', (windowId) ->
              withContext "listen newAgent", ->
                if windowId isnt thisWindowId
                  notTheAgent()
                return

            db.transaction((tx) ->
              db.ensureWindow(tx, thisWindowId)
            )
            .then(->
              check()
              Meteor.setInterval check, 10000
            )
            return


Fixed evaulate value functionality

Fixed evaulate value functionality

#Includes

	{EventEmitter} = require 'events'
	require './stringshims'

#Phrase

	class Phrase
		constructor: (options, @scope, elementFactory) ->
			{@name, @sentence, @evaluate} = options
			@root = elementFactory.create(options.root)

		parse: (input, context, data, done) ->
			@root.parse input, context, (result) ->
				delete result.result[id] for id of result.result when id.startsWith '@temp'
				data(result)
			, done

		getValue: (options, result) ->
			if @evaluate?
				return @scope[@evaluate].call(options, result)
			else
				return result['@value']

	module.exports = Phrase

#Includes

	{EventEmitter} = require 'events'
	require './stringshims'

#Phrase

	class Phrase
		constructor: (options, @scope, elementFactory) ->
			{@name, @sentence} = options
			@root = elementFactory.create(options.root)

		parse: (input, context, data, done) ->
			@root.parse input, context, (result) ->
				delete result.result[id] for id of result.result when id.startsWith '@temp'
				data(result)
			, done

		getValue: (options, result) ->
			if options.evaluate?
				return @scope[options.evaluate].call(options, result)
			else
				return result['@value']

	module.exports = Phrase


throw meaningful error during Model.access if model not found

# Model - instance of schema-driven data

The `Model` class aggregates [Property](./property.litcoffee)
attachments to provide the *adaptive* and *event-driven* data
interactions.

It is typically not instantiated directly, but is generated as a
result of [Yang::eval](../yang.litcoffee#eval-data-opts) for a YANG
`module` schema.

```javascript
var schema = Yang.parse('module foo { container bar { leaf a { type uint8; } } }');
var model = schema.eval({ 'foo:bar': { a: 7 } });
// model is { 'foo:bar': [Getter/Setter] }
```

The generated `Model` is a hierarchical composition of
[Property](./property.litcoffee) instances. The instance itself uses
`Object.preventExtensions` to ensure no additional properties that are
not known to itself can be added.

It is designed to provide *stand-alone* interactions on a per-module
basis. For flexible management of multiple modules (such as hotplug
modules) and data persistence, please take a look at the
[yang-store](http://github.com/corenova/yang-store) project.

Below are list of properties available to every instance of `Model`
(it also inherits properties from [Property](./property.litcoffee)):

property | type | mapping | description
--- | --- | --- | ---
transactable | boolean | computed | getter/setter for `state.transactable`
engine | Emitter | access(state) | holds runtime features

## Dependencies

    debug    = require('debug')('yang:model')
    delegate = require 'delegates'
    clone    = require 'clone'
    Stack    = require 'stacktrace-parser'
    Emitter  = require('events').EventEmitter
    Property = require './property'

## Class Model

    class Model extends Property

      @Store   = {}
      @Property = Property

      constructor: ->
        unless this instanceof Model then return new Model arguments...
        super

        @state.transactable = false
        @state.maxTransactions = 100
        @state.queue = []
        @state.engine = new Emitter # <-- should eventually be a singleton instance
        @state.engine.__ = this
        Object.setPrototypeOf @state, Emitter.prototype

        #@on 'update', -> @save() unless @transactable

        # register this instance in the Model class singleton instance
        @join Model.Store, replace: true

      delegate @prototype, 'state'
        .method 'emit'
        .method 'once'
        .access 'engine'

### Computed Properties

      enqueue = (prop) ->
        if @queue.length > @maxTransactions
          throw @error "exceeded max transaction queue of #{@maxTransactions}, forgot to save()?"
        @queue.push { target: prop, value: prop.state.prev }

      @property 'transactable',
        enumerable: true
        get: -> @state.transactable
        set: (toggle) ->
          return if toggle is @state.transactable
          if toggle is true
            @state.on 'update', enqueue
          else
            @state.removeListener 'update', enqueue
            @state.queue.splice(0, @state.queue.length)
          @state.transactable = toggle

### access (model)

This is a unique capability for a Model to be able to access any
other arbitrary model present inside the Model.Store.

      access: (model) ->
        try Model.Store[model].__
        catch e then throw @error "unable to locate '#{model}' instance in the Store"

### enable (feature)

This routine will enable a given `feature`

      enable: (feature, controller) ->
        @engine[feature] = controller if controller?
        unless @engine.hasOwnProperty feature
          throw @error "unable to enable unknown feature '#{feature}'"
        @engine.emit "enable:#{feature}", @engine[feature]
        return this

### save

This routine triggers a 'commit' event for listeners to handle any
persistence operations. It also clears the `@state.queue` transaction
queue so that future [rollback](#rollback) will reset back to this
state.

      save: ->
        @emit 'commit', @state.queue.slice();
        @state.queue.splice(0, @state.queue.length)

### rollback

This routine will replay tracked `@state.queue` in reverse chronological
order (most recent -> oldest) when `@transactable` is set to
`true`. It will restore the Property instance back to the last known
[save](#save-opts) state.

      rollback: ->
        while change = @state.queue.pop()
          change.target.set change.value, suppress: true
        this

### on (event)

The `Model` instance registers `@state` as an `EventEmitter` and you
can attach various event listeners to handle events generated by the
`Model`:

event | arguments | description
--- | --- | ---
update | (prop, prev) | fired when an update takes place within the data tree
change | (elems...) | fired when the schema is modified
create | (items...) | fired when one or more `list` element is added
delete | (items...) | fired when one or more `list` element is deleted

It also accepts optional XPATH/YPATH expressions which will *filter*
for granular event subscription to specified events from only the
elements of interest.

The event listeners to the `Model` can handle any customized behavior
such as saving to database, updating read-only state, scheduling
background tasks, etc.

This operation is protected from recursion, where operations by the
`callback` may result in the same `callback` being executed multiple
times due to subsequent events triggered due to changes to the
`Model`. Currently, it will allow the same `callback` to be executed
at most two times within the same execution stack.

      on: (event, filters..., callback) ->
        unless callback instanceof Function
          throw new Error "must supply callback function to listen for events"

        recursive = (name) ->
          seen = {}
          frames = Stack.parse(new Error().stack)
          for frame, i in frames when ~frame.methodName.indexOf(name)
            { file, lineNumber, column } = frames[i-1]
            callee = "#{file}:#{lineNumber}:#{column}"
            seen[callee] ?= 0
            if ++seen[callee] > 1
              console.warn "detected recursion for '#{callee}'"
              return true
          return false

        ctx = @context
        $$$ = (prop, args...) ->
          debug "$$$: check if '#{prop.path}' in '#{filters}'"
          if not filters.length or prop.path.contains filters...
            unless recursive('$$$')
              callback.apply ctx, [prop].concat args

        @state.on event, $$$

Please refer to [Model Events](../TUTORIAL.md#model-events) section of
the [Getting Started Guide](../TUTORIAL.md) for usage examples.

### in (pattern)

A convenience routine to locate one or more matching Property
instances based on `pattern` (XPATH or YPATH) from this Model.

      in: (pattern) ->
        try props = @find pattern
        return unless props? and props.length
        return switch
          when props.length > 1 then props
          else props[0]

## Prototype Overrides

### toJSON

Calls `Property.toJSON` with `tag = false`.

      toJSON: -> super false

### set

Calls `Property.set` with a *clone* of the data being passed
in. Should look for a way to eliminate this override.

      set: (value, opts) -> super clone(value), opts

### find (pattern)

This routine enables *cross-model* property search when the `Model` is
joined to another object (such as a datastore). The schema-bound model
restricts *cross-model* property access to only those modules that are
`import` dependencies of the current model instance.

      find: (pattern='.', opts={}) ->
        return super unless @parent?

        debug "[#{@name}] find #{pattern}"
        try match = super pattern, root: true
        catch err then debug err
        return match if match?.length or opts.root

        # here we have a @parent that likely has a collection of Models
        debug "[#{@name}] search parent with collection of Models"
        opts.root = true
        for k, model of @parent.__props__ when k isnt @name
          debug "[#{@name}] searching into '#{k}'"
          try match = model.find pattern, opts
          catch then continue
          return match if match?.length
        return []

### invoke (path, input)

Executes a [Property](./property.litcoffee) holding a function found
at the `path` using the `input` data.

      invoke: (path, args...) ->
        target = @in(path)
        unless target?
          throw @error "cannot invoke on '#{path}', not found"
        target.invoke args...

## Export Model Class

    module.exports = Model

# Model - instance of schema-driven data

The `Model` class aggregates [Property](./property.litcoffee)
attachments to provide the *adaptive* and *event-driven* data
interactions.

It is typically not instantiated directly, but is generated as a
result of [Yang::eval](../yang.litcoffee#eval-data-opts) for a YANG
`module` schema.

```javascript
var schema = Yang.parse('module foo { container bar { leaf a { type uint8; } } }');
var model = schema.eval({ 'foo:bar': { a: 7 } });
// model is { 'foo:bar': [Getter/Setter] }
```

The generated `Model` is a hierarchical composition of
[Property](./property.litcoffee) instances. The instance itself uses
`Object.preventExtensions` to ensure no additional properties that are
not known to itself can be added.

It is designed to provide *stand-alone* interactions on a per-module
basis. For flexible management of multiple modules (such as hotplug
modules) and data persistence, please take a look at the
[yang-store](http://github.com/corenova/yang-store) project.

Below are list of properties available to every instance of `Model`
(it also inherits properties from [Property](./property.litcoffee)):

property | type | mapping | description
--- | --- | --- | ---
transactable | boolean | computed | getter/setter for `state.transactable`
engine | Emitter | access(state) | holds runtime features

## Dependencies

    debug    = require('debug')('yang:model')
    delegate = require 'delegates'
    clone    = require 'clone'
    Stack    = require 'stacktrace-parser'
    Emitter  = require('events').EventEmitter
    Property = require './property'

## Class Model

    class Model extends Property

      @Store   = {}
      @Property = Property

      constructor: ->
        unless this instanceof Model then return new Model arguments...
        super

        @state.transactable = false
        @state.maxTransactions = 100
        @state.queue = []
        @state.engine = new Emitter # <-- should eventually be a singleton instance
        @state.engine.__ = this
        Object.setPrototypeOf @state, Emitter.prototype

        #@on 'update', -> @save() unless @transactable

        # register this instance in the Model class singleton instance
        @join Model.Store, replace: true

      delegate @prototype, 'state'
        .method 'emit'
        .method 'once'
        .access 'engine'

### Computed Properties

      enqueue = (prop) ->
        if @queue.length > @maxTransactions
          throw @error "exceeded max transaction queue of #{@maxTransactions}, forgot to save()?"
        @queue.push { target: prop, value: prop.state.prev }

      @property 'transactable',
        enumerable: true
        get: -> @state.transactable
        set: (toggle) ->
          return if toggle is @state.transactable
          if toggle is true
            @state.on 'update', enqueue
          else
            @state.removeListener 'update', enqueue
            @state.queue.splice(0, @state.queue.length)
          @state.transactable = toggle

### access (model)

This is a unique capability for a Model to be able to access any
other arbitrary model present inside the Model.Store.

      access: (model) -> Model.Store[model]?.__

### enable (feature)

This routine will enable a given `feature`

      enable: (feature, controller) ->
        @engine[feature] = controller if controller?
        unless @engine.hasOwnProperty feature
          throw @error "unable to enable unknown feature '#{feature}'"
        @engine.emit "enable:#{feature}", @engine[feature]
        return this

### save

This routine triggers a 'commit' event for listeners to handle any
persistence operations. It also clears the `@state.queue` transaction
queue so that future [rollback](#rollback) will reset back to this
state.

      save: ->
        @emit 'commit', @state.queue.slice();
        @state.queue.splice(0, @state.queue.length)

### rollback

This routine will replay tracked `@state.queue` in reverse chronological
order (most recent -> oldest) when `@transactable` is set to
`true`. It will restore the Property instance back to the last known
[save](#save-opts) state.

      rollback: ->
        while change = @state.queue.pop()
          change.target.set change.value, suppress: true
        this

### on (event)

The `Model` instance registers `@state` as an `EventEmitter` and you
can attach various event listeners to handle events generated by the
`Model`:

event | arguments | description
--- | --- | ---
update | (prop, prev) | fired when an update takes place within the data tree
change | (elems...) | fired when the schema is modified
create | (items...) | fired when one or more `list` element is added
delete | (items...) | fired when one or more `list` element is deleted

It also accepts optional XPATH/YPATH expressions which will *filter*
for granular event subscription to specified events from only the
elements of interest.

The event listeners to the `Model` can handle any customized behavior
such as saving to database, updating read-only state, scheduling
background tasks, etc.

This operation is protected from recursion, where operations by the
`callback` may result in the same `callback` being executed multiple
times due to subsequent events triggered due to changes to the
`Model`. Currently, it will allow the same `callback` to be executed
at most two times within the same execution stack.

      on: (event, filters..., callback) ->
        unless callback instanceof Function
          throw new Error "must supply callback function to listen for events"

        recursive = (name) ->
          seen = {}
          frames = Stack.parse(new Error().stack)
          for frame, i in frames when ~frame.methodName.indexOf(name)
            { file, lineNumber, column } = frames[i-1]
            callee = "#{file}:#{lineNumber}:#{column}"
            seen[callee] ?= 0
            if ++seen[callee] > 1
              console.warn "detected recursion for '#{callee}'"
              return true
          return false

        ctx = @context
        $$$ = (prop, args...) ->
          debug "$$$: check if '#{prop.path}' in '#{filters}'"
          if not filters.length or prop.path.contains filters...
            unless recursive('$$$')
              callback.apply ctx, [prop].concat args

        @state.on event, $$$

Please refer to [Model Events](../TUTORIAL.md#model-events) section of
the [Getting Started Guide](../TUTORIAL.md) for usage examples.

### in (pattern)

A convenience routine to locate one or more matching Property
instances based on `pattern` (XPATH or YPATH) from this Model.

      in: (pattern) ->
        try props = @find pattern
        return unless props? and props.length
        return switch
          when props.length > 1 then props
          else props[0]

## Prototype Overrides

### toJSON

Calls `Property.toJSON` with `tag = false`.

      toJSON: -> super false

### set

Calls `Property.set` with a *clone* of the data being passed
in. Should look for a way to eliminate this override.

      set: (value, opts) -> super clone(value), opts

### find (pattern)

This routine enables *cross-model* property search when the `Model` is
joined to another object (such as a datastore). The schema-bound model
restricts *cross-model* property access to only those modules that are
`import` dependencies of the current model instance.

      find: (pattern='.', opts={}) ->
        return super unless @parent?

        debug "[#{@name}] find #{pattern}"
        try match = super pattern, root: true
        catch err then debug err
        return match if match?.length or opts.root

        # here we have a @parent that likely has a collection of Models
        debug "[#{@name}] search parent with collection of Models"
        opts.root = true
        for k, model of @parent.__props__ when k isnt @name
          debug "[#{@name}] searching into '#{k}'"
          try match = model.find pattern, opts
          catch then continue
          return match if match?.length
        return []

### invoke (path, input)

Executes a [Property](./property.litcoffee) holding a function found
at the `path` using the `input` data.

      invoke: (path, args...) ->
        target = @in(path)
        unless target?
          throw @error "cannot invoke on '#{path}', not found"
        target.invoke args...

## Export Model Class

    module.exports = Model


added parameter to broadcast annotation

added parameter to broadcast annotation

# CLASS: Evented
* Extends: [Node][Node]

Event delivery Subsystem.

    Event = require './event'
    module.exports = class Evented extends require './node'

---

## CONSTRUCTOR

### new Evented(args)

Creates **Evented** instance. Initializes **#listeners** and **#events**

      constructor: (args) ->
        super
        (@events ||= {}) and @events[key] = value for key, value of args?.events
        @listeners = [{}, {}]

#### PARAMETERS

**args**
* Type: [Object][Object] - optional constructor argument.

**args.events**
* Type: [Object][Object] - enabled events. (event-name: true/false)

---

## METHODS

### #addListener(args)
### #addListener(type, listener, capture)
* Returns: [Evented][Evented]

Add a listener (per capturing/non-capturing group) for the event type.
Will not add duplicates. Will not add listeners if event is not enabled.

      addListener: (type, listener, capture = false) ->
        if type instanceof Object then {type, listener, capture} = type
        if @events[type] and typeof listener is 'function'
          listeners = (@listeners[if capture is true then 1 else 0][type] ||= [])
          listeners.push listener if -1 is listeners.indexOf listener
        return this

#### PARAMETERS
**args**
* Type: [Object][Object] - type, listener and capture parameters

Only if the first argument to addListener is an [Object][Object]

**type**
* Type: [String][String] - event type

**listener**
* Type: [Function][Function] - event listener

**capture**
* Type: [Boolean][Boolean] - capturing/non-capturing flag
* Default: `false`

---

### #removeListener(args)
### #removeListener(type, listener, capture)
* Returns: [Evented][Evented]

Remove the listener (per capturing/non-capturing group) for the event type.

      removeListener: (type, listener, capture = false) ->
        if type instanceof Object then {type, listener, capture} = type
        if type and typeof listener is 'function'
          if listeners = @listeners[if capture is true then 1 else 0][type]
            listeners.splice idx, 1 if -1 isnt idx = listeners.indexOf listener
        return this

#### PARAMETERS
**args**
* Type: [Object][Object] - type, listener and capture parameters

Only if the first argument to removeListener is an [Object][Object]

**type**
* Type: [String][String] - event type

**listener**
* Type: [Function][Function] - event listener

**capture**
* Type: [Boolean][Boolean] - capturing/non-capturing flag
* Default: `false`

---

### #dispatchEvent(event)
* Returns: [Evented][Evented]

Event dispatcher. Dispatches the event to appropriate listener

      dispatchEvent: (event) ->
        if (type = event?.type) and @event[type] and not event.aborted
          phase = event.phase
          if phase > 0 and phase < 3 and listeners = @listeners[2-phase][type]
            event.start()
            for listener in listeners
              break if event.stopped or event.aborted
              listener.call this, event
        return this

#### PARAMETERS

**event**
* Type: [Event][Event] - event to dispatch

---

### #broadcastEvent(event)
* Returns: [Evented][Evented]

Process the event, dispatch it to listeners and broadcast to children.

      broadcastEvent: (event) ->

No action will be taken if event has no type. This method
will initialize fresh events.
Only broadcast events that have a type. Event source will be
set to this instance if event has no source. Event target
can be changed by providing it as second argument to broadcast.

        if type = event?.type
          unless event.aborted or event.done or event.phase is 3
            event.start()
            event.source ||= this
            phase = (event.phase ||= 1)

If event target is this instance, it means event has reached it's
destination. Event phase is set to 2 (AT_TARGET)

            if event.target is this then event.phase = 2

If event phase is 1 (CAPTURING), the event will be dispatched to
this instance. After that, the event will be dispatched to every
while the phase remains CAPTURING and event was not aborted.

            if event.phase is 1
              @dispatchEvent event

              if @children then for child in @children
                if event.phase is 1 and not event.aborted
                  child.broadcastEvent event
                else break

After dispatching was finished, it may be possible that the event
target was changed. If the event target is this instance, the
event phase will be set to AT_TARGET and the event will be dispatched
again.

            if event.target is this then event.phase = 2

            if event.phase is 2 then @dispatchEvent event

At this point the event has finished it's lifecycle. If event.source
is this instance, so the event callback will be called if the
event was not aborted or finished or canceled.

            if event.source is this
              unless event.canceled or event.aborted or event.done or event.phase is 3
                event.callback?.call? this, event
              event.phase = 3
              event.finish()
        return this

#### PARAMETERS

**event**
* Type: [Event][Event] - event to broadcast

[Node]: ./node.litcoffee
[Event]: ./event.litcoffee
[Evented]: ./evented.litcoffee
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
[String]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
[Function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
[Boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean

# CLASS: Evented
* Extends: [Node][Node]

Event delivery Subsystem.

    Event = require './event'
    module.exports = class Evented extends require './node'

---

## CONSTRUCTOR

### new Evented(args)

Creates **Evented** instance. Initializes **#listeners** and **#events**

      constructor: (args) ->
        super
        (@events ||= {}) and @events[key] = value for key, value of args?.events
        @listeners = [{}, {}]

#### PARAMETERS

**args**
* Type: [Object][Object] - optional constructor argument.

**args.events**
* Type: [Object][Object] - enabled events. (event-name: true/false)

---

## METHODS

### #addListener(args)
### #addListener(type, listener, capture)
* Returns: [Evented][Evented]

Add a listener (per capturing/non-capturing group) for the event type.
Will not add duplicates. Will not add listeners if event is not enabled.

      addListener: (type, listener, capture = false) ->
        if type instanceof Object then {type, listener, capture} = type
        if @events[type] and typeof listener is 'function'
          listeners = (@listeners[if capture is true then 1 else 0][type] ||= [])
          listeners.push listener if -1 is listeners.indexOf listener
        return this

#### PARAMETERS
**args**
* Type: [Object][Object] - type, listener and capture parameters

Only if the first argument to addListener is an [Object][Object]

**type**
* Type: [String][String] - event type

**listener**
* Type: [Function][Function] - event listener

**capture**
* Type: [Boolean][Boolean] - capturing/non-capturing flag
* Default: `false`

---

### #removeListener(args)
### #removeListener(type, listener, capture)
* Returns: [Evented][Evented]

Remove the listener (per capturing/non-capturing group) for the event type.

      removeListener: (type, listener, capture = false) ->
        if type instanceof Object then {type, listener, capture} = type
        if type and typeof listener is 'function'
          if listeners = @listeners[if capture is true then 1 else 0][type]
            listeners.splice idx, 1 if -1 isnt idx = listeners.indexOf listener
        return this

#### PARAMETERS
**args**
* Type: [Object][Object] - type, listener and capture parameters

Only if the first argument to removeListener is an [Object][Object]

**type**
* Type: [String][String] - event type

**listener**
* Type: [Function][Function] - event listener

**capture**
* Type: [Boolean][Boolean] - capturing/non-capturing flag
* Default: `false`

---

### #dispatchEvent(event)
* Returns: [Evented][Evented]

Event dispatcher. Dispatches the event to appropriate listener

      dispatchEvent: (event) ->
        if (type = event?.type) and @event[type] and not event.aborted
          phase = event.phase
          if phase > 0 and phase < 3 and listeners = @listeners[2-phase][type]
            event.start()
            for listener in listeners
              break if event.stopped or event.aborted
              listener.call this, event
        return this

#### PARAMETERS

**event**
* Type: [Event][Event] - event to dispatch

---

### #broadcastEvent(event)
* Returns: [Evented][Evented]

      broadcastEvent: (event) ->

No action will be taken if event has no type. This method
will initialize fresh events.
Only broadcast events that have a type. Event source will be
set to this instance if event has no source. Event target
can be changed by providing it as second argument to broadcast.

        if type = event?.type
          unless event.aborted or event.done or event.phase is 3
            event.start()
            event.source ||= this
            phase = (event.phase ||= 1)

If event target is this instance, it means event has reached it's
destination. Event phase is set to 2 (AT_TARGET)

            if event.target is this then event.phase = 2

If event phase is 1 (CAPTURING), the event will be dispatched to
this instance. After that, the event will be dispatched to every
while the phase remains CAPTURING and event was not aborted.

            if event.phase is 1
              @dispatchEvent event

              if @children then for child in @children
                if event.phase is 1 and not event.aborted
                  child.broadcastEvent event
                else break

After dispatching was finished, it may be possible that the event
target was changed. If the event target is this instance, the
event phase will be set to AT_TARGET and the event will be dispatched
again.

            if event.target is this then event.phase = 2

            if event.phase is 2 then @dispatchEvent event

At this point the event has finished it's lifecycle if event.source
is this instance, so the event callback will be called if the
event was not aborted or finished or canceled.

            if event.source is this
              unless event.canceled or event.aborted or event.done or event.phase is 3
                event.callback?.call? this, event
              event.phase = 3
              event.finish()
        return this

#### PARAMETERS

[Node]: ./node.litcoffee
[Event]: ./event.litcoffee
[Evented]: ./evented.litcoffee
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
[String]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
[Function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
[Boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean


fixed date removal

fixed date removal

#Includes

	_ = require 'lodash'
	async = require 'async'
	{EventEmitter} = require 'events'
	util = require 'util'

	ElementFactory = require './element-factory'
	Phrase = require './phrase'
	InputOption = require './input-option'

#Lacona

	class Parser extends EventEmitter
		constructor: (options) ->

			@optionsForInput = _.pick(options, 'fuzzy')
			@options = _.omit(options, 'fuzzy')

			@phrases = []

			@middleware = []

			@understand require('./plugins/literal')
			@understand require('./plugins/freetext')
			@understand require('./plugins/integer')
			@understand require('./plugins/validator')
			@understand require('./plugins/suggester')
			@understand require('./plugins/list')

			@currentParseNumber = 0

		_phraseAccessor: (name) =>
			_.filter @phrases, (phrase) ->
				phrase.name is name or name in phrase.extends

###`understand`

`understand` takes a schema and scope, and defines a set of words for Lacona to understand

		understand: (options) ->
			scope = options.scope
			schema = options.schema ? options
			if not util.isArray(schema)
				schema = [schema]
			for phrase in schema
				elementFactory = new ElementFactory(scope, @)
				@phrases.push new Phrase(phrase, scope, elementFactory)
			return @

###`use`

`use` defines middleware. It must be passed a function that accepts 2 arguments, an inputOption,
and a callback that should be passed an error (or null) and a modulated inputOption. This will be passed
to the `data` event (or the next middleware) rather than the inputOption itself.

		use: (next) ->
			@middleware.push next
			return @

###`parse`

		parse: (inputText, lang) ->
			lang = lang ? window?.navigator?.language?.replace?('-', '_') ? process?.env?.LANG?.split?('.')?[0] ? 'default' #ALL THE QUESTION MARKS
			@currentParseNumber++
			thisParseNumber = @currentParseNumber

			phraseIsSentence = (item) ->
				item.run?

			gotData = (phrase, done) =>
				input = new InputOption(@optionsForInput, phrase, inputText)
				phrase.parse input, lang, null, (option) =>
					if option.text is '' and thisParseNumber is @currentParseNumber
						async.eachSeries @middleware, (call, done) =>
							call option, done
						, (err) =>
							return done(err) if err?
							@emit 'data', option
				, done

			allPhrasesDone = (err) =>
				if err?
					@emit 'error', err
				else if thisParseNumber is @currentParseNumber
					@emit 'end'

			async.each _.filter(@phrases, phraseIsSentence), gotData, allPhrasesDone

			return @

	run = (inputOption, done) ->
		inputOption.sentence.scope[inputOption.sentence.run](inputOption.result, done)

	nextText = (inputOption, done) ->
		match = _.reduce inputOption.match, (string, match) ->
			return string + match.string
		, ''

		matchAndSuggestion = _.reduce inputOption.suggestion.words, (string, suggestion) ->
			return string + suggestion.string
		, match

		done(null, matchAndSuggestion)

	module.exports =
		Parser: Parser
		run: run
		nextText: nextText

#Includes

	_ = require 'lodash'
	async = require 'async'
	{EventEmitter} = require 'events'
	util = require 'util'

	ElementFactory = require './element-factory'
	Phrase = require './phrase'
	InputOption = require './input-option'

#Lacona

	class Parser extends EventEmitter
		constructor: (options) ->

			@optionsForInput = _.pick(options, 'fuzzy')
			@options = _.omit(options, 'fuzzy')

			@phrases = []

			@middleware = []

			@understand require('./plugins/literal')
			@understand require('./plugins/freetext')
			@understand require('./plugins/integer')
			@understand require('./plugins/date')
			@understand require('./plugins/validator')
			@understand require('./plugins/suggester')
			@understand require('./plugins/list')

			@currentParseNumber = 0

		_phraseAccessor: (name) =>
			_.filter @phrases, (phrase) ->
				phrase.name is name or name in phrase.extends

###`understand`

`understand` takes a schema and scope, and defines a set of words for Lacona to understand

		understand: (options) ->
			scope = options.scope
			schema = options.schema ? options
			if not util.isArray(schema)
				schema = [schema]
			for phrase in schema
				elementFactory = new ElementFactory(scope, @)
				@phrases.push new Phrase(phrase, scope, elementFactory)
			return @

###`use`

`use` defines middleware. It must be passed a function that accepts 2 arguments, an inputOption,
and a callback that should be passed an error (or null) and a modulated inputOption. This will be passed
to the `data` event (or the next middleware) rather than the inputOption itself.

		use: (next) ->
			@middleware.push next
			return @

###`parse`

		parse: (inputText, lang) ->
			lang = lang ? window?.navigator?.language?.replace?('-', '_') ? process?.env?.LANG?.split?('.')?[0] ? 'default' #ALL THE QUESTION MARKS
			@currentParseNumber++
			thisParseNumber = @currentParseNumber

			phraseIsSentence = (item) ->
				item.run?

			gotData = (phrase, done) =>
				input = new InputOption(@optionsForInput, phrase, inputText)
				phrase.parse input, lang, null, (option) =>
					if option.text is '' and thisParseNumber is @currentParseNumber
						async.eachSeries @middleware, (call, done) =>
							call option, done
						, (err) =>
							return done(err) if err?
							@emit 'data', option
				, done

			allPhrasesDone = (err) =>
				if err?
					@emit 'error', err
				else if thisParseNumber is @currentParseNumber
					@emit 'end'

			async.each _.filter(@phrases, phraseIsSentence), gotData, allPhrasesDone

			return @

	run = (inputOption, done) ->
		inputOption.sentence.scope[inputOption.sentence.run](inputOption.result, done)

	nextText = (inputOption, done) ->
		match = _.reduce inputOption.match, (string, match) ->
			return string + match.string
		, ''

		matchAndSuggestion = _.reduce inputOption.suggestion.words, (string, suggestion) ->
			return string + suggestion.string
		, match

		done(null, matchAndSuggestion)

	module.exports =
		Parser: Parser
		run: run
		nextText: nextText


docs(collection): explain `false` parameter on `cursor.cound()`

    MongoQS     = require 'mongo-querystring'
    Document    = require './model/Document'
    stringify   = require('JSONStream').stringify

    collections = require('./helper/schema').types
    sentry      = require './db/sentry'
    mongo       = require '@turbasen/db-mongo'

    qs = new MongoQS
      alias:
        tag: 'tags.0'
        gruppe: 'grupper'
        endret: 'after'
      blacklist:
        api_key     : true # other use
        order       : true # reserved
        sort        : true # other use
        limit       : true # other use
        skip        : true # other use
        fields      : true # other use
        _id         : true # use API endpoint
      custom:
        bbox: 'geojson'
        near: 'geojson'
        after: 'endret'

## PARAM {collection}

    exports.param = (req, res, next, col) ->
      col = decodeURIComponent col

      if col not in collections
        return res.status(404).json message: "Type #{col} not found"

      req.type = col
      req.db   = col: mongo[col], query: {}

      next()

    exports.paramCol2 = (req, res, next, col2) ->
      if col not in collections
        return res.status(404).json message: "Type #{col2} not found"

      req.type      = col2
      req.db.col    = mongo[col2]
      req.db.query  = [{status: 'Offentlig'}, {tilbyder: req.user.provider}]

      next()

## HEAD /{collection}
## GET /{collection}

    exports.get = (req, res, next) ->

### Query

      req.db.query = req.user.query qs.parse req.query

### Fields

Always return `endret`, `lisens`, `navn`, `status`, `tilbyder`, and `tags` in
addition to `\_id` ObjectID which is always returned by MongoDB.

      fields =
        endret: true
        lisens: true
        navn: true
        status: true
        tags: true
        tilbyder: true

Parse user specified fields to be returned.

      if typeof req.query.fields is 'string' and req.query.fields
        for field in req.query.fields.split ','
          fields[field] = true

If any private fields are to be returned we need to limit the query to documents
owner by the API user to prevent exposing private data publicly.

          req.db.query.tilbyder = req.user.provider if field.substr(0,6) is 'privat'

### Sort

Limit sort to ascending or descending on `\_id`, `endret`, and `navn` since they
are indexed. Non-indexed fields will be slower. Also, don't allow ordering of
geospatial queries to prevent performance bottlenecks. From the [MongoDB
refference](http://docs.mongodb.org/manual/reference/operator/query/near/#behavior):

> $near always returns the documents sorted by distance. Any other sort order
> requires to sort the documents in memory, which can be inefficient.

      if not req.db.query.geojson
        sort = switch req.query.sort
          when '_id' then [['_id', 1]]
          when '-_id' then [['_id', -1]]
          when 'endret' then [['endret', 1]]
          when '-endret' then [['endret', -1]]
          when 'navn' then [['navn', 1]]
          when '-navn' then [['navn', -1]]
          else 'endret'

### Execute

Make new cursor object with the correct query, fields, and other options (limit,
skip, and sort).

      cursor = req.db.col.find req.db.query, fields,
        limit: Math.min (parseInt(req.query.limit, 10) or 20), 50
        skip: parseInt(req.query.skip, 10) or 0
        sort: sort

Count number of matching documents in MongoDB database. Ignore limit and skip
settings by passing `false` as the first argument to `cursor.count()`.

      cursor.count false, (err, total) ->
        return next err if err

Calculate the total number of documents that will eventually be returned (not
the total number of matching documents) since we don't know that in advanced
(due to the nature of streaming).

        count = Math.min cursor.cmd.limit, Math.max total - cursor.cmd.skip, 0

Set `Count-Return` and `Count-Total` headers so that one can use a `HEAD` query
to look up the number of matched documents for a query without the documents
them selves. This may be useful for statistics purposes.

        res.set 'Count-Return', count
        res.set 'Count-Total', total

Return to the user if this is a `HEAD` query or there are no matching documents.

        return res.sendStatus 204 if req.method is 'HEAD'
        return res.json documents: [], count: 0, total: 0 if total is 0

Stream matching documents in order to prevent loading them into memory.

        res.set 'Content-Type', 'application/json; charset=utf-8'

        op = '{"documents":['
        cl = '],"count":' + count + ',"total":' + total + '}'

        cursor.stream().pipe(stringify(op, ',', cl)).pipe(res)

## POST /{collection}

    exports.post = (req, res, next) ->
      return res.status(400).json message: 'Body is missing' if Object.keys(req.body).length is 0
      return res.status(422).json message: 'Body should be a JSON Hash' if req.body instanceof Array

      req.body.tilbyder = req.user.provider

      new Document(req.type, null).once('error', next).once 'ready', ->
        @insert req.body, (err, warn, data) ->
          if err
            return next(err) if err.name isnt 'ValidationError'

            sentry.captureDocumentError req, err

            return res.status(422).json
              document: req.body
              message: 'Validation Failed'
              errors: err.details #TODO(starefossen) document this

          res.set 'ETag', "\"#{data.checksum}\""
          res.set 'Last-Modified', new Date(data.endret).toUTCString()
          # res.set 'Location', req.get 'host'

          return res.status(201).json
            document: data
            message: 'Validation Warnings' if warn.length > 0
            warnings: warn if warn.length > 0

    MongoQS     = require 'mongo-querystring'
    Document    = require './model/Document'
    stringify   = require('JSONStream').stringify

    collections = require('./helper/schema').types
    sentry      = require './db/sentry'
    mongo       = require '@turbasen/db-mongo'

    qs = new MongoQS
      alias:
        tag: 'tags.0'
        gruppe: 'grupper'
        endret: 'after'
      blacklist:
        api_key     : true # other use
        order       : true # reserved
        sort        : true # other use
        limit       : true # other use
        skip        : true # other use
        fields      : true # other use
        _id         : true # use API endpoint
      custom:
        bbox: 'geojson'
        near: 'geojson'
        after: 'endret'

## PARAM {collection}

    exports.param = (req, res, next, col) ->
      col = decodeURIComponent col

      if col not in collections
        return res.status(404).json message: "Type #{col} not found"

      req.type = col
      req.db   = col: mongo[col], query: {}

      next()

    exports.paramCol2 = (req, res, next, col2) ->
      if col not in collections
        return res.status(404).json message: "Type #{col2} not found"

      req.type      = col2
      req.db.col    = mongo[col2]
      req.db.query  = [{status: 'Offentlig'}, {tilbyder: req.user.provider}]

      next()

## HEAD /{collection}
## GET /{collection}

    exports.get = (req, res, next) ->

### Query

      req.db.query = req.user.query qs.parse req.query

### Fields

Always return `endret`, `lisens`, `navn`, `status`, `tilbyder`, and `tags` in
addition to `\_id` ObjectID which is always returned by MongoDB.

      fields =
        endret: true
        lisens: true
        navn: true
        status: true
        tags: true
        tilbyder: true

Parse user specified fields to be returned.

      if typeof req.query.fields is 'string' and req.query.fields
        for field in req.query.fields.split ','
          fields[field] = true

If any private fields are to be returned we need to limit the query to documents
owner by the API user to prevent exposing private data publicly.

          req.db.query.tilbyder = req.user.provider if field.substr(0,6) is 'privat'

### Sort

Limit sort to ascending or descending on `\_id`, `endret`, and `navn` since they
are indexed. Non-indexed fields will be slower. Also, don't allow ordering of
geospatial queries to prevent performance bottlenecks. From the [MongoDB
refference](http://docs.mongodb.org/manual/reference/operator/query/near/#behavior):

> $near always returns the documents sorted by distance. Any other sort order
> requires to sort the documents in memory, which can be inefficient.

      if not req.db.query.geojson
        sort = switch req.query.sort
          when '_id' then [['_id', 1]]
          when '-_id' then [['_id', -1]]
          when 'endret' then [['endret', 1]]
          when '-endret' then [['endret', -1]]
          when 'navn' then [['navn', 1]]
          when '-navn' then [['navn', -1]]
          else 'endret'

### Execute

Make new cursor object with the correct query, fields, and other options (limit,
skip, and sort).

      cursor = req.db.col.find req.db.query, fields,
        limit: Math.min (parseInt(req.query.limit, 10) or 20), 50
        skip: parseInt(req.query.skip, 10) or 0
        sort: sort

Count number of matching documents in MongoDB database.

      cursor.count false, (err, total) ->
        return next err if err

Calculate the total number of documents that will eventually be returned (not
the total number of matching documents) since we don't know that in advanced
(due to the nature of streaming).

        count = Math.min cursor.cmd.limit, Math.max total - cursor.cmd.skip, 0

Set `Count-Return` and `Count-Total` headers so that one can use a `HEAD` query
to look up the number of matched documents for a query without the documents
them selves. This may be useful for statistics purposes.

        res.set 'Count-Return', count
        res.set 'Count-Total', total

Return to the user if this is a `HEAD` query or there are no matching documents.

        return res.sendStatus 204 if req.method is 'HEAD'
        return res.json documents: [], count: 0, total: 0 if total is 0

Stream matching documents in order to prevent loading them into memory.

        res.set 'Content-Type', 'application/json; charset=utf-8'

        op = '{"documents":['
        cl = '],"count":' + count + ',"total":' + total + '}'

        cursor.stream().pipe(stringify(op, ',', cl)).pipe(res)

## POST /{collection}

    exports.post = (req, res, next) ->
      return res.status(400).json message: 'Body is missing' if Object.keys(req.body).length is 0
      return res.status(422).json message: 'Body should be a JSON Hash' if req.body instanceof Array

      req.body.tilbyder = req.user.provider

      new Document(req.type, null).once('error', next).once 'ready', ->
        @insert req.body, (err, warn, data) ->
          if err
            return next(err) if err.name isnt 'ValidationError'

            sentry.captureDocumentError req, err

            return res.status(422).json
              document: req.body
              message: 'Validation Failed'
              errors: err.details #TODO(starefossen) document this

          res.set 'ETag', "\"#{data.checksum}\""
          res.set 'Last-Modified', new Date(data.endret).toUTCString()
          # res.set 'Location', req.get 'host'

          return res.status(201).json
            document: data
            message: 'Validation Warnings' if warn.length > 0
            warnings: warn if warn.length > 0


fix(collection): blacklist `?expand` query keyword

This is reserved for future use for expanding releated documents for
collections.

Close: #97

    MongoQS     = require 'mongo-querystring'
    Document    = require './model/Document'
    stringify   = require('JSONStream').stringify

    collections = require('./helper/schema').types
    sentry      = require './db/sentry'
    mongo       = require '@turbasen/db-mongo'

    qs = new MongoQS
      alias:
        tag: 'tags.0'
        gruppe: 'grupper'
        endret: 'after'
        order: 'sort'
      blacklist:
        api_key     : true # other use
        sort        : true # other use
        limit       : true # other use
        skip        : true # other use
        fields      : true # other use
        expand      : true # other use
        _id         : true # use API endpoint
      custom:
        bbox: 'geojson'
        near: 'geojson'
        after: 'endret'

## PARAM {collection}

    exports.param = (req, res, next, col) ->
      col = decodeURIComponent col

      if col not in collections
        return res.status(404).json message: "Type #{col} not found"

      req.type = col
      req.db   = col: mongo[col], query: {}

      next()

    exports.paramCol2 = (req, res, next, col2) ->
      if col not in collections
        return res.status(404).json message: "Type #{col2} not found"

      req.type      = col2
      req.db.col    = mongo[col2]
      req.db.query  = [{status: 'Offentlig'}, {tilbyder: req.user.provider}]

      next()

## HEAD /{collection}
## GET /{collection}

    exports.get = (req, res, next) ->

### Query

      req.db.query = req.user.query qs.parse req.query

### Fields

Always return `endret`, `lisens`, `navn`, `status`, `tilbyder`, and `tags` in
addition to `\_id` ObjectID which is always returned by MongoDB.

      fields =
        endret: true
        lisens: true
        navn: true
        status: true
        tags: true
        tilbyder: true

Parse user specified fields to be returned.

      if typeof req.query.fields is 'string' and req.query.fields
        for field in req.query.fields.split ','
          fields[field] = true

If any private fields are to be returned we need to limit the query to documents
owner by the API user to prevent exposing private data publicly.

          req.db.query.tilbyder = req.user.provider if field.substr(0,6) is 'privat'

### Sort

Limit sort to ascending or descending on `\_id`, `endret`, and `navn` since they
are indexed. Non-indexed fields will be slower. Also, don't allow ordering of
geospatial queries to prevent performance bottlenecks. From the [MongoDB
refference](http://docs.mongodb.org/manual/reference/operator/query/near/#behavior):

> $near always returns the documents sorted by distance. Any other sort order
> requires to sort the documents in memory, which can be inefficient.

      if not req.db.query.geojson
        sort = switch req.query.sort
          when '_id' then [['_id', 1]]
          when '-_id' then [['_id', -1]]
          when 'endret' then [['endret', 1]]
          when '-endret' then [['endret', -1]]
          when 'navn' then [['navn', 1]]
          when '-navn' then [['navn', -1]]
          else 'endret'

### Execute

Make new cursor object with the correct query, fields, and other options (limit,
skip, and sort).

      cursor = req.db.col.find req.db.query, fields,
        limit: Math.min (parseInt(req.query.limit, 10) or 20), 50
        skip: parseInt(req.query.skip, 10) or 0
        sort: sort

Count number of matching documents in MongoDB database. Ignore limit and skip
settings by passing `false` as the first argument to `cursor.count()`.

      cursor.count false, (err, total) ->
        return next err if err

Calculate the total number of documents that will eventually be returned (not
the total number of matching documents) since we don't know that in advanced
(due to the nature of streaming).

        count = Math.min cursor.cmd.limit, Math.max total - cursor.cmd.skip, 0

Set `Count-Return` and `Count-Total` headers so that one can use a `HEAD` query
to look up the number of matched documents for a query without the documents
them selves. This may be useful for statistics purposes.

        res.set 'Count-Return', count
        res.set 'Count-Total', total

Return to the user if this is a `HEAD` query or there are no matching documents.

        return res.sendStatus 204 if req.method is 'HEAD'
        return res.json documents: [], count: 0, total: 0 if total is 0

Stream matching documents in order to prevent loading them into memory.

        res.set 'Content-Type', 'application/json; charset=utf-8'

        op = '{"documents":['
        cl = '],"count":' + count + ',"total":' + total + '}'

        cursor.stream().pipe(stringify(op, ',', cl)).pipe(res)

## POST /{collection}

    exports.post = (req, res, next) ->
      return res.status(400).json message: 'Body is missing' if Object.keys(req.body).length is 0
      return res.status(422).json message: 'Body should be a JSON Hash' if req.body instanceof Array

      req.body.tilbyder = req.user.provider

      new Document(req.type, null).once('error', next).once 'ready', ->
        @insert req.body, (err, warn, data) ->
          if err
            return next(err) if err.name isnt 'ValidationError'

            sentry.captureDocumentError req, err

            return res.status(422).json
              document: req.body
              message: 'Validation Failed'
              errors: err.details #TODO(starefossen) document this

          res.set 'ETag', "\"#{data.checksum}\""
          res.set 'Last-Modified', new Date(data.endret).toUTCString()
          # res.set 'Location', req.get 'host'

          return res.status(201).json
            document: data
            message: 'Validation Warnings' if warn.length > 0
            warnings: warn if warn.length > 0

    MongoQS     = require 'mongo-querystring'
    Document    = require './model/Document'
    stringify   = require('JSONStream').stringify

    collections = require('./helper/schema').types
    sentry      = require './db/sentry'
    mongo       = require '@turbasen/db-mongo'

    qs = new MongoQS
      alias:
        tag: 'tags.0'
        gruppe: 'grupper'
        endret: 'after'
        order: 'sort'
      blacklist:
        api_key     : true # other use
        sort        : true # other use
        limit       : true # other use
        skip        : true # other use
        fields      : true # other use
        _id         : true # use API endpoint
      custom:
        bbox: 'geojson'
        near: 'geojson'
        after: 'endret'

## PARAM {collection}

    exports.param = (req, res, next, col) ->
      col = decodeURIComponent col

      if col not in collections
        return res.status(404).json message: "Type #{col} not found"

      req.type = col
      req.db   = col: mongo[col], query: {}

      next()

    exports.paramCol2 = (req, res, next, col2) ->
      if col not in collections
        return res.status(404).json message: "Type #{col2} not found"

      req.type      = col2
      req.db.col    = mongo[col2]
      req.db.query  = [{status: 'Offentlig'}, {tilbyder: req.user.provider}]

      next()

## HEAD /{collection}
## GET /{collection}

    exports.get = (req, res, next) ->

### Query

      req.db.query = req.user.query qs.parse req.query

### Fields

Always return `endret`, `lisens`, `navn`, `status`, `tilbyder`, and `tags` in
addition to `\_id` ObjectID which is always returned by MongoDB.

      fields =
        endret: true
        lisens: true
        navn: true
        status: true
        tags: true
        tilbyder: true

Parse user specified fields to be returned.

      if typeof req.query.fields is 'string' and req.query.fields
        for field in req.query.fields.split ','
          fields[field] = true

If any private fields are to be returned we need to limit the query to documents
owner by the API user to prevent exposing private data publicly.

          req.db.query.tilbyder = req.user.provider if field.substr(0,6) is 'privat'

### Sort

Limit sort to ascending or descending on `\_id`, `endret`, and `navn` since they
are indexed. Non-indexed fields will be slower. Also, don't allow ordering of
geospatial queries to prevent performance bottlenecks. From the [MongoDB
refference](http://docs.mongodb.org/manual/reference/operator/query/near/#behavior):

> $near always returns the documents sorted by distance. Any other sort order
> requires to sort the documents in memory, which can be inefficient.

      if not req.db.query.geojson
        sort = switch req.query.sort
          when '_id' then [['_id', 1]]
          when '-_id' then [['_id', -1]]
          when 'endret' then [['endret', 1]]
          when '-endret' then [['endret', -1]]
          when 'navn' then [['navn', 1]]
          when '-navn' then [['navn', -1]]
          else 'endret'

### Execute

Make new cursor object with the correct query, fields, and other options (limit,
skip, and sort).

      cursor = req.db.col.find req.db.query, fields,
        limit: Math.min (parseInt(req.query.limit, 10) or 20), 50
        skip: parseInt(req.query.skip, 10) or 0
        sort: sort

Count number of matching documents in MongoDB database. Ignore limit and skip
settings by passing `false` as the first argument to `cursor.count()`.

      cursor.count false, (err, total) ->
        return next err if err

Calculate the total number of documents that will eventually be returned (not
the total number of matching documents) since we don't know that in advanced
(due to the nature of streaming).

        count = Math.min cursor.cmd.limit, Math.max total - cursor.cmd.skip, 0

Set `Count-Return` and `Count-Total` headers so that one can use a `HEAD` query
to look up the number of matched documents for a query without the documents
them selves. This may be useful for statistics purposes.

        res.set 'Count-Return', count
        res.set 'Count-Total', total

Return to the user if this is a `HEAD` query or there are no matching documents.

        return res.sendStatus 204 if req.method is 'HEAD'
        return res.json documents: [], count: 0, total: 0 if total is 0

Stream matching documents in order to prevent loading them into memory.

        res.set 'Content-Type', 'application/json; charset=utf-8'

        op = '{"documents":['
        cl = '],"count":' + count + ',"total":' + total + '}'

        cursor.stream().pipe(stringify(op, ',', cl)).pipe(res)

## POST /{collection}

    exports.post = (req, res, next) ->
      return res.status(400).json message: 'Body is missing' if Object.keys(req.body).length is 0
      return res.status(422).json message: 'Body should be a JSON Hash' if req.body instanceof Array

      req.body.tilbyder = req.user.provider

      new Document(req.type, null).once('error', next).once 'ready', ->
        @insert req.body, (err, warn, data) ->
          if err
            return next(err) if err.name isnt 'ValidationError'

            sentry.captureDocumentError req, err

            return res.status(422).json
              document: req.body
              message: 'Validation Failed'
              errors: err.details #TODO(starefossen) document this

          res.set 'ETag', "\"#{data.checksum}\""
          res.set 'Last-Modified', new Date(data.endret).toUTCString()
          # res.set 'Location', req.get 'host'

          return res.status(201).json
            document: data
            message: 'Validation Warnings' if warn.length > 0
            warnings: warn if warn.length > 0


Use the correct data source for the current match

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 = Data.matchSchedule.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()


Document ModelMixin and RecordMixin

Document ModelMixin and RecordMixin

# /api/App Components/Batman.StorageAdapter

`Batman.StorageAdapter`s handle persistence of [`Batman.Model`s](/docs/api/batman.model.html). Any `Batman.Model` which will be created, read, updated or deleted must have a storage adapter, which is declared with [`Batman.Model@persist`](/docs/api/batman.model.html#class_function_persist):

```coffeescript
class App.Superhero extends Batman.Model
  @persist Batman.RestStorage # a StorageAdapter subclass
```

__Note:__ `@persist` instantiates a StorageAdapter instance during model definition, so it will use `@storageKey` and `@resourceName` from the model where it was instantiated, but it won't play well with inheritance. For example:

```coffeescript
class App.Model extends Batman.Model
  @storageKey: 'model'
  @persist Batman.LocalStorage

class App.Superhero extends App.Model
  # have to do it again here, or else it will use @storageKey of 'model'
  @storageKey: 'superhero'
  @persist Batman.LocalStorage
```

### Batman's Included StorageAdapters

Batman ships with a few storage adapters to get you up and coding quickly:

- `Batman.LocalStorage` uses [`window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage#localStorage) to persist records.
- `Batman.SessionStorage` extends `Batman.LocalStorage` and uses [`window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage#sessionStorage) to persist records.
- `Batman.RestStorage` uses [HTTP REST](http://en.wikipedia.org/wiki/REST) to persist records, mapping HTTP verbs to storage operations and handling HTTP response codes appropriately. _Note: Because `Batman.RestStorage` depends on [`Batman.Request`](/docs/api/batman.request.html), you'll need a [platform library](/docs/api/batman.request.html#platform_request_implementation_libraries) to implement `Batman.Request`._

### Batman.RailsStorage
Also, `Batman.RailsStorage` (available in separate file: [Coffee](https://github.com/batmanjs/batman/blob/master/src/extras/batman.rails.coffee), JS) extends `Batman.RestStorage` and provides some helpful Rails integrations:

- If the response's status code is 422 (`Unprocessable Entity`), Any `errors` defined in the JSON response will be added to the `Batman.Model`'s [errors](/docs/api/batman.model.html#prototype_accessor_errors).
- By default, Batman will get the Rails CSRF token from the `<head>`and send it as a request header. (This can be prevented by setting `Batman.config.protectFromCSRF = false`.)
- If a record's URL doesn't end in `.json` and doesn't include a query string, `.json` is automatically appended to the URL.

_Note: Like `Batman.RestStorage`, `Batman.RailsStorage` depends on [`Batman.Request`](/docs/api/batman.request.html), so you'll need a [platform library](/docs/api/batman.request.html#platform_request_implementation_libraries) to implement `Batman.Request`._

If you're using Batman and Rails, be sure to check out the [batman-rails](https://github.com/batmanjs/batman-rails) gem.

## Storage Errors

`Batman.StorageAdapter` throws [storage errors](/docs/api/batman.storageadapter_errors.html) when its operations fail. You can catch these errors with [`Batman.Controller.catchError`](/docs/api/batman.controller.html#class_function_catcherror).

## Subclassing Batman.StorageAdapter

You may want to customize Batman's storage operations for your own app, for example:

- Adding before- and after-operation callbacks
- Overriding default storage operations

To do this, extend `Batman.StorageAdapter` (or one of the provided subclasses), then use your storage adapter to persist your models.
```
class App.HeroicStorageAdapter extends Batman.LocalStorage
  # filters, overrides

class App.Superhero extends Batman.Model
  @persist App.HeroicStorageAdapter
```

## ModelMixin and RecordMixin

A storage adapter may also have a class property called `ModelMixin`. If that property exists, in will be mixed into model classes that are persisted with that adapter. `RecordMixin` works the same way: it will be mixed into the prototype of models that are persisted with this adapter.

For example, this is how `Batman.RestStorage` provides URL-related functions to models that use it for persistence.

### `env` and `next`

Storage operations and callbacks each take two arguments: `env` and `next`.

`env` is a vanilla JS object which is passed to each function in the chain. `Batman.StorageAdapter` sets these attributes on `env`:

- `env.subject` is the `Batman.Model` record which had the storage operation called on it.
- `env.options` contains the options passed to the operation on the subject.
- `env.action` is the storage operation that will be executed by the storage adapter.
- `env.error` stores any errors that occur during the chain. It becomes the first argument to the operation callback.
- `env.result` contains the record(s) returned by the operation and should be set by the storage adapter implementation. It becomes the second argument to the operation's callback.

Storage adapters may use `env` to collect any extra information needed by their operations. For example, `Batman.RestStorage` adds some attributes to `env` and `env.options`:

- `env.request` is the `Batman.Request` which implements the storage operation.
- `env.options.method` is the HTTP method used by `Batman.Request` to implement the storage operation.
- `env.options.url` is the URL used by `Batman.Request` to implement the storage operation.

`next` is a reference to the next function in the call chain for the current storage operation. `Batman.StorageAdapter` uses this to execute before-filters, storage operations and after-filters in the correct sequence.

`next` should be called (`next()`) when the current function has completed. This indicates that the operation should proceed with the next function in its call chain. To ensure the completion of the call chain, consider wrapping your function in [`@skipIfError`](/docs/api/batman.storageadapter.html#class_function_skipiferror).

### Adding Callbacks

Callbacks can be registered with [`before`](/docs/api/batman.storageadapter.html#prototype_function_before) and [`after`](/docs/api/batman.storageadapter.html#prototype_function_after) on any storage operation (listed below) or `'all'` (which runs the callback before or after any operation). Callbacks accept two arguments, `env` and `next`, discussed below.

```coffeescript
class App.SpecificHeaderStorageAdapter extends Batman.RestStorage
  # include a specific header in all requests:
  @::before 'all', (env, next) ->
    headers = env.options.headers ||= {}
    headers["App-Specific-Header"] = App.getSpecificHeader()
    next()

  @::after 'create', (env, next) ->
    console.log "A #{env.subject.constructor.name} was created!"
    next()
```

## Storage Operations

Any `StorageAdapter` must implement these operations as instance methods, for example:

```
class App.ModifiedStorageAdapter extends Batman.RestStorage
  destroy: (env, next) ->
    # your custom destroy operation
    next()
```

When a record is saved, destroyed or loaded, `Batman.StorageAdapter` invokes these methods. Although you may reimplement them, you probably don't need to call them in your application code.

## ::create(env : Object, next : Function)
Called to save a new record. When `save` is called on a record and `isNew` returns `true`, the storage adapter's `create` method is called.

## ::read(env : Object, next : Function)
Called to load a record from storage.

## ::update(env : Object, next : Function)
Called to update an existing record. When `save` is called on a record and `isNew` returns `false`, the storage adapter's `update` method is called.

## ::destroy(env : Object, next : Function)
Called to destroy an existing record.

## ::readAll(env : Object, next : Function)
Called to load all records of a particular type from storage.

## Other Useful Methods

## @skipIfError(wrappedFunction : Function )

Wraps a function, bypassing the function body and calling next() if an error has already occurred.

    test "StorageAdapter@skipIfError doesn't call the function if env.error?", ->
      insideFunction = createSpy()
      nextFunction = createSpy()

      functionWithWrapper = Batman.LocalStorage.skipIfError (env, next) ->
        insideFunction()

      dummyEnv = {error: true}

      functionWithWrapper(dummyEnv, nextFunction)

      equal dummyEnv.error?, true, 'an error is present'
      equal insideFunction.called, false, 'insideFunction was skipped'
      equal nextFunction.called, true, 'nextFunction was called'

Inside a storage adapter definition, this function is referenced as `@skipIfError`, for example:

```
class App.SpecialStorageAdapter extends Batman.RestStorage
  @::before 'create', @skipIfError (env, next) ->
    console.log("This will be skipped if env.error? is true!")
    next()
```
## ::constructor(model: Function) : StorageAdapter

Returns a `StorageAdapter` attached to `model`, which should be a subclass of `Batman.Model`.

## ::.model : Function

Returns the `Batman.Model` passed to the constructor.

## ::before (keys... : Strings, filter : Function)
Registers `filter` as a before-operation callback for the storage operations named by `keys...`.

## ::after (keys... : Strings, filter : Function)
Registers `filter` as a after-operation callback for the storage operations named by `keys...`.

## ::getRecordFromData(attributes: Object, constructor[=@model] : Function) : Model

Finds or creates an instance of `constructor` with `attributes` and returns it. Delegated to `Model.createFromJSON`.

## ::getRecordsFromData(attributesArray: Array, constructor[=@model] : Function) : Array

Finds or creates instances of `constructor` for each item in `attributesArray` and returns the resulting records. Delegated to `Model.createMultipleFromJSON`.

# /api/App Components/Batman.StorageAdapter

`Batman.StorageAdapter`s handle persistence of [`Batman.Model`s](/docs/api/batman.model.html). Any `Batman.Model` which will be created, read, updated or deleted must have a storage adapter, which is declared with [`Batman.Model@persist`](/docs/api/batman.model.html#class_function_persist):

```coffeescript
class App.Superhero extends Batman.Model
  @persist Batman.RestStorage # a StorageAdapter subclass
```

__Note:__ `@persist` instantiates a StorageAdapter instance during model definition, so it will use `@storageKey` and `@resourceName` from the model where it was instantiated, but it won't play well with inheritance. For example:

```coffeescript
class App.Model extends Batman.Model
  @storageKey: 'model'
  @persist Batman.LocalStorage

class App.Superhero extends App.Model
  # have to do it again here, or else it will use @storageKey of 'model'
  @storageKey: 'superhero'
  @persist Batman.LocalStorage
```

### Batman's Included StorageAdapters

Batman ships with a few storage adapters to get you up and coding quickly:

- `Batman.LocalStorage` uses [`window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage#localStorage) to persist records.
- `Batman.SessionStorage` extends `Batman.LocalStorage` and uses [`window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage#sessionStorage) to persist records.
- `Batman.RestStorage` uses [HTTP REST](http://en.wikipedia.org/wiki/REST) to persist records, mapping HTTP verbs to storage operations and handling HTTP response codes appropriately. _Note: Because `Batman.RestStorage` depends on [`Batman.Request`](/docs/api/batman.request.html), you'll need a [platform library](/docs/api/batman.request.html#platform_request_implementation_libraries) to implement `Batman.Request`._

### Batman.RailsStorage
Also, `Batman.RailsStorage` (available in separate file: [Coffee](https://github.com/batmanjs/batman/blob/master/src/extras/batman.rails.coffee), JS) extends `Batman.RestStorage` and provides some helpful Rails integrations:

- If the response's status code is 422 (`Unprocessable Entity`), Any `errors` defined in the JSON response will be added to the `Batman.Model`'s [errors](/docs/api/batman.model.html#prototype_accessor_errors).
- By default, Batman will get the Rails CSRF token from the `<head>`and send it as a request header. (This can be prevented by setting `Batman.config.protectFromCSRF = false`.)
- If a record's URL doesn't end in `.json` and doesn't include a query string, `.json` is automatically appended to the URL.

_Note: Like `Batman.RestStorage`, `Batman.RailsStorage` depends on [`Batman.Request`](/docs/api/batman.request.html), so you'll need a [platform library](/docs/api/batman.request.html#platform_request_implementation_libraries) to implement `Batman.Request`._

If you're using Batman and Rails, be sure to check out the [batman-rails](https://github.com/batmanjs/batman-rails) gem.

## Storage Errors

`Batman.StorageAdapter` throws [storage errors](/docs/api/batman.storageadapter_errors.html) when its operations fail. You can catch these errors with [`Batman.Controller.catchError`](/docs/api/batman.controller.html#class_function_catcherror).

## Subclassing Batman.StorageAdapter

You may want to customize Batman's storage operations for your own app, for example:

- Adding before- and after-operation callbacks
- Overriding default storage operations

To do this, extend `Batman.StorageAdapter` (or one of the provided subclasses), then use your storage adapter to persist your models.
```
class App.HeroicStorageAdapter extends Batman.LocalStorage
  # filters, overrides

class App.Superhero extends Batman.Model
  @persist App.HeroicStorageAdapter
```
### `env` and `next`

Storage operations and callbacks each take two arguments: `env` and `next`.

`env` is a vanilla JS object which is passed to each function in the chain. `Batman.StorageAdapter` sets these attributes on `env`:

- `env.subject` is the `Batman.Model` record which had the storage operation called on it.
- `env.options` contains the options passed to the operation on the subject.
- `env.action` is the storage operation that will be executed by the storage adapter.
- `env.error` stores any errors that occur during the chain. It becomes the first argument to the operation callback.
- `env.result` contains the record(s) returned by the operation and should be set by the storage adapter implementation. It becomes the second argument to the operation's callback.

Storage adapters may use `env` to collect any extra information needed by their operations. For example, `Batman.RestStorage` adds some attributes to `env` and `env.options`:

- `env.request` is the `Batman.Request` which implements the storage operation.
- `env.options.method` is the HTTP method used by `Batman.Request` to implement the storage operation.
- `env.options.url` is the URL used by `Batman.Request` to implement the storage operation.

`next` is a reference to the next function in the call chain for the current storage operation. `Batman.StorageAdapter` uses this to execute before-filters, storage operations and after-filters in the correct sequence.

`next` should be called (`next()`) when the current function has completed. This indicates that the operation should proceed with the next function in its call chain. To ensure the completion of the call chain, consider wrapping your function in [`@skipIfError`](/docs/api/batman.storageadapter.html#class_function_skipiferror).

### Adding Callbacks

Callbacks can be registered with [`before`](/docs/api/batman.storageadapter.html#prototype_function_before) and [`after`](/docs/api/batman.storageadapter.html#prototype_function_after) on any storage operation (listed below) or `'all'` (which runs the callback before or after any operation). Callbacks accept two arguments, `env` and `next`, discussed below.

```coffeescript
class App.SpecificHeaderStorageAdapter extends Batman.RestStorage
  # include a specific header in all requests:
  @::before 'all', (env, next) ->
    headers = env.options.headers ||= {}
    headers["App-Specific-Header"] = App.getSpecificHeader()
    next()

  @::after 'create', (env, next) ->
    console.log "A #{env.subject.constructor.name} was created!"
    next()
```

## Storage Operations

Any `StorageAdapter` must implement these operations as instance methods, for example:

```
class App.ModifiedStorageAdapter extends Batman.RestStorage
  destroy: (env, next) ->
    # your custom destroy operation
    next()
```

When a record is saved, destroyed or loaded, `Batman.StorageAdapter` invokes these methods. Although you may reimplement them, you probably don't need to call them in your application code.

## ::create(env : Object, next : Function)
Called to save a new record. When `save` is called on a record and `isNew` returns `true`, the storage adapter's `create` method is called.

## ::read(env : Object, next : Function)
Called to load a record from storage.

## ::update(env : Object, next : Function)
Called to update an existing record. When `save` is called on a record and `isNew` returns `false`, the storage adapter's `update` method is called.

## ::destroy(env : Object, next : Function)
Called to destroy an existing record.

## ::readAll(env : Object, next : Function)
Called to load all records of a particular type from storage.

## Other Useful Methods

## @skipIfError(wrappedFunction : Function )

Wraps a function, bypassing the function body and calling next() if an error has already occurred.

    test "StorageAdapter@skipIfError doesn't call the function if env.error?", ->
      insideFunction = createSpy()
      nextFunction = createSpy()

      functionWithWrapper = Batman.LocalStorage.skipIfError (env, next) ->
        insideFunction()

      dummyEnv = {error: true}

      functionWithWrapper(dummyEnv, nextFunction)

      equal dummyEnv.error?, true, 'an error is present'
      equal insideFunction.called, false, 'insideFunction was skipped'
      equal nextFunction.called, true, 'nextFunction was called'

Inside a storage adapter definition, this function is referenced as `@skipIfError`, for example:

```
class App.SpecialStorageAdapter extends Batman.RestStorage
  @::before 'create', @skipIfError (env, next) ->
    console.log("This will be skipped if env.error? is true!")
    next()
```
## ::constructor(model: Function) : StorageAdapter

Returns a `StorageAdapter` attached to `model`, which should be a subclass of `Batman.Model`.

## ::.model : Function

Returns the `Batman.Model` passed to the constructor.

## ::before (keys... : Strings, filter : Function)
Registers `filter` as a before-operation callback for the storage operations named by `keys...`.

## ::after (keys... : Strings, filter : Function)
Registers `filter` as a after-operation callback for the storage operations named by `keys...`.

## ::getRecordFromData(attributes: Object, constructor[=@model] : Function) : Model

Finds or creates an instance of `constructor` with `attributes` and returns it. Delegated to `Model.createFromJSON`.

## ::getRecordsFromData(attributesArray: Array, constructor[=@model] : Function) : Array

Finds or creates instances of `constructor` for each item in `attributesArray` and returns the resulting records. Delegated to `Model.createMultipleFromJSON`.