Update markdown

Update markdown

    _self = this

    if console?.log?
     LOG = console.log.bind console
    else
     LOG = -> null

    if console?.error?
     ERROR_LOG = console.error.bind console
    else
     ERROR_LOG = -> null

    POLL_TYPE =
     progress: true

## Response class

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

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

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

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

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

     _multipleResponse: ->
      responseList = []
      callbacks = []
      for d in @queue
       responseList.push
        status: d.method
        data: d.data
        options: d.options
       if d.callback?
        callbacks.push d.callback

      done = ->
       for callback in callbacks
        callback()

      if @port.isStreaming
       @port.respondMultiple this, responseList, @options, done
      else if @fresh
       @port.respondMultiple this, responseList, @options, done
       @fresh = false

      @queue = []

     _handleQueue: ->
      return unless @queue.length > 0
      return if not @port.isStreaming and not @fresh

      if @queue.length > 1
       return @_multipleResponse()

      d = @queue[0]

      if @port.isStreaming
       @port.respond this, d.method, d.data, d.options, @options, d.callback
      else if @fresh
       @port.respond this, d.method, d.data, d.options, @options, d.callback
       @fresh = false

      @queue = []

## Call class

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

     handle: (data, options) ->
      if not @callbacks[options.status]?
       return if options.status is 'progress'
       #Handled by caller
       throw new Error "No callback registered #{@method} #{options.status}"
      self = this
      setTimeout ->
       try
        self.callbacks[options.status] data, options
       catch e
        if self.port.onerror?
         self.port.onerror e
        else
         throw e
      , 0

      if POLL_TYPE[options.status]?
       return false
      else
       return true

## Port base class

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

     isStreaming: true

     onCallError: (msg, options) ->
      callsCache = @callsCache
      @callsCache = {}
      for id, call of callsCache
       if not call.callbacks.fail?
        ERROR_LOG 'fail callback not registered', call.method, call.data
       else
        call.callbacks.fail error: 'connectionError', msg: msg, options: options, {}

      @errorCallback msg, options

     onHandleError: (msg, data, options) ->
      @errorCallback msg, data
      response = new Response data, this, options
      response.fail msg

     errorCallback: (msg, options) ->
      ERROR_LOG msg, options

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

### Send RPC call

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

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

      params = @_createCall method, data, callbacks, options
      @_send params, callbacks

### Respond to a RPC call

     respond: (response, status, data, options = {}, portOptions = {}, callback = null) ->
      if not POLL_TYPE[status]?
       delete @responses[response.id]

      for f in @wrappers.respond
       return unless f.apply this, [response, status, data, options, portOptions]

      @_respond (@_createResponse response, status, data, options), portOptions, callback

     respondMultiple: (response, list, portOptions = {}, callback = null) ->
      for d in list
       if not POLL_TYPE[d.status]?
        delete @responses[response.id]
        break

      data = []
      for d in list
       cancel = false
       d.options ?= {}
       for f in @wrappers.respond
        r = f.apply this, [response, d.status, d.data, d.options, portOptions]
        if not r
         cancel = true
         break

        continue if cancel

       data.push @_createResponse response, d.status, d.data, d.options

      return if data.length is 0

      data =
       type: 'list'
       list: data

      @_respond data, portOptions, callback

### Create Call object
This is a private function

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

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

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

### Create Response object

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

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

      return params

### Add handler

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

### Handle incoming message

     _handleMessage: (data, options, last = true) ->
      switch data.type
       when 'list'
        for d, i in data.list
         @_handleMessage d, options, (last and i + 1 is data.list.length)
       when 'response'
        try
         @_handleResponse data, options, last
        catch e
         @onerror? e
       when 'call'
        try
         @_handleCall data, options, last
        catch e
         @onerror? e
       when 'poll'
        try
         @_handlePoll data, options, last
        catch e
         @onerror? e

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

     _handleResponse: (data, options) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       #Cannot reply
       @errorCallback "Response without call: #{data.id}", data
       return
      try
       if @callsCache[data.id].handle data.data, data
        delete @callsCache[data.id]
      catch e
       @errorCallback e.message, data
       delete @callsCache[data.id]

     _handlePoll: (data, options) ->
      @onHandleError "Poll not implemented", data, options

## WorkerPort class
Used for browser and worker

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

     _send: (data) ->
      dd = data.data
      if dd? and dd._transferList?
       transferList = dd._transferList
       delete dd._transferList
      else
       transferList = []
      @worker.postMessage data, transferList

     _respond: (data, options, callback) ->
      dd = data.data
      if dd? and dd._transferList?
       transferList = dd._transferList
       delete dd._transferList
      else
       transferList = []
      @worker.postMessage data, transferList
      callback?()

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

## FramePort class
Used for browser and worker

    class FramePort extends Port
     constructor: (source, dest) ->
      super()
      @source = source
      @dest = dest
      @source.addEventListener 'message', @_onMessage.bind this

     _send: (data) ->
      @dest.postMessage data, '*'

     _respond: (data, options, callback) ->
      @dest.postMessage data, '*'
      callback?()

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

## SocketPort class

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

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

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

## ServerSocketPort class

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

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

## AJAX class

    class AjaxHttpPort extends Port
     constructor: (options) ->
      super()
      @protocol = options.protocol
      @host = options.host
      @port = options.port
      @path = options.path ? '/'
      @url = @path
      if @protocol?
       if @host? and not @port?
        @url = "#{@protocol}://#{@host}#{@path}"
       else if @host? and @port?
        @url = "#{@protocol}://#{@host}:#{@port}#{@path}"
      else
       if @host? and not @port?
        @url = "//#{@host}#{@path}"
       else if @host? and @port?
        @url = "//#{@host}:#{@port}#{@path}"

     isStreaming: false

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

       @_handleMessage jsonData, xhr: xhr
      else
       @onCallError 'Cannot connect to server'

     _respond: (data, options, callback) ->
      @errorCallback 'AJAX cannot respond', data
      callback?()

     _send: (data) ->
      data = JSON.stringify data
      xhr = new XMLHttpRequest
      xhr.open 'POST', @url
      xhr.onreadystatechange = =>
       @_onRequest xhr
      xhr.setRequestHeader 'Accept', 'application/json'
      #xhr.setRequestHeader 'Content-Type', 'application/json'
      xhr.send data

     _handleResponse: (data, options, last = true) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       @errorCallback "Response without call: #{data.id}", data
       return
      call = @callsCache[data.id]
      try
       if call.handle data.data, data
        delete @callsCache[data.id]
       else if last
        params =
         type: 'poll'
         id: call.id
        params[k] = v for k, v of call.options
        @_send params
      catch e
       @errorCallback e.message, data
       delete @callsCache[data.id]

## NodeHttpPort class

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

     isStreaming: false

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

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

       @_handleMessage jsonData, response: res

     _respond: (data, options, callback) ->
      data = JSON.stringify data
      res = options.response
      res.setHeader 'content-length', Buffer.byteLength data, 'utf8'
      if callback?
       res.once 'finish', ->
        callback()
       res.once 'close', ->
        callback()

      res.write data
      res.end()

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

      req = @http.request options, @_onRequest.bind this
      delete options.headers['content-length']
      req.on 'error', (e) =>
       try
        callbacks.fail? e
       catch err
        @onerror? err

      req.write data
      req.end()

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

## NodeHttpServerPort class

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

     isStreaming: false

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

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

       @_handleMessage jsonData, response: res, request: req

     _respond: (data, options, callback) ->
      if @allowOrigin?
       options.response.setHeader 'Access-Control-Allow-Origin', @allowOrigin

      accept = options.request.headers['accept-encoding']
      accept ?= ''
      if not @zlib?
       accept = ''

      data = JSON.stringify data
      buffer = new Buffer data, 'utf8'
      if accept.match /\bgzip\b/
       options.response.setHeader 'content-encoding', 'gzip'
       @zlib.gzip buffer, (err, result) =>
        if err?
         return @errorCallback 'GZipeError', e
        @_sendBuffer result, options.response, callback
      else if accept.match /\bdeflate\b/
       options.response.setHeader 'content-encoding', 'deflate'
       @zlib.deflate buffer, (err, result) =>
        if err?
         return @errorCallback 'DeflateError', e
        @_sendBuffer result, options.response, callback
      else
       @_sendBuffer buffer, options.response, callback

     _sendBuffer: (buf, res, callback) ->
       res.setHeader 'content-length', buf.length
       if callback?
        res.once 'finish', ->
         callback()
        res.once 'close', ->
         callback()

       res.write buf
       res.end()

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

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

## NodeHttpsServerPort class

    class NodeHttpsServerPort extends NodeHttpServerPort
     constructor: (options, http) ->
      super options, http
      @_key = options.key
      @_cert = options.cert

     listen: ->
      options =
       key: @_key
       cert: @_cert

      @server = @http.createServer options, @_onRequest.bind this
      @server.listen @port

# IO Module

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

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

     Helpers:
      progress: (from, to, func) ->
       (progress, data) ->
        func? progress * (to - from) + from, data

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

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

    _self = this

    if console?.log?
     LOG = console.log.bind console
    else
     LOG = -> null

    if console?.error?
     ERROR_LOG = console.error.bind console
    else
     ERROR_LOG = -> null

    POLL_TYPE =
     progress: true

##Response class

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

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

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

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

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

     _multipleResponse: ->
      responseList = []
      callbacks = []
      for d in @queue
       responseList.push
        status: d.method
        data: d.data
        options: d.options
       if d.callback?
        callbacks.push d.callback

      done = ->
       for callback in callbacks
        callback()

      if @port.isStreaming
       @port.respondMultiple this, responseList, @options, done
      else if @fresh
       @port.respondMultiple this, responseList, @options, done
       @fresh = false

      @queue = []

     _handleQueue: ->
      return unless @queue.length > 0
      return if not @port.isStreaming and not @fresh

      if @queue.length > 1
       return @_multipleResponse()

      d = @queue[0]

      if @port.isStreaming
       @port.respond this, d.method, d.data, d.options, @options, d.callback
      else if @fresh
       @port.respond this, d.method, d.data, d.options, @options, d.callback
       @fresh = false

      @queue = []

##Call class

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

     handle: (data, options) ->
      if not @callbacks[options.status]?
       return if options.status is 'progress'
       #Handled by caller
       throw new Error "No callback registered #{@method} #{options.status}"
      self = this
      setTimeout ->
       try
        self.callbacks[options.status] data, options
       catch e
        if self.port.onerror?
         self.port.onerror e
        else
         throw e
      , 0

      if POLL_TYPE[options.status]?
       return false
      else
       return true

##Port base class

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

     isStreaming: true

     onCallError: (msg, options) ->
      callsCache = @callsCache
      @callsCache = {}
      for id, call of callsCache
       if not call.callbacks.fail?
        ERROR_LOG 'fail callback not registered', call.method, call.data
       else
        call.callbacks.fail error: 'connectionError', msg: msg, options: options, {}

      @errorCallback msg, options

     onHandleError: (msg, data, options) ->
      @errorCallback msg, data
      response = new Response data, this, options
      response.fail msg

     errorCallback: (msg, options) ->
      ERROR_LOG msg, options

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

###Send RPC call

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

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

      params = @_createCall method, data, callbacks, options
      @_send params, callbacks

###Respond to a RPC call

     respond: (response, status, data, options = {}, portOptions = {}, callback = null) ->
      if not POLL_TYPE[status]?
       delete @responses[response.id]

      for f in @wrappers.respond
       return unless f.apply this, [response, status, data, options, portOptions]

      @_respond (@_createResponse response, status, data, options), portOptions, callback

     respondMultiple: (response, list, portOptions = {}, callback = null) ->
      for d in list
       if not POLL_TYPE[d.status]?
        delete @responses[response.id]
        break

      data = []
      for d in list
       cancel = false
       d.options ?= {}
       for f in @wrappers.respond
        r = f.apply this, [response, d.status, d.data, d.options, portOptions]
        if not r
         cancel = true
         break

        continue if cancel

       data.push @_createResponse response, d.status, d.data, d.options

      return if data.length is 0

      data =
       type: 'list'
       list: data

      @_respond data, portOptions, callback

###Create Call object
This is a private function

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

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

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

###Create Response object

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

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

      return params

###Add handler

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

###Handle incoming message

     _handleMessage: (data, options, last = true) ->
      switch data.type
       when 'list'
        for d, i in data.list
         @_handleMessage d, options, (last and i + 1 is data.list.length)
       when 'response'
        try
         @_handleResponse data, options, last
        catch e
         @onerror? e
       when 'call'
        try
         @_handleCall data, options, last
        catch e
         @onerror? e
       when 'poll'
        try
         @_handlePoll data, options, last
        catch e
         @onerror? e

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

     _handleResponse: (data, options) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       #Cannot reply
       @errorCallback "Response without call: #{data.id}", data
       return
      try
       if @callsCache[data.id].handle data.data, data
        delete @callsCache[data.id]
      catch e
       @errorCallback e.message, data
       delete @callsCache[data.id]

     _handlePoll: (data, options) ->
      @onHandleError "Poll not implemented", data, options

##WorkerPort class
Used for browser and worker

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

     _send: (data) ->
      dd = data.data
      if dd? and dd._transferList?
       transferList = dd._transferList
       delete dd._transferList
      else
       transferList = []
      @worker.postMessage data, transferList

     _respond: (data, options, callback) ->
      dd = data.data
      if dd? and dd._transferList?
       transferList = dd._transferList
       delete dd._transferList
      else
       transferList = []
      @worker.postMessage data, transferList
      callback?()

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

##FramePort class
Used for browser and worker

    class FramePort extends Port
     constructor: (source, dest) ->
      super()
      @source = source
      @dest = dest
      @source.addEventListener 'message', @_onMessage.bind this

     _send: (data) ->
      @dest.postMessage data, '*'

     _respond: (data, options, callback) ->
      @dest.postMessage data, '*'
      callback?()

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

##SocketPort class

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

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

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

##ServerSocketPort class

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

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

##AJAX class

    class AjaxHttpPort extends Port
     constructor: (options) ->
      super()
      @protocol = options.protocol
      @host = options.host
      @port = options.port
      @path = options.path ? '/'
      @url = @path
      if @protocol?
       if @host? and not @port?
        @url = "#{@protocol}://#{@host}#{@path}"
       else if @host? and @port?
        @url = "#{@protocol}://#{@host}:#{@port}#{@path}"
      else
       if @host? and not @port?
        @url = "//#{@host}#{@path}"
       else if @host? and @port?
        @url = "//#{@host}:#{@port}#{@path}"

     isStreaming: false

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

       @_handleMessage jsonData, xhr: xhr
      else
       @onCallError 'Cannot connect to server'

     _respond: (data, options, callback) ->
      @errorCallback 'AJAX cannot respond', data
      callback?()

     _send: (data) ->
      data = JSON.stringify data
      xhr = new XMLHttpRequest
      xhr.open 'POST', @url
      xhr.onreadystatechange = =>
       @_onRequest xhr
      xhr.setRequestHeader 'Accept', 'application/json'
      #xhr.setRequestHeader 'Content-Type', 'application/json'
      xhr.send data

     _handleResponse: (data, options, last = true) ->
      for f in @wrappers.handleResponse
       return unless f.apply this, arguments
      if not @callsCache[data.id]?
       @errorCallback "Response without call: #{data.id}", data
       return
      call = @callsCache[data.id]
      try
       if call.handle data.data, data
        delete @callsCache[data.id]
       else if last
        params =
         type: 'poll'
         id: call.id
        params[k] = v for k, v of call.options
        @_send params
      catch e
       @errorCallback e.message, data
       delete @callsCache[data.id]

##NodeHttpPort class

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

     isStreaming: false

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

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

       @_handleMessage jsonData, response: res

     _respond: (data, options, callback) ->
      data = JSON.stringify data
      res = options.response
      res.setHeader 'content-length', Buffer.byteLength data, 'utf8'
      if callback?
       res.once 'finish', ->
        callback()
       res.once 'close', ->
        callback()

      res.write data
      res.end()

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

      req = @http.request options, @_onRequest.bind this
      delete options.headers['content-length']
      req.on 'error', (e) =>
       try
        callbacks.fail? e
       catch err
        @onerror? err

      req.write data
      req.end()

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

##NodeHttpServerPort class

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

     isStreaming: false

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

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

       @_handleMessage jsonData, response: res, request: req

     _respond: (data, options, callback) ->
      if @allowOrigin?
       options.response.setHeader 'Access-Control-Allow-Origin', @allowOrigin

      accept = options.request.headers['accept-encoding']
      accept ?= ''
      if not @zlib?
       accept = ''

      data = JSON.stringify data
      buffer = new Buffer data, 'utf8'
      if accept.match /\bgzip\b/
       options.response.setHeader 'content-encoding', 'gzip'
       @zlib.gzip buffer, (err, result) =>
        if err?
         return @errorCallback 'GZipeError', e
        @_sendBuffer result, options.response, callback
      else if accept.match /\bdeflate\b/
       options.response.setHeader 'content-encoding', 'deflate'
       @zlib.deflate buffer, (err, result) =>
        if err?
         return @errorCallback 'DeflateError', e
        @_sendBuffer result, options.response, callback
      else
       @_sendBuffer buffer, options.response, callback

     _sendBuffer: (buf, res, callback) ->
       res.setHeader 'content-length', buf.length
       if callback?
        res.once 'finish', ->
         callback()
        res.once 'close', ->
         callback()

       res.write buf
       res.end()

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

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

##NodeHttpsServerPort class

    class NodeHttpsServerPort extends NodeHttpServerPort
     constructor: (options, http) ->
      super options, http
      @_key = options.key
      @_cert = options.cert

     listen: ->
      options =
       key: @_key
       cert: @_cert

      @server = @http.createServer options, @_onRequest.bind this
      @server.listen @port

#IO Module

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

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

     Helpers:
      progress: (from, to, func) ->
       (progress, data) ->
        func? progress * (to - from) + from, data

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

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


add page.post to routes with sample input

# Application Routes

To configure routes, the only object we need access to is `app`.  This module
can be called with `require("./lib/routes")(app)` as long as `app` is an
already initialized ExpressJS application.

Also note, app.models contians:

- **app.models.db** - Mongoose MongoDB connection
- **app.models.MODELNAME** - model objects

    module.exports = (app) ->

## /

Render the main index page.

      app.get '/', (req, res) ->
        fs = require('fs')
        res.render 'index', {
          title: 'Welcome',
          extra_head_css: fs.readFileSync('views/extra/nivo.css'),
          extra_foot_js: fs.readFileSync('views/extra/nivo.js')
        }

## /page/:name

Render static pages at /view/page/name.html

      app.get '/page/:name', (req, res) ->
        require('../page').getPage(app.models.page, req.params.name, (page) ->
          if page
            res.render 'page', page
          else
            console.error 'page '+req.params.name+' not found in db, showing static'
            res.render 'page/'+req.params.name
        )

      app.post '/page/_post', (req, res) ->
        sample =
          name: 'test'
          title: 'Test Page'
          body: 'Hello world, from inside the DB'
        require('../page').setPage(app.models.page, sample, (page) ->
          if page
            res.render 'page', page
          else
            page =
              name: 'error'
              title: 'Error'
              body: 'There was an error posting your page, please try again.'
            res.render 'page', page
        )

## Copying

This software is released under the ISC License.

Copyright (c) 2013, Cameron King <cking@ecc12.com>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

<!-- vim: ts=2 sts=2 sw=2 expandtab
     CoffeeScript-friendly tabstops in vim. -->

# Application Routes

To configure routes, the only object we need access to is `app`.  This module
can be called with `require("./lib/routes")(app)` as long as `app` is an
already initialized ExpressJS application.

Also note, app.models contians:

- **app.models.db** - Mongoose MongoDB connection
- **app.models.MODELNAME** - model objects

    module.exports = (app) ->

## /

Render the main index page.

      app.get '/', (req, res) ->
        fs = require('fs')
        res.render 'index', {
          title: 'Welcome',
          extra_head_css: fs.readFileSync('views/extra/nivo.css'),
          extra_foot_js: fs.readFileSync('views/extra/nivo.js')
        }

## /page/:name

Render static pages at /view/page/name.html

      app.get '/page/:name', (req, res) ->
        require('../page').getPage(app.models.page, req.params.name, (page) ->
          if page
            res.render 'page', page
          else
            console.error 'page '+req.params.name+' not found in db, showing static'
            res.render 'page/'+req.params.name
        )

## Copying

This software is released under the ISC License.

Copyright (c) 2013, Cameron King <cking@ecc12.com>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

<!-- vim: ts=2 sts=2 sw=2 expandtab
     CoffeeScript-friendly tabstops in vim. -->


docs(document): refactor documentation of some class methods

    ObjectID  = require('mongodb').ObjectID

Import event emitter stuff.

    EventEmitter = require('events').EventEmitter
    inherits = require('util').inherits

Import data storage modules.

    mongo   = require '../db/mongo'
    redis   = require '../db/redis'

    parse   = require '../helper/parse'
    cache   = require '../helper/cache'

    collections = require('../helper/schema').types

---

## Class: Doc

### Params

* **string** `type` - document type
* **string** `id` - document id

### Return

New `Doc`.

---

    module.exports = Doc = (type, id) ->
      throw new Error('Missing Doc type param') if not type
      throw new Error('Missing or invalid ID param') if typeof id isnt 'string' and id isnt null

      EventEmitter.call @

      @db   = mongo[type]
      @id   = if typeof id is 'string' then new ObjectID(id) else null
      @type = type
      @data = {}
      @chit = false

      if @id
        redis.hgetall "#{@type}:#{@id.toString()}", (err, data) =>
          return @emit 'error', err if err

          if Object.keys(data).length
            @data = cache.arrayify @type, data
            @chit = true
            return @emit 'ready'

          @db.findOne _id: @id, cache.getFilter(@type), (err, data) =>
            return @emit 'error', err if err

            @data = data or status: 'Slettet'
            redis.hmset "#{@type}:#{@id}", @data, (err) =>
              return @emit 'ready'
      else
        process.nextTick => @emit 'ready'

      return @

    inherits Doc, EventEmitter

## Doc.exists

Check if document exists.

### Return

Returns `true` if document exists; otherwise `false`.

---

    Doc.prototype.exists = ->
      return @data.status? and @data.status isnt 'Slettet'

## Doc.getId

Get document id (ObjectID) for current document.

### Return

Returns an `ObjectID` if the document has an id; otherwise `null`.

---

    Doc.prototype.getId = ->
      @id

## Doc.wasCacheHit

Check if document request was a cache hit.

### Return

Returns `true` if document request was a cache hit; otherwise `false`.

---

    Doc.prototype.wasCacheHit = ->
      @chit

## Doc.isNotModifiedSince

Check if document has not been modified since a given timestamp.

### Params

* **string** `time` - timestamp to check against.

### Return

Returns `true` if document has not been modified since given timestamp; otherwise
`false`.

---

    Doc.prototype.isModifiedSince = (time) -> @modifiedSince time, false
    Doc.prototype.isNotModifiedSince = (time) -> @modifiedSince time, true

    Doc.prototype.modifiedSince = (time, negate) ->
      return false if not time or not @data.endret

      if not isNaN time
        # Make UNIX timestamp into milliseconds
        time = time + '000' if (time + '').length is 10
        time = parseInt time

      org = new Date(@data.endret)
      chk = new Date(time)

      # HTTP-date's don't have milliseconds
      org.setMilliseconds(0)

      return false if chk.toString() is 'Invalid Date'

      return chk >= org if negate
      return chk <  org

## doc.isMatch

Check if checksum, or Etag, matches current document checksum.

### Params

* **string** `checksum` - checksum to check against.

### Return

Returns `true` if checksum matches the current checksum; otherwise `false`.

---

    Doc.prototype.isMatch = (checksum) ->
      return false if not checksum or not @data.checksum
      return (checksum is "\"#{@data.checksum}\"" or checksum is '*')

## doc.isNoneMatch

Check if checksum, or Etag, doesn't match current document checksum.

### Params

* **string** `checksum` - checksum to check against.

### Return

Return `true` if checksum does not match or document has no checksum; otherwise
`false`.

---

    Doc.prototype.isNoneMatch = (checksum) ->
      return false if not checksum
      return true if not @data.checksum
      return checksum isnt "\"#{@data.checksum}\""

## Doc.get

Get cached data for current document.

### Params

* **string** `key` - get specific object property

### Return

Returns an `object` if `key` is `undefined`; otherwise `string`. Will return
`undefined` if the given key does not exist.

---

    Doc.prototype.get = (key) ->
      return @data if not key
      return @data[key]

## doc.getFull

### Params

* **object** `filter` - control which object properties are returned
* **function** `cb` - callback function (**Error** `err`, **object** `data`)

### Return

Returns `undefined`.

---

    Doc.prototype.getFull = (filter, cb) ->
      if not cb
        throw new Error('Document doesnt exists') if not @exists()
        return @db.find _id: @id, filter, limit: 1
      else
        return cb new Error('Document doesnt exists') if not @exists()
        @db.findOne _id: @id, filter, cb

## doc.getFuller

### Params

* **object** `filter` - control which object properties are returned
* **array** `expand` - sub-document property names to expand
* **object** `query` - query for expanded sub-documents
* **function** `cb` - callback function (**Error** `err`, **object** `data`)

### Return

Returns `undefined`.

---

    Doc.prototype.getFuller = (filter, expand, query, cb) ->
      return cb new Error('Document doesnt exists') if not @exists()

      count = 0
      mapped = {}

Since `expand` might be a user supplied input parameter we need to limit it to
properties which are expandable (`collections`). We also remove properties which
holds no value for this document.

      expand = expand
        .filter (v) => @data[v] and v in collections

Next we map the string array into an object array on the following format:
`{ type: String, ids: Array }` where `ids` are ObjectIDs to an document of the
collection `type`.

        .map (v) => type: v, ids: @data[v].map (d) -> new ObjectID d

Set up a `final` function which is called when expanded data is ready to be
mapped to the original document.

      final = () =>
        @db.findOne _id: @id, filter, (err, doc) ->
          cb err, Object.assign doc or {}, mapped

If there are no fields to expand, we just call the `final` function instantly
and no fields will get expanded.

      return final() if expand.length is 0

Set up a `next` function which is called for each expanded data returned from
the database. Expanded data is stored in the `mapped` object which is merged
with the main document in `final`. When all expanded fields are mapped `final`
is called.

      next = (type, _, docs) ->
        mapped[type] = docs or []
        return final() if Object.keys(mapped).length == expand.length

Get the expanded sub-documents from the database.

      expand.forEach (x) ->
        mongo[x.type]

We find the sub-documents using the ObjectIDs in the expanded fields, that is
the easy part. The tricky part is to prevent information leakage of private
documents. This the second part is a `query` limiting the results to public
documents or those owned by the current API user.

          .find Object.assign {_id: $in: x.ids}, query

We reuse the projection filter from the original document, but since we do not
know in advanced who owns a given sub-document we remove the `private` property
to prevent information leakage.

          .project Object.assign filter, privat: false

Bundle all the sub-documents in one array and pass it to the `next` function.

          .toArray next.bind null, x.type

## Doc.insert

Inserts a document into database.

### Params

* **object** `data` - data to insert for document.
* **function** `cb` - callback function (**Error** `err`, **Array** `warn`, **object** `data`).

### Return

Returns `undefined`.

---

    Doc.prototype.insert = (data, cb) ->
      return cb new Error('Document already exists') if @exists()
      return cb new Error('Document is deleted') if @data.status is 'Slettet'

      parse.docInsert @type, data, (err, warn, data) =>
        return cb err if err

        @db.insertOne data, w: 1, (err) =>
          return cb err if err

          @id   = new ObjectID(data._id)
          @data = cache.filterData @type, data

          redis.hmset "#{@type}:#{@id.toString()}", @data

          cb err, warn, data

## Doc.replace

Replaces all document data in database.

### Params

* **object** `data` - replacement data for document.
* **function** `cb` - callback function (**Error** `err`, **Array** `warn`, **object** `data`).

### Return

Returns `undefined`.

---

    Doc.prototype.replace = (data, cb) ->
      return cb new Error('Document doesnt exists') if not @exists()

      parse.docReplace @type, data, (err, warn, data) =>
        return cb err if err

        @db.replaceOne _id: @id, data, w: 1, (err) =>
          return cb err if err

          @data = cache.filterData @type, data

          redis.del "#{@type}:#{@id.toString()}"
          redis.hmset "#{@type}:#{@id.toString()}", @data

          data._id = @id
          cb err, warn, data

## Doc.update

Partially update the document-data in database. This is some times referred to as
PATCH in a HTTP / REST context.

### Params

* **object** `data` - replacement object for document.
* **function** `cb` - callback function (**Error** `err`, **Array** `warn`, **object** `data`).

### Return

Returns `undefined`.

---

    Doc.prototype.update = (query, cb) ->
      return cb new Error('Document doesnt exists') if not @exists()

      parse.docPatch @type, query, (err, warn, query) =>
        return cb err if err

        @db.updateOne _id: @id, query, w: 1, (err) =>
          return cb err if err

          @getFull {}, (err, doc) =>
            @data = cache.filterData @type, doc

            redis.del "#{@type}:#{@id.toString()}"
            redis.hmset "#{@type}:#{@id.toString()}", @data

            cb err, warn, doc

## Doc.delete

Delete a document from the database by removing all document properties and
setting the `status` property to `Slettet`.

### Params

* **function** `cb` - callback function (**Error** `err`).

### Return

Returns `undefined`.

---

    Doc.prototype.delete = (cb) ->
      return cb new Error('Document doesnt exists') if not @exists()

      @data = status: 'Slettet', endret: new Date()
      @db.replaceOne _id: @id, @data, w: 1, (err) =>
        return cb err if err

        redis.del "#{@type}:#{@id.toString()}"
        redis.hmset "#{@type}:#{@id.toString()}", @data

        cb null

    ObjectID  = require('mongodb').ObjectID

Import event emitter stuff.

    EventEmitter = require('events').EventEmitter
    inherits = require('util').inherits

Import data storage modules.

    mongo   = require '../db/mongo'
    redis   = require '../db/redis'

    parse   = require '../helper/parse'
    cache   = require '../helper/cache'

    collections = require('../helper/schema').types

---

## Class: Doc

### Params

* **string** `type` - document type
* **string** `id` - document id

### Return

New `Doc`.

---

    module.exports = Doc = (type, id) ->
      throw new Error('Missing Doc type param') if not type
      throw new Error('Missing or invalid ID param') if typeof id isnt 'string' and id isnt null

      EventEmitter.call @

      @db   = mongo[type]
      @id   = if typeof id is 'string' then new ObjectID(id) else null
      @type = type
      @data = {}
      @chit = false

      if @id
        redis.hgetall "#{@type}:#{@id.toString()}", (err, data) =>
          return @emit 'error', err if err

          if Object.keys(data).length
            @data = cache.arrayify @type, data
            @chit = true
            return @emit 'ready'

          @db.findOne _id: @id, cache.getFilter(@type), (err, data) =>
            return @emit 'error', err if err

            @data = data or status: 'Slettet'
            redis.hmset "#{@type}:#{@id}", @data, (err) =>
              return @emit 'ready'
      else
        process.nextTick => @emit 'ready'

      return @

    inherits Doc, EventEmitter

## Doc.exists

Check if document exists.

### Return

Returns `true` if document exists; otherwise `false`.

---

    Doc.prototype.exists = ->
      return @data.status? and @data.status isnt 'Slettet'

## Doc.getId

Get document id (ObjectID) for current document.

### Return

Returns an `ObjectID` if the document has an id; otherwise `null`.

---

    Doc.prototype.getId = ->
      @id

## Doc.wasCacheHit

Check if document request was a cache hit.

### Return

Returns `true` if document request was a cache hit; otherwise `false`.

---

    Doc.prototype.wasCacheHit = ->
      @chit

## Doc.isNotModifiedSince

Check if document has not been modified since a given timestamp.

### Params

* **string** `time` - timestamp to check against.

### Return

Returns `true` if document has not been modified since given tmestamp; otherwise
`false`.

---

    Doc.prototype.isModifiedSince = (time) -> @modifiedSince time, false
    Doc.prototype.isNotModifiedSince = (time) -> @modifiedSince time, true

    Doc.prototype.modifiedSince = (time, negate) ->
      return false if not time or not @data.endret

      if not isNaN time
        # Make unix timestamp into milliseconds
        time = time + '000' if (time + '').length is 10
        time = parseInt time

      org = new Date(@data.endret)
      chk = new Date(time)

      # HTTP-date's don't have milliseconds
      org.setMilliseconds(0)

      return false if chk.toString() is 'Invalid Date'

      return chk >= org if negate
      return chk <  org

## doc.isMatch

Check if checksum, or Etag, matche current document checksum.

### Params

* **string** `checksum` - checksum to check against.

### Return

Returns `true` if checksum matches the current checksum; otherwise `false`.

---

    Doc.prototype.isMatch = (checksum) ->
      return false if not checksum or not @data.checksum
      return (checksum is "\"#{@data.checksum}\"" or checksum is '*')

## doc.isNoneMatch

Check if checksum, or Etag, doesnt match current document checksum.

### Params

* **string** `checksum` - checksum to check against.

### Return

Return `true` if checksum does not match or document has no checksum; otherwise
`false`.

---

    Doc.prototype.isNoneMatch = (checksum) ->
      return false if not checksum
      return true if not @data.checksum
      return checksum isnt "\"#{@data.checksum}\""

## Doc.get

Get cached data for current document.

### Params

* **string** `key` - get specific object property

### Return

Returns an `object` if `key` is `undefined`; otherwise `string`. Will retur
`undefined` if the given key does not exist.

---

    Doc.prototype.get = (key) ->
      return @data if not key
      return @data[key]

## doc.getFull

### Params

* **object** `filter` - control which object properties are returned
* **function** `cb` - callback function (**Error** `err`, **object** `data`)

### Return

Returns `undefined`.

---

    Doc.prototype.getFull = (filter, cb) ->
      if not cb
        throw new Error('Document doesnt exists') if not @exists()
        return @db.find _id: @id, filter, limit: 1
      else
        return cb new Error('Document doesnt exists') if not @exists()
        @db.findOne _id: @id, filter, cb

## doc.getFuller

### Params

* **object** `filter` - control which object properties are returned
* **array** `expand` - sub-document property names to expand
* **object** `query` - query for expanded sub-documents
* **function** `cb` - callback function (**Error** `err`, **object** `data`)

### Return

Returns `undefined`.

---

    Doc.prototype.getFuller = (filter, expand, query, cb) ->
      return cb new Error('Document doesnt exists') if not @exists()

      count = 0
      mapped = {}

Since `expand` might be a user supplied input parameter we need to limit it to
properties which are expandable (eg. `collections`). We also remove properties
which holds no value for this document.

      expand = expand
        .filter (v) => @data[v] and v in collections

Next we map the string array into an object array on the following format:
`{ type: String, ids: Array }` where `ids` are ObjectIDs to an document of the
collection `type`.

        .map (v) => type: v, ids: @data[v].map (d) -> new ObjectID d

Set up a `final` function which is called when expanded data is ready to be
mapped to the original document.

      final = () =>
        @db.findOne _id: @id, filter, (err, doc) ->
          cb err, Object.assign doc or {}, mapped

If there are no fields to expand, we just call the `final` function instantly
and no fields will get expanded.

      return final() if expand.length is 0

Set up a `next` function which is called for each expanded data returned from
the database. Expanded data is stored in the `mapped` object which is merged
with the main document in `final`. When all expanded fields are mapped `final`
is called.

      next = (type, _, docs) ->
        mapped[type] = docs or []
        return final() if Object.keys(mapped).length == expand.length

Get the expanded sub-documents from the database.

      expand.forEach (x) ->
        mongo[x.type]

We find the sub-documents using the ObjectIDs in the expanded fields, that is
the easy part. The tricky part is to prevent information leackage of private
documents. This the second part is a `query` limiting the results to public
documents or those owned by the current API user.

          .find Object.assign {_id: $in: x.ids}, query

We reuse the projection filter from the original document, but since we do not
know in advanced who owns a given sub-document we remove the `private` property
to prevent information leakage.

          .project Object.assign filter, privat: false

Bundle all the sub-documents in one array and pass it to the `next` function.

          .toArray next.bind null, x.type

## Doc.insert

Inserts a document into databse.

### Params

* **object** `data` - data to insert for document.
* **function** `cb` - callback function (**Error** `err`, **Array** `warn`, **object** `data`).

### Return

Returns `undefined`.

---

    Doc.prototype.insert = (data, cb) ->
      return cb new Error('Document already exists') if @exists()
      return cb new Error('Document is deleted') if @data.status is 'Slettet'

      parse.docInsert @type, data, (err, warn, data) =>
        return cb err if err

        @db.insertOne data, w: 1, (err) =>
          return cb err if err

          @id   = new ObjectID(data._id)
          @data = cache.filterData @type, data

          redis.hmset "#{@type}:#{@id.toString()}", @data

          cb err, warn, data

## Doc.replace

Replaces all document data in database.

### Params

* **object** `data` - replacement data for document.
* **function** `cb` - callback function (**Error** `err`, **Array** `warn`, **object** `data`).

### Return

Returns `undefined`.

---

    Doc.prototype.replace = (data, cb) ->
      return cb new Error('Document doesnt exists') if not @exists()

      parse.docReplace @type, data, (err, warn, data) =>
        return cb err if err

        @db.replaceOne _id: @id, data, w: 1, (err) =>
          return cb err if err

          @data = cache.filterData @type, data

          redis.del "#{@type}:#{@id.toString()}"
          redis.hmset "#{@type}:#{@id.toString()}", @data

          data._id = @id
          cb err, warn, data

## Doc.update

Partially update the document-data in database. This is some times referred to as
PATCH in a HTTP / REST context.

### Params

* **object** `data` - replacement object for document.
* **function** `cb` - callback function (**Error** `err`, **Array** `warn`, **object** `data`).

### Return

Returns `undefined`.

---

    Doc.prototype.update = (query, cb) ->
      return cb new Error('Document doesnt exists') if not @exists()

      parse.docPatch @type, query, (err, warn, query) =>
        return cb err if err

        @db.updateOne _id: @id, query, w: 1, (err) =>
          return cb err if err

          @getFull {}, (err, doc) =>
            @data = cache.filterData @type, doc

            redis.del "#{@type}:#{@id.toString()}"
            redis.hmset "#{@type}:#{@id.toString()}", @data

            cb err, warn, doc

## Doc.delete

Delete a document from the database by removing all document properties and
setting the `status` property to `Slettet`.

### Params

* **function** `cb` - callback function (**Error** `err`).

### Return

Returns `undefined`.

---

    Doc.prototype.delete = (cb) ->
      return cb new Error('Document doesnt exists') if not @exists()

      @data = status: 'Slettet', endret: new Date()
      @db.replaceOne _id: @id, @data, w: 1, (err) =>
        return cb err if err

        redis.del "#{@type}:#{@id.toString()}"
        redis.hmset "#{@type}:#{@id.toString()}", @data

        cb null


Clean up config order of precidence

Clean up config order of precidence

The order of precidence is:

1. Command line params (these are passed via arguments.litcoffee - so they're
   present always, even when Skyll isn't called from the command line)
2. Params from config file

    Module = require './module'
    q      = require 'q'
    argv   = require './arguments'
    path   = require 'path'
    fs     = require 'fs'

    process.env.CGAPI_LOG_LEVEL = if argv?.verbosity? then argv.verbosity else 3

    class Config extends Module

      initialize: (params) =>
        deferred = q.defer()
        levels   = [ ]

        if params?.location?
          config = params.location
        else if argv.conf?
          config = argv.conf
        else
          config = path.join __dirname, '..', '.skyll.conf.js'

        if argv?._?.length > 0
          levels = levels.concat argv._
        else if argv?.l?
          for level in [0...argv.l]
            levels.push new Date(Date.now() - (((argv.l - 1) - level) * 1000)).toString()
        else if argv?.levels? == false
          levels.push new Date().toString()

        for key, val of argv
          if key.length > 2 && typeof val != 'undefined'
            if key == 'levels'
              levels = levels.concat val
            else
              @[key] = val

        fs.stat config, (err) =>
          if err?
            return deferred.reject @error 'DOES NOT EXIST', file: config
          else
            for key, val of require config
              if typeof val == 'object' && val != null && val.length? == false
                @[key] = { } unless @[key]?

                @[key][k] = v for k, v of val
              else
                unless @[key]?
                  if key == 'levels'
                    for level in [0...val]
                      levels.push new Date(Date.now() - (((val - 1) - level) * 1000)).toString()
                  else
                    @[key] = val

          levels.push new Date().toString() if levels.length == 0

          @levels = levels
          @grid   = ((false for j in [0..@columns]) for i in [0..@rows])
          @width  = @width  * @multiplier
          @height = @height * @multiplier

          super().then => deferred.resolve @

        deferred.promise

    module.exports = Config

    Module = require './module'
    q      = require 'q'
    argv   = require './arguments'
    path   = require 'path'
    fs     = require 'fs'

    process.env.CGAPI_LOG_LEVEL = if argv?.verbosity? then argv.verbosity else 3

    class Config extends Module

      pathfinder: (neighbors) ->
        o = [ ]

        for n in [1..(neighbors.length / 1)]
          c = 1 / neighbors.length

          if neighbors[n - 1] == 'RIGHT'
            o.push "#{neighbors[n - 1]}": ((c * n) * ((2 / c) * 0.5)) * c
          else
            o.push "#{neighbors[n - 1]}": ((c * n) * (0.5 / c)) * c

        chance = Math.random()

        for option in o
          neighbor = Object.keys(option)[0]

          return neighbor if option[neighbor] > chance

      initialize: (params) =>
        deferred = q.defer()
        levels   = [ ]
        base     = if params?.location? then params.location else path.join __dirname, '..'
        config   = path.join base, '.skyll.conf.js'

        fs.stat config, (err) =>
          if err? || params?.cli?
            if argv?._?.length > 0
              levels = levels.concat argv._
            else if argv?.l?
              for level in [0...argv.l]
                levels.push new Date(Date.now() - (((argv.l - 1) - level) * 1000)).toString()
            else if argv?.levels? == false
              levels.push new Date().toString()
            for key, val of argv
              if key.length > 2 && typeof val != 'undefined'
                if key == 'levels'
                  levels = levels.concat val
                else
                  @[key] = val
          else
            for key, val of require config
              unless @[key]?
                if key == 'levels'
                  for level in [0...val]
                    levels.push new Date(Date.now() - (((val - 1) - level) * 1000)).toString()
                else
                  @[key] = val

          levels.push new Date().toString() if levels.length == 0

          @levels = levels
          @grid   = ((false for j in [0..@columns]) for i in [0..@rows])
          @width  = @width  * @multiplier
          @height = @height * @multiplier

          super().then => deferred.resolve @

        deferred.promise

    module.exports = Config