Dox tweak

Dox tweak

# Documentation generation

*Generate the dynamic portions of Shrub's documentation.*

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

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

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

    class HookInvocations extends Transform

      constructor: ->
        super

        @list = []

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

        done()

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

    class HookImplementations extends Transform

      constructor: ->
        super

        @list = []

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

        done()

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

    class Todos extends Transform

      @context = 4

      constructor: ->
        super

        @lines = []
        @todos = []

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

        done()

      withContext: ->

        for todo in @todos

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

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

Gather all source files.

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

Generate an HTML ID from a hook name.

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

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

    _sourcePath = (filename) ->

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

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

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

    _collateFilesByType = (files) ->

      client = []
      server = []

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

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

      client: client, server: server

Gather statistics for all files.

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

      allFilesPromises = for type, files of _collateFilesByType allFiles

        typePromises = for file in files

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

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

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

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

            fstream.on 'error', reject

            fstream.on 'end', ->

Include all information from Transform streams in the statistics.

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

        Promise.all typePromises

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

Massage the statistics to help rendering the hooks page.

    fileStatsListPromise.then((fileStatsList) ->

      hooksIndex = {}

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

      for fileStats in fileStatsList

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

          for hook in fileStats[key]
            indexes[key][hook] ?= {}
            (indexes[key][hook][fileStats.file] ?= []).push fileStats.type

            hooksIndex[hook] = true

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

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

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

      O =
        hookFiles: hookFiles
        hooks: hooks

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

      return O

Render the hooks page.

    ).then((O) ->

      {hookFiles, hooks} = O

      keys = ['implementations', 'invocations']

      wordingFor =
        implementations: 'implements'
        invocations: 'invoke'

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

      for hook in hooks

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

        for key in keys

          if O[key][hook]?

            count = Object.keys(O[key][hook]).length
            render += "### #{count} #{key}(s)\n\n"

            for file, types of O[key][hook]

              file = _sourcePath file

              types = types.map (type) -> "[#{type}](source/#{file}##{wordingFor[key]}-hook-#{hook.toLowerCase()})"

              render += "* #{file} (#{types.join ','})\n"

            render += '\n'

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

    ).done()

Render the TODOs page.

    fileStatsListPromise.then((fileStatsList) ->

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

      for fileStats in fileStatsList

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

        idMap = {}

        for todo in fileStats.todos

          render += '\n'

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

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

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

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

          render += '\n'

          filename = _sourcePath fileStats.file

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

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

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

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

    ).done()

Add all the source files to a generated mkdocs.yml

    _allSourceFiles().then (files) ->

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

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

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

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

Add them under the 'Source code' path.

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

      fs.writeFileSync 'mkdocs.yml', yml

      return

Render the packages page.

    fileStatsListPromise.then((fileStatsList) ->

Sort by package name first.

      newList = []

      for fileStats in fileStatsList

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

        sourcePath = _sourcePath fileStats.file

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

        newList.push fileStats

      newList.sort (l, r) ->
        if l.pkg < r.pkg then -1 else if l.pkg > r.pkg then 1 else 0

    ).then((fileStatsList) ->

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

      type = null

      for fileStats in fileStatsList

        if fileStats.type isnt type
          type = fileStats.type

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

          render += '\n\n'

        isSubpackage = fileStats.pkg.split('/').length isnt 1

Link to the package.

        sourcePath = _sourcePath fileStats.file

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

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

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

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

        title = chunks[0]
        if 35 is title.charCodeAt 0
          render += " — #{title.substr 2}"

        render += '\n\n'

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

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

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

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

    )

# Documentation generation

*Generate the dynamic portions of Shrub's documentation.*

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

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

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

    class HookInvocations extends Transform

      constructor: ->
        super

        @list = []

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

        done()

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

    class HookImplementations extends Transform

      constructor: ->
        super

        @list = []

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

        done()

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

    class Todos extends Transform

      @context = 4

      constructor: ->
        super

        @lines = []
        @todos = []

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

        done()

      withContext: ->

        for todo in @todos

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

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

Gather all source files.

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

Generate an HTML ID from a hook name.

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

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

    _sourcePath = (filename) ->

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

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

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

    _collateFilesByType = (files) ->

      client = []
      server = []

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

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

      client: client, server: server

Gather statistics for all files.

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

      allFilesPromises = for type, files of _collateFilesByType allFiles

        typePromises = for file in files

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

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

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

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

            fstream.on 'error', reject

            fstream.on 'end', ->

Include all information from Transform streams in the statistics.

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

        Promise.all typePromises

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

Massage the statistics to help rendering the hooks page.

    fileStatsListPromise.then((fileStatsList) ->

      hooksIndex = {}

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

      for fileStats in fileStatsList

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

          for hook in fileStats[key]
            indexes[key][hook] ?= {}
            (indexes[key][hook][fileStats.file] ?= []).push fileStats.type

            hooksIndex[hook] = true

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

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

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

      O =
        hookFiles: hookFiles
        hooks: hooks

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

      return O

Render the hooks page.

    ).then((O) ->

      {hookFiles, hooks} = O

      keys = ['implementations', 'invocations']

      wordingFor =
        implementations: 'implements'
        invocations: 'invoke'

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

      for hook in hooks

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

        for key in keys

          if O[key][hook]?

            count = Object.keys(O[key][hook]).length
            render += "### #{count} #{key}(s)\n\n"

            for file, types of O[key][hook]

              file = _sourcePath file

              types = types.map (type) -> "[#{type}](source/#{file}##{wordingFor[key]}-hook-#{hook.toLowerCase()})"

              render += "* #{file} (#{types.join ','})\n"

            render += '\n'

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

    ).done()

Render the TODOs page.

    fileStatsListPromise.then((fileStatsList) ->

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

      for fileStats in fileStatsList

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

        idMap = {}

        for todo in fileStats.todos

          render += '\n'

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

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

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

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

          render += '\n'

          filename = _sourcePath fileStats.file

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

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

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

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

    ).done()

Add all the source files to a generated mkdocs.yml

    _allSourceFiles().then (files) ->

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

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

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

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

Add them under the 'Source code' path.

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

      fs.writeFileSync 'mkdocs.yml', yml

      return

Render the packages page.

    fileStatsListPromise.then((fileStatsList) ->

Sort by package name first.

      newList = []

      for fileStats in fileStatsList

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

        sourcePath = _sourcePath fileStats.file

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

        newList.push fileStats

      newList.sort (l, r) ->
        if l.pkg < r.pkg then -1 else if l.pkg > r.pkg then 1 else 0

    ).then((fileStatsList) ->

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

      type = null

      for fileStats in fileStatsList

        if fileStats.type isnt type
          type = fileStats.type

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

          render += '\n\n'

        isSubpackage = fileStats.pkg.split('/').length isnt 1

Link to the package.

        sourcePath = _sourcePath fileStats.file

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

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

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

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

        title = chunks[0]
        if 35 is title.charCodeAt 0
          render += " — #{title.substr 2}"

        render += '\n\n'

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

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

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

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

    )


Send file upload count to Librato

Send file upload count to Librato

    url          = require 'url'

    compression  = require 'compression'
    express      = require 'express'
    morgan       = require 'morgan'
    responseTime = require 'response-time'

    multer       = require 'multer'
    async        = require 'async'
    dms2dec      = require 'dms2dec'

    raven        = require 'raven'
    sentry       = require './sentry'
    librato      = require './librato'

    app = express()
    app.use morgan 'combined'
    app.use compression()
    app.use responseTime()

    Upload = require 's3-uploader'
    s3 = new Upload process.env.AWS_BUCKET_NAME,
      awsBucketUrl: "https://s3-eu-west-1.amazonaws.com/#{process.env.AWS_BUCKET_NAME}/"
      awsBucketPath: process.env.AWS_BUCKET_PATH
      awsBucketAcl: 'public-read'
      awsHttpTimeout: 60000
      returnExif: true

      versions: [{
        original: true
        awsImageAcl: 'private'
      },{
        suffix: '-large'
        quality: 80
        maxHeight: 1040
        maxWidth: 1040
      },{
        suffix: '-medium'
        maxHeight: 780
        maxWidth: 780
      },{
        suffix: '-small'
        maxHeight: 320
        maxWidth: 320
      }]

    origins = process.env.ALLOW_ORIGINS?.split(',') or []

    app.all '/upload', (req, res, next) ->
      res.once 'close', -> sentry.captureResClose res

      origin = url.parse req.get('Origin') or ''

      if origin.hostname not in origins
        error = new Error "Bad Origin Header #{req.get('Origin')}"
        error.status = 403
        return next error

      res.set 'Access-Control-Allow-Origin', req.get('Origin')
      res.set 'Access-Control-Allow-Methods', 'POST'
      res.set 'Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'
      res.set 'Access-Control-Expose-Headers', 'X-Response-Time'
      res.set 'Access-Control-Allow-Max-Age', 0

      return res.send 200 if req.method is 'OPTIONS'
      return next()

    app.post '/upload', multer(dest: require('os').tmpdir()), (req, res, next) ->
      librato.measure 'image.upload', Object.keys(req.files).length, {}

      async.mapSeries Object.keys(req.files), (key, cb) ->
        if req.files[key].extension.toLowerCase() not in ['jpg', 'jpeg', 'png', 'gif', 'tiff']
          error = new Error "Invalid Image #{req.files[key].extension}"
          error.status = 422
          return cb error

        s3.upload req.files[key].path, {}, (err, images, meta) ->
          return cb err if err

          if meta.exif['exif:GPSLatitude'] and meta.exif['exif:GPSLongitude']
            meta.geojson =
              type: 'Point'
              coordinates: dms2dec \
                meta.exif['exif:GPSLatitude'], \
                meta.exif['exif:GPSLatitudeRef'], \
                meta.exif['exif:GPSLongitude'], \
                meta.exif['exif:GPSLongitudeRef']
              .reverse()

          return cb null, versions: images.splice(1), meta: meta
      , (err, files) ->
        return next err if err
        sentry.captureHeaderSent req, files if res._headerSent
        return res.status(201).json files

    app.use (req, res, next) ->
      res.status(404).json message: 'Not Found'

    app.use raven.middleware.express sentry
    app.use (err, req, res, next) ->
      if not err.status or err.status >= 500
        sentry.captureError err

        console.error err.message
        console.error err.stack

        res.status(err.status or 500).json message: 'Internal Server Error'
      else
        sentry.captureMessage err.message, level: 'warning', req: req
        res.status(err.status).json message: err.message

    if not module.parent
      app.listen process.env.PORT_WWW
      console.log "Server listening on port #{process.env.PORT_WWW}"
    else
      module.exports = app

    url          = require 'url'

    compression  = require 'compression'
    express      = require 'express'
    morgan       = require 'morgan'
    responseTime = require 'response-time'

    multer       = require 'multer'
    async        = require 'async'
    dms2dec      = require 'dms2dec'

    raven        = require 'raven'
    sentry       = require './sentry'

    app = express()
    app.use morgan 'combined'
    app.use compression()
    app.use responseTime()

    Upload = require 's3-uploader'
    s3 = new Upload process.env.AWS_BUCKET_NAME,
      awsBucketUrl: "https://s3-eu-west-1.amazonaws.com/#{process.env.AWS_BUCKET_NAME}/"
      awsBucketPath: process.env.AWS_BUCKET_PATH
      awsBucketAcl: 'public-read'
      awsHttpTimeout: 60000
      returnExif: true

      versions: [{
        original: true
        awsImageAcl: 'private'
      },{
        suffix: '-large'
        quality: 80
        maxHeight: 1040
        maxWidth: 1040
      },{
        suffix: '-medium'
        maxHeight: 780
        maxWidth: 780
      },{
        suffix: '-small'
        maxHeight: 320
        maxWidth: 320
      }]

    origins = process.env.ALLOW_ORIGINS?.split(',') or []

    app.all '/upload', (req, res, next) ->
      res.once 'close', -> sentry.captureResClose res

      origin = url.parse req.get('Origin') or ''

      if origin.hostname not in origins
        error = new Error "Bad Origin Header #{req.get('Origin')}"
        error.status = 403
        return next error

      res.set 'Access-Control-Allow-Origin', req.get('Origin')
      res.set 'Access-Control-Allow-Methods', 'POST'
      res.set 'Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'
      res.set 'Access-Control-Expose-Headers', 'X-Response-Time'
      res.set 'Access-Control-Allow-Max-Age', 0

      return res.send 200 if req.method is 'OPTIONS'
      return next()

    app.post '/upload', multer(dest: require('os').tmpdir()), (req, res, next) ->
      console.log "Recieved #{Object.keys(req.files).length} files"

      async.mapSeries Object.keys(req.files), (key, cb) ->
        if req.files[key].extension.toLowerCase() not in ['jpg', 'jpeg', 'png', 'gif', 'tiff']
          error = new Error "Invalid Image #{req.files[key].extension}"
          error.status = 422
          return cb error

        s3.upload req.files[key].path, {}, (err, images, meta) ->
          return cb err if err

          if meta.exif['exif:GPSLatitude'] and meta.exif['exif:GPSLongitude']
            meta.geojson =
              type: 'Point'
              coordinates: dms2dec \
                meta.exif['exif:GPSLatitude'], \
                meta.exif['exif:GPSLatitudeRef'], \
                meta.exif['exif:GPSLongitude'], \
                meta.exif['exif:GPSLongitudeRef']
              .reverse()

          return cb null, versions: images.splice(1), meta: meta
      , (err, files) ->
        return next err if err
        sentry.captureHeaderSent req, files if res._headerSent
        return res.status(201).json files

    app.use (req, res, next) ->
      res.status(404).json message: 'Not Found'

    app.use raven.middleware.express sentry
    app.use (err, req, res, next) ->
      if not err.status or err.status >= 500
        sentry.captureError err

        console.error err.message
        console.error err.stack

        res.status(err.status or 500).json message: 'Internal Server Error'
      else
        sentry.captureMessage err.message, level: 'warning', req: req
        res.status(err.status).json message: err.message

    if not module.parent
      app.listen process.env.PORT_WWW
      console.log "Server listening on port #{process.env.PORT_WWW}"
    else
      module.exports = app


Fix a socket bug with Firefox

Fix a socket bug with Firefox

WebSockets API
==============

This API acts as the bridge between the Bacon.js-filled world of
the screens and the raw WebSockets API with which we communicate
with the server.

It is publicised through the *WS* public API.

First, we create the actual WebSocket.

    socket = new WebSocket 'ws://localhost:8080/websocket.rpy'

Transmission
------------

Transmission of data is significantly frustrated by an unfortunate
side-effect of the asynchronous nature of programming on the web -
that is, we cannot guarantee that the socket is actually open yet
when a call is made. Instead, we use a bus to push messages, with
a custom buffering function which will hold all messages until the
socket is ready.

We first create the actual bus. This contains already-encoded data,
which merely needs to be passed to the native WebSockets *send*
API.

    transmitBus = new Bacon.Bus

We also define two auxiliary variables necessary for this to work:
a flag indicating whether the connection has yet been opened, and
a list of callbacks to invoke once it has been opened.

    connected = false
    onConnectCallbacks = []

The buffering function, *bufferTXBus*, implements the protocol
required for *bufferWithTime*. Given its argument, *f*, which decides
when to dispatch the actual message, it:

* Schedules a call at the end of the current iteration of the JavaScript run
  loop if the socket is already connected, and
* Adds it to the list of callbacks to invoked once the socket is open.

    bufferTXBus = (f) ->
        _.defer f if connected
        onConnectCallbacks.push f if not connected

Once the socket is opened, indicated by the *onopen* function in
the WebSocket, we flush the *onConnectCallbacks* queue and set the
*connected* flag, preventing further calls being added to the queue.

    socket.onopen = ->
        connected = true
        do f for f in onConnectCallbacks
        onConnectCallbacks = []

We bind the *transmitBus*, with buffering, to transmit messages
through the native WebSockets API.

    transmitBus.bufferWithTime(bufferTXBus).onValue (x) ->
        socket.send msg for msg in x

Finally, we define the public API function through which clients
can send messages. This function constructs a native JavaScript
object representing the message (without modifying the argument),
then pushes it to the *transmitBus*.

    send = (message, data = {}) ->
        target = _.clone(data)
        target.type = message
        transmitBus.push JSON.stringify(target)

Receipt
-------

Receiving messages through the WebSockets API is an altogether
simpler affair.  We simply define a bus on which messages are
received, and interested clients subscribe to it.

The bus is deliberately publicly exposed as a bus rather than an
event source, so that (for testing and debugging purposes or for
disgusting hacks) clients can *inject* messages to be spread through
the system as if they were received from the server.

    messageBus = new Bacon.Bus
    socket.onmessage = (msg) ->
        messageBus.push JSON.parse(msg.data)

Public API
----------

Finally, the public interface to the WebSockets API is presented
through the *WS* global.

    window.WS =
        message: messageBus
        send: send

WebSockets API
==============

This API acts as the bridge between the Bacon.js-filled world of
the screens and the raw WebSockets API with which we communicate
with the server.

It is publicised through the *WS* public API.

First, we create the actual WebSocket.

    socket = new WebSocket 'ws://localhost:8080/websocket.rpy'

Transmission
------------

Transmission of data is significantly frustrated by an unfortunate
side-effect of the asynchronous nature of programming on the web -
that is, we cannot guarantee that the socket is actually open yet
when a call is made. Instead, we use a bus to push messages, with
a custom buffering function which will hold all messages until the
socket is ready.

We first create the actual bus. This contains already-encoded data,
which merely needs to be passed to the native WebSockets *send*
API.

    transmitBus = new Bacon.Bus

We also define two auxiliary variables necessary for this to work:
a flag indicating whether the connection has yet been opened, and
a list of callbacks to invoke once it has been opened.

    connected = false
    onConnectCallbacks = []

The buffering function, *bufferTXBus*, implements the protocol
required for *bufferWithTime*. Given its argument, *f*, which decides
when to dispatch the actual message, it:

* Schedules a call at the end of the current iteration of the JavaScript run
  loop if the socket is already connected, and
* Adds it to the list of callbacks to invoked once the socket is open.

    bufferTXBus = (f) ->
        _.defer f if connected
        onConnectCallbacks.push f if not connected

Once the socket is opened, indicated by the *onopen* function in
the WebSocket, we flush the *onConnectCallbacks* queue and set the
*connected* flag, preventing further calls being added to the queue.

    socket.onopen = ->
        connected = true
        do f for f in onConnectCallbacks
        onConnectCallbacks = []

We bind the *transmitBus*, with buffering, to transmit messages
through the native WebSockets API.

    transmitBus.bufferWithTime(bufferTXBus).onValue (x) ->
        socket.send x

Finally, we define the public API function through which clients
can send messages. This function constructs a native JavaScript
object representing the message (without modifying the argument),
then pushes it to the *transmitBus*.

    send = (message, data = {}) ->
        target = _.clone(data)
        target.type = message
        transmitBus.push JSON.stringify(target)

Receipt
-------

Receiving messages through the WebSockets API is an altogether
simpler affair.  We simply define a bus on which messages are
received, and interested clients subscribe to it.

The bus is deliberately publicly exposed as a bus rather than an
event source, so that (for testing and debugging purposes or for
disgusting hacks) clients can *inject* messages to be spread through
the system as if they were received from the server.

    messageBus = new Bacon.Bus
    socket.onmessage = (msg) ->
        messageBus.push JSON.parse(msg.data)

Public API
----------

Finally, the public interface to the WebSockets API is presented
through the *WS* global.

    window.WS =
        message: messageBus
        send: send


Return JSON with correct spacing

Return JSON with correct spacing

    raven       = require 'raven'
    sentry      = require './db/sentry'
    express     = require 'express'

    auth        = require './helper/auth'

    system      = require './system'
    collection  = require './collection'
    document    = require './document'

## Init

    app = express()

### Authentication

This routine is called for all request to the API. It is reponsible for
authenticating the user by validating the `api\_key`. It then sets some request
session variables so they become accessible throughout the entire system during
the rquest.

    app.use '/', (req, res, next) ->
      return next() if req.originalUrl.substr(0, 17) is '/CloudHealthCheck'

      auth.check req.query.api_key, (err, user) ->
        if user
          res.set 'X-RateLimit-Limit', user.limit
          res.set 'X-RateLimit-Remaining', user.remaining
          res.set 'X-RateLimit-Reset', user.reset

        req.user = user

        return next err if err

        next()

### Configuration

    app.use(express.favicon())
    app.use(express.logger(':date :remote-addr - :method :url :status :res[content-length] - :response-time ms')) if not process.env.SILENT
    app.use(express.compress())
    app.use(express.json())
    app.disable('x-powered-by')
    app.set 'port', process.env.PORT_WWW or 8080
    app.use(app.router)

### Error handling

Before handling the error ours self make sure that it is propperly logged in
Sentry by using the express/connect middleware.

    app.use raven.middleware.express sentry

All errors passed to `next` or exceptions ends up here. We set the status code
to `500` if it is not already defined in the `Error` object. We then print the
error mesage and stack trace to the console for debug purposes.

Log 401 Unauthorized (warning) and 403 Forbidden (notice) errors to Sentry to
enhance debugging and oversight as Nasjonal Turbase gets more used.

Before returning a response to the user the request method is check. HEAD
requests shall not contain any body – this applies for errors as well.

    app.use (err, req, res, next) ->
      res.status err.status or 500

      if res.statusCode is 401
        sentry.captureMessage "Invalid API-key #{req.query.api_key}",
          level: 'warning'
          extra: sentry.parseRequest req, user: req.user

      if res.statusCode is 403
        sentry.captureMessage "Rate limit exceeded for #{req.user.tilbyder}",
          level: 'notice'
          extra: sentry.parseRequest req, user: req.user

      if res.statusCode >= 500
        console.error err.message
        console.error err.stack

      return res.end() if req.method is 'HEAD'
      return res.json message: err.message or 'Ukjent feil'

This the fmous 404 Not Found handler. If no route configuration for the request
is found, it ends up here. We don't do much fancy about it – just a standard
error message and HTTP status code.

    app.use (req, res) -> res.json 404, message: "Resurs ikke funnet"

## GET /

    app.get '/', (req, res) ->
      res.json message: 'Here be dragons'

## GET /CloudHealthCheck

> So...You’re seeing the dotCloud active health check looking to make sure that
> your service is up. There is no way for you to disable it, but you can prevent
> it and you can handle it [1]!

[1] [dotCloud](http://docs.dotcloud.com/tutorials/more/cloud-health-check/)

    app.all '/CloudHealthCheck', system.check

## GET /objekttyper

    app.get '/objekttyper', (req, res, next) ->
      res.json 200, ['turer', 'steder', 'områder', 'grupper', 'arrangementer', 'bilder']

## ALL /{collection}

    app.param 'collection', collection.param
    app.all '/:collection', (req, res, next) ->
      switch req.method
        when 'OPTIONS' then collection.options req, res, next
        when 'HEAD', 'GET' then collection.get req, res, next
        when 'POST' then collection.post req, res, next
        else res.json 405, message: "HTTP Method #{req.method.toUpperCase()} Not Allowed"

## ALL /{collection}/{objectid}

    app.param 'objectid', document.param
    app.options '/:collection/:ojectid', document.options
    app.all '/:collection/:objectid', document.all, (req, res, next) ->
      switch req.method
        when 'HEAD', 'GET' then document.get req, res, next
        when 'PUT' then document.put req, res, next
        when 'PATCH' then document.patch req, res, next
        when 'DELETE' then document.delete req, res, next
        else res.json 405, message: "HTTP Method #{req.method.toUpperCase()} Not Allowed"

## Start

Ok, so if the server is running in stand-alone mode i.e. there is not
`module.parent` then continue with starting the databse and listening to a port.

    if not module.parent
      require('./db/redis')
      require('./db/mongo').once 'ready', ->
        console.log 'Database is open...'

        app.listen app.get('port'), ->
          console.log "Server is listening on port #{app.get('port')}"

However, if there is a `module.parent` don't do all the stuff above. This means
that there is some program that is including the server from it, e.g a test, and
hence it should not listen to a port for incomming connections and rather just
return the application instance so that we can do some testing on it.

    else
      module.exports = app

    raven       = require 'raven'
    sentry      = require './db/sentry'
    express     = require 'express'

    auth        = require './helper/auth'

    system      = require './system'
    collection  = require './collection'
    document    = require './document'

## Init

    app = express()

### Authentication

This routine is called for all request to the API. It is reponsible for
authenticating the user by validating the `api\_key`. It then sets some request
session variables so they become accessible throughout the entire system during
the rquest.

    app.use '/', (req, res, next) ->
      return next() if req.originalUrl.substr(0, 17) is '/CloudHealthCheck'

      auth.check req.query.api_key, (err, user) ->
        if user
          res.set 'X-RateLimit-Limit', user.limit
          res.set 'X-RateLimit-Remaining', user.remaining
          res.set 'X-RateLimit-Reset', user.reset

        req.user = user

        return next err if err

        next()

### Configuration

    app.use(express.favicon())
    app.use(express.logger(':date :remote-addr - :method :url :status :res[content-length] - :response-time ms')) if not process.env.SILENT
    app.set('json spaces', 0) if app.get('env') isnt 'testing'
    app.use(express.compress())
    app.use(express.json())
    app.disable('x-powered-by')
    app.set 'port', process.env.PORT_WWW or 8080
    app.use(app.router)

### Error handling

Before handling the error ours self make sure that it is propperly logged in
Sentry by using the express/connect middleware.

    app.use raven.middleware.express sentry

All errors passed to `next` or exceptions ends up here. We set the status code
to `500` if it is not already defined in the `Error` object. We then print the
error mesage and stack trace to the console for debug purposes.

Log 401 Unauthorized (warning) and 403 Forbidden (notice) errors to Sentry to
enhance debugging and oversight as Nasjonal Turbase gets more used.

Before returning a response to the user the request method is check. HEAD
requests shall not contain any body – this applies for errors as well.

    app.use (err, req, res, next) ->
      res.status err.status or 500

      if res.statusCode is 401
        sentry.captureMessage "Invalid API-key #{req.query.api_key}",
          level: 'warning'
          extra: sentry.parseRequest req, user: req.user

      if res.statusCode is 403
        sentry.captureMessage "Rate limit exceeded for #{req.user.tilbyder}",
          level: 'notice'
          extra: sentry.parseRequest req, user: req.user

      if res.statusCode >= 500
        console.error err.message
        console.error err.stack

      return res.end() if req.method is 'HEAD'
      return res.json message: err.message or 'Ukjent feil'

This the fmous 404 Not Found handler. If no route configuration for the request
is found, it ends up here. We don't do much fancy about it – just a standard
error message and HTTP status code.

    app.use (req, res) -> res.json 404, message: "Resurs ikke funnet"

## GET /

    app.get '/', (req, res) ->
      res.json message: 'Here be dragons'

## GET /CloudHealthCheck

> So...You’re seeing the dotCloud active health check looking to make sure that
> your service is up. There is no way for you to disable it, but you can prevent
> it and you can handle it [1]!

[1] [dotCloud](http://docs.dotcloud.com/tutorials/more/cloud-health-check/)

    app.all '/CloudHealthCheck', system.check

## GET /objekttyper

    app.get '/objekttyper', (req, res, next) ->
      res.json 200, ['turer', 'steder', 'områder', 'grupper', 'arrangementer', 'bilder']

## ALL /{collection}

    app.param 'collection', collection.param
    app.all '/:collection', (req, res, next) ->
      switch req.method
        when 'OPTIONS' then collection.options req, res, next
        when 'HEAD', 'GET' then collection.get req, res, next
        when 'POST' then collection.post req, res, next
        else res.json 405, message: "HTTP Method #{req.method.toUpperCase()} Not Allowed"

## ALL /{collection}/{objectid}

    app.param 'objectid', document.param
    app.options '/:collection/:ojectid', document.options
    app.all '/:collection/:objectid', document.all, (req, res, next) ->
      switch req.method
        when 'HEAD', 'GET' then document.get req, res, next
        when 'PUT' then document.put req, res, next
        when 'PATCH' then document.patch req, res, next
        when 'DELETE' then document.delete req, res, next
        else res.json 405, message: "HTTP Method #{req.method.toUpperCase()} Not Allowed"

## Start

Ok, so if the server is running in stand-alone mode i.e. there is not
`module.parent` then continue with starting the databse and listening to a port.

    if not module.parent
      require('./db/redis')
      require('./db/mongo').once 'ready', ->
        console.log 'Database is open...'

        app.listen app.get('port'), ->
          console.log "Server is listening on port #{app.get('port')}"

However, if there is a `module.parent` don't do all the stuff above. This means
that there is some program that is including the server from it, e.g a test, and
hence it should not listen to a port for incomming connections and rather just
return the application instance so that we can do some testing on it.

    else
      module.exports = app


- use WrappedModule

- use WrappedModule

NodeSentry
==========

The key idea of our proposal, called \ns, is to use a variant of an inline
reference monitor~\cite{schneider2000,erlingsson2003} as modified for the
Security-by-Contract approach for Java and {.NET}~\cite{desmet2008} in order to
make it more flexible. Specifically, we do not embed the monitor into the code as
suggested by most approaches for inline reference monitors but inline only
hooks in a few key places, while the monitor itself is an external component. In our
case this has the added advantage of potentially improving performance (a key
requirement for server-side code) as the monitor can now run in a separate
thread and threads that do not call security relevant actions are unaffected.
Further, and maybe most important, we do not limit ourselves to purely
raising security exceptions and stopping the execution but support policies
that specify how to ``fix'' the execution; the ability to continue executing after
security errors is an essential requirement
for server-side applications.

In order to maintain control over all references acquired by the library, e.g.,
via a call to \jslib{require}, \ns applies the \emph{membrane} pattern,
originally proposed by Miller~\cite[\S 9]{miller2006} and further refined
by Van Cutsem and Miller~\cite{vancutsem2013}. The goal of a membrane is to fully isolate two object
graphs~\cite{miller2006,vancutsem2013}.  Intuitively, a membrane creates a
shadow object that is a ``clone'' of the target object that it wishes to
protect. Only the references to the shadow
object are passed further to callers. Any access to the shadowed object is then
intercepted and either served directly or eventually reflected on the target
object through handlers. In this way, when a membrane revokes a reference,
essentially by destroying the shadow object~\cite{vancutsem2013}, it instantly
achieves the goal of transitively revoking all references as advocated by
Miller~\cite{miller2006}.

    harmonyFlagsEnabled = -> "function" == typeof Map
    throw new Error "NodeSentry requires the harmony flags. (`node --harmony`)" if not harmonyFlagsEnabled()

    harmony = require "harmony-reflect"
    log = require "./logger"
    WrappedModule = require "./module"
    path = require "path"

    absolutePath = (name) -> name[0] == "/"
    installedModule = (name) -> not (name.indexOf(".js") > -1) and not (name.indexOf(".") > -1)
    updatePath = (name) -> path.join(path.dirname(module.parent.filename), name)

    global.safe_require = (libName, policyObj) ->
        libName = updatePath(libName) unless absolutePath(libName) or installedModule(libName)
        modPaths = module.parent.paths

Load the module in a specific `ModuleLoader` that makes it possible to intercept all subsequent calls to `require`.

        wrappedModule = new WrappedModule(libName, modPaths, policyObj)
        do wrappedModule.load

    module.exports.Policy = require "./policy"

Copyright 2014 -- iMinds-DistriNet, KU Leuven

NodeSentry
==========

The key idea of our proposal, called \ns, is to use a variant of an inline
reference monitor~\cite{schneider2000,erlingsson2003} as modified for the
Security-by-Contract approach for Java and {.NET}~\cite{desmet2008} in order to
make it more flexible. Specifically, we do not embed the monitor into the code as
suggested by most approaches for inline reference monitors but inline only
hooks in a few key places, while the monitor itself is an external component. In our
case this has the added advantage of potentially improving performance (a key
requirement for server-side code) as the monitor can now run in a separate
thread and threads that do not call security relevant actions are unaffected.
Further, and maybe most important, we do not limit ourselves to purely
raising security exceptions and stopping the execution but support policies
that specify how to ``fix'' the execution; the ability to continue executing after
security errors is an essential requirement
for server-side applications.

In order to maintain control over all references acquired by the library, e.g.,
via a call to \jslib{require}, \ns applies the \emph{membrane} pattern,
originally proposed by Miller~\cite[\S 9]{miller2006} and further refined
by Van Cutsem and Miller~\cite{vancutsem2013}. The goal of a membrane is to fully isolate two object
graphs~\cite{miller2006,vancutsem2013}.  Intuitively, a membrane creates a
shadow object that is a ``clone'' of the target object that it wishes to
protect. Only the references to the shadow
object are passed further to callers. Any access to the shadowed object is then
intercepted and either served directly or eventually reflected on the target
object through handlers. In this way, when a membrane revokes a reference,
essentially by destroying the shadow object~\cite{vancutsem2013}, it instantly
achieves the goal of transitively revoking all references as advocated by
Miller~\cite{miller2006}.

    harmonyFlagsEnabled = -> "function" == typeof Map
    throw new Error "NodeSentry requires the harmony flags. (`node --harmony`)" if not harmonyFlagsEnabled()

    harmony = require "harmony-reflect"
    log = require "./logger"
    Module = require "./module"
    path = require "path"

    absolutePath = (name) -> name[0] == "/"
    installedModule = (name) -> not (name.indexOf(".js") > -1)
    updatePath = (name) -> path.join(path.dirname(module.parent.filename), name)

    global.safe_require = (libName, policyObj) ->
        # update relative paths
        libName = updatePath(libName) unless absolutePath(libName) or installedModule(libName)
        # update search paths for local/global modules
        module.paths = module.parent.paths
        if policyObj?
            m = new Module(libName)
            m.setPolicy policyObj
            m.loadLibrary()
        else
            require libName

    module.exports.Policy = require "./policy"

Copyright 2014 -- iMinds-DistriNet, KU Leuven


http socket

http socket

    _self = this

##Response class

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

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

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

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

##Call class

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

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

##Port base class

    class Port
     constructor: (options = {}) ->
      @handlers = options.handlers ? {}
      @callsCache = {}
      @callsCounter = 0

###Send RPC call

     send: (method, data, callbacks, options = {}) ->
      @_send @_createCall method, data, callbacks, options

###Respond to a RPC call

     respond: (response, status, data, options = {}) ->
      @_respond @_createRespose response, status, data, options

###Create Call object
This is a private function

     _createCall: (method, data, callbacks, options) ->
      #TODO other params via options
      if (typeof callbacks) is 'function'
       callbacks =
        success: callbacks

      call = new Call @callsCounter, method, data, callbacks
      @callsCounter++
      @callsCache[call.id] = call

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

      return params

###Create Response object

     _createRespose: (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

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

###Handle incoming message

     handleMessage: (data) ->
      if data.type is 'response'
       return unless @callsCache[data.id]?
       @callsCache[data.id].handle data
      else if data.type is 'call'
       return unless @handlers[data.method]?
       @handlers[data.method] data.data, new Response data, this

##WorkerPort class
Used for browser and worker

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

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

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

     _onError: (e) -> console.log e

##SocketPort class

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

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

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

##ServerSocketPort class

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

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

##NodeHttpPort class

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

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

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

      req = @http.request options, (res) =>
       console.log 'STATUS: ' + res.statusCode
       console.log 'HEADERS: ' + JSON.stringify res.headers
       res.setEncoding 'utf8'
       res.on 'data', (chunk) ->
        console.log 'BODY: ' + chunk
       #console.log 'result', res

      delete options.headers['content-length']
      req.on 'error', (e) ->
       console.log 'error', e

      req.write data
      req.end()

#IO Module

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

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

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

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

    _self = this

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

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

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

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

    class Port
     constructor: (options = {}) ->
      @handlers = options.handlers ? {}
      @callsCache = {}
      @callsCounter = 0

     send: (method, data, callbacks, options = {}) ->
      @_send @_createCall method, data, callbacks, options

     respond: (response, status, data, options = {}) ->
      @_respond @_createRespose response, status, data, options

     _createCall: (method, data, callbacks, options) ->
      #TODO other params via options
      if (typeof callbacks) is 'function'
       callbacks =
        success: callbacks

      call = new Call @callsCounter, method, data, callbacks
      @callsCounter++
      @callsCache[call.id] = call

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

      return params

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

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

      return params

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

     handleMessage: (data) ->
      if data.type is 'response'
       return unless @callsCache[data.id]?
       @callsCache[data.id].handle data
      else if data.type is 'call'
       return unless @handlers[data.method]?
       @handlers[data.method] data.data, new Response data, this

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

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

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

     _onError: (e) -> console.log e

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

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

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

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

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

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

     ports:
      WorkerPort: WorkerPort
      SocketPort: SocketPort
      ServerSocketPort: ServerSocketPort

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

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


Update floor thickness rendering to make more sense visually

    PngRenderer = require './png_renderer'

    class PngGround extends PngRenderer

      do_render: (ctx, path) =>
        cell_width  = @config.width
        cell_height = @config.height

        ground_segments = @config.ground.segments
        segment_width   = 1 / ground_segments
        ground_width    = cell_width * segment_width

        fill_segment = (block, segment, height, color) =>
          right  = segment * (cell_width * segment_width)
          left   = (cell_width * block.x) + right
          top    = (cell_height * (block.y + 1)) - height

          ctx.fillStyle = color

          ctx.beginPath()
          ctx.rect left, top, ground_width, height
          ctx.fill()

        for step in path
          for g, i in step.ground
            fill_segment step, i, (g.thickness * @config.multiplier) * 0.5, @config.ground.fill_color

    module.exports = PngGround

    PngRenderer = require './png_renderer'

    class PngGround extends PngRenderer

      do_render: (ctx, path) =>
        cell_width  = @config.width
        cell_height = @config.height

        ground_segments = @config.ground.segments
        segment_width   = 1 / ground_segments
        ground_width    = cell_width * segment_width

        fill_segment = (block, segment, height, color) =>
          right  = segment * (cell_width * segment_width)
          left   = (cell_width * block.x) + right
          top    = (cell_height * (block.y + 1)) - height

          ctx.fillStyle = color

          ctx.beginPath()
          ctx.rect left, top, ground_width, height
          ctx.fill()

        for step in path
          for g, i in step.ground
            fill_segment step, i, g.thickness * @config.multiplier, @config.ground.fill_color

    module.exports = PngGround


fix proper return from create call, see #40

# Property - controller of Object properties

The `Property` class is the *secretive shadowy* element that governs
`Object` behavior and are bound to the `Object` via
`Object.defineProperty`. It acts like a shadow `Proxy/Reflector` to
the `Object` instance and provides tight control via the
`Getter/Setter` interfaces.

The `Property` instances attach themselves to the `Object.__` property
and are rarely accessed directly - but they are the **fundamental**
actors that *actualize* YANG schema compliance into ordinary JS
objects.

Below are list of properties available to every instance of `Property`:

property | type | mapping | description
--- | --- | --- | ---
name   | string | direct | name of the property
schema | object | direct | a schema instance (usually [Yang](./src/yang.listcoffee))
state  | object | direct | *private* object holding internal state
container | object | access(state) | reference to object containing this property
configurable | boolean | getter(state) | defines whether this property can be redefined
enumerable   | boolean | getter(state) | defines whether this property is enumerable
content | any | computed | getter/setter for `state.value`
context | object | computed | dynamically generated using [context](./src/context.coffee)
root  | [Property](./src/property.litcoffee) | computed | dynamically returns the root Property instance
props | array(Property) | computed | returns children Property instances
key   | string/number | computed | conditionally returns unique key for Property if a list item
path  | [XPath](./src/xpath.coffee) | computed | dynamically generate XPath for this Property from root

## Class Property

    debug    = require('debug')('yang:property') if process.env.DEBUG?
    co       = require 'co'
    delegate = require 'delegates'
    context  = require './context'
    XPath    = require './xpath'

    class Property

      @property: (prop, desc) ->
        Object.defineProperty @prototype, prop, desc

      constructor: (@name, @schema={}) ->
        unless this instanceof Property then return new Property arguments...

        @state  =
          value: null
          container: null
          configurable: true
          enumerable: @binding?
          mutable: @schema.config?.valueOf() isnt false

        @schema.kind   ?= 'anydata'
        @schema.config ?= true

        # Bind the get/set functions to call with 'this' bound to this
        # Property instance.  This is needed since native Object
        # Getter/Setter uses the Object itself as 'this'
        @set = @set.bind this
        @get = @get.bind this

        # Below makes this Property adaptive to @schema changes
        unless @kind is 'list' and @name isnt @schema.datakey
          unless @kind isnt 'list' and @schema.parent?.kind is 'list'
            @schema.counter ?= 0
            @schema.counter++
            #console.log "#{@name} listening to schema change #{@schema.counter} times"
            @schema.on? 'change', =>
              debug? "[adaptive] #{@kind}(#{@name}) detected schema change, re-applying data"
              @set @content, force: true

        # soft freeze this instance
        Object.preventExtensions this

      delegate @prototype, 'state'
        .access 'container'
        .getter 'configurable'
        .getter 'enumerable'
        .getter 'mutable'

      delegate @prototype, 'schema'
        .getter 'kind'
        .getter 'type'
        .getter 'binding'

### Computed Properties

      @property 'content',
        get: -> @state.value
        set: (value) -> @set value, force: true

      @property 'context',
        get: ->
          ctx = Object.create(context)
          ctx.property = this
          Object.defineProperty ctx, 'action',
            get: -> @content if @content instanceof Function
          Object.preventExtensions ctx
          return ctx

      @property 'parent', get: -> @container?.__

      @property 'root',
        get: ->
          return this if @kind is 'module'
          if @parent instanceof Property then @parent.root
          else this

      @property 'props',
        get: -> prop for k, prop of @content?.__props__

      @property 'key',
        get: ->
          return unless @schema is @parent?.schema
          switch
            when @content not instanceof Object  then @name + 1
            when @content.hasOwnProperty('@key') then @content['@key']
            when Array.isArray @container
              for idx, item of @container when item is @content
                idx = Number(idx) unless (Number.isNaN (Number idx))
                return idx+1
              return undefined

      # TODO: cache this in @state so that it's not reconstructed every time
      @property 'path',
        get: ->
          return XPath.parse '/', @schema if this is @root
          #debug? "[path] #{@kind}(#{@name}) has #{@key} #{typeof @key}"
          entity = switch typeof @key
            when 'number' then ".[#{@key}]"
            when 'string' then ".[key() = '#{@key}']"
            else switch
              when @kind is 'list' then @schema.datakey
              else @name
          #debug? "[#{@name}] path: #{@parent.name} + #{entity}"
          @parent.path.append entity

## Instance-level methods

      emit: (event) ->
        return if this is @root
        debug? "[emit] '#{event}' from '#{@name}' to '#{@root.name}'"
        @root.emit arguments...

### join (obj)

This call is the primary mechanism via which the `Property` instance
attaches itself to the provided target `obj`. It registers itself into
`obj.__props__` as well as defined in the target `obj` via
`Object.defineProperty`.

      join: (obj, opts={ replace: false, suppress: false }) ->
        return obj unless obj instanceof Object

        # when joining for the first time, apply the data found in the
        # 'obj' into the property instance
        unless @container?
          debug? "[join] #{@kind}(#{@name}) assigning container"
          debug? opts
          @container = obj
          unless opts.replace is true
            opts.suppress = true
            @set obj[@name], opts
          return obj

        debug? "[join] #{@kind}(#{@name}) into #{obj.constructor.name} container"
        if Array.isArray(obj) and Array.isArray(@content)
          debug? @content
          throw @error "cannot join array property into list container"
        if @kind is 'list' and not Array.isArray(obj) and @content? and not Array.isArray(@content)
          throw @error "cannot join non-list array property into containing object"

        unless obj.hasOwnProperty '__props__'
          Object.defineProperty obj, '__props__', value: {}
        obj.__props__[@name] = this
        try Object.defineProperty obj, @name, this
        @emit 'update', this unless opts.suppress
        return obj

### get (pattern)

This is the main `Getter` for the target object's property value. When
called with optional `pattern` it will perform an internal
[find](#find-xpath) operation to traverse/locate that value being
requested instead of returning its own `@content`.

It also provides special handling based on different types of
`@content` currently held.

When `@content` is a function, it will call it with the current
`@context` instance as the bound context for the function being
called.

      get: (pattern) -> switch
        when pattern?
          match = @find pattern
          debug? match
          switch
            when match.length is 1 then match[0].get()
            when match.length > 1  then match.map (x) -> x.get()
            else undefined
        when @kind in [ 'rpc', 'action' ] then @invoke.bind this
        else
          @binding.call @context if @binding?
          # TODO: should utilize yield to resolve promises
          @content

### set (value)

This is the main `Setter` for the target object's property value.  It
utilizes internal `@schema` attribute if available to enforce schema
validations.

      set: (value, opts={ force: false, suppress: false }) ->
        debug? "[set] #{@kind}(#{@name}) enter with:"
        debug? value
        debug? opts
        unless @mutable or not value? or opts.force
          throw @error "cannot set data on read-only element"

        try Object.defineProperty value, '__', configurable: true, value: this
        value = switch
          when @schema.apply?
            @schema.apply value, @context.with(opts)
          else value
        try Object.defineProperty value, '__', value: this

        @state.prev = @state.value
        @state.enumerable = value? or @binding?
        @state.value = value

        try @join @container, opts
        catch e then @state.value = @state.prev; throw e
        debug? "[set] #{@kind}(#{@name}) completed"
        return this

### merge (value)

Performs a granular merge of `value` into existing `@content` if
available, otherwise performs [set](#set-value) operation.

      merge: (value, opts={ replace: true, suppress: false }) ->
        unless typeof @content is 'object' then return @set value

        value = value[@name] if value? and value.hasOwnProperty @name
        return unless typeof value is 'object'

        if Array.isArray @content
          length = @content.length
          debug? "[merge] merging into existing Array(#{length}) for #{@name}"
          value = [ value ] unless Array.isArray value
          try Object.defineProperty value, '__', configurable: true, value: this
          value = @schema.apply value
          value.forEach (item) =>
            item.__.name += length
            item.__.join @content, opts
          # TODO: need to re-apply schema on the 'list'
        else
          # TODO: protect this as a transaction?
          @content[k] = v for k, v of value when @content.hasOwnProperty k
          # TODO: need to reapply schema to self
        return this

### create (value)

A simple convenience wrap around the above [merge](#merge-value) operation.

      create: (value) ->
        @merge value, replace: false
        @emit 'create', this
        return this

### remove

The reverse of [join](#join-obj), it will detach itself from the
`@parent` containing object.

      remove: ->
        if @key?
          @container.splice @name, 1
          #delete @parent[@name]
        else
          @state.enumerable = false
          @state.value = undefined unless @kind is 'list'
          @join @container
        @emit 'delete', this
        return this

### find (pattern)

This helper routine can be used to allow traversal to other elements
in the data tree from the relative location of the current `Property`
instance. It returns matching `Property` instances based on the
provided `pattern` in the form of XPATH or YPATH.

It is internally used via [get](#get) and generally used inside
controller logic bound inside the [Yang expression](./yang.litcoffee)
as well as event handler listening on [Model](./model.litcoffee)
events.

It *always* returns an array (empty to denote no match) unless it
encounters an error, in which case it will throw an Error.

      find: (pattern='.', opts={}) ->
        xpath = switch
          when pattern instanceof XPath then pattern
          else XPath.parse pattern, @schema
        debug? "[#{@path}] finding #{pattern} starting with #{xpath.tag}"
        if opts.root or not @container? or xpath.tag not in [ '/', '..' ]
          debug? "[#{@path}] #{@name} applying '#{xpath}'"
          debug? @content
          xpath.apply(@content).props ? []
        else switch
          when xpath.tag is '/'  and @parent? then @parent.find xpath, opts
          when xpath.tag is '..' and @parent? then @parent.find xpath.xpath, opts
          else []

### invoke

A convenience wrap to a Property instance that holds a function to
perform a Promise-based execution.

      invoke: (args...) ->
        try
          ctx = @context
          unless ctx.action?
            throw @error "cannot invoke on a property without function"
          debug? "[invoke] calling #{@name} method"
          # TODO: need to ensure unique instance of 'input' and 'output' for concurrency
          ctx.input = args[0] ? {}
          ctx.action.apply ctx, args
          return co -> yield Promise.resolve ctx.output
        catch e
          return Promise.reject e

### error (msg)

Provides more contextual error message pertaining to the Property instance.

      error: (msg, ctx=this) ->
        at = "#{@path}"
        at += @name if at is '/'
        res = new Error "[#{at}] #{msg}"
        res.name = 'PropertyError'
        res.context = ctx
        return res

### inspect

      inspect: ->
        return {
          name:   @schema.tag ? @name
          kind:   @schema.kind
          key:    @key
          xpath:  @path.toString()
          schema: @schema.toJSON tag: false, extended: true
          active: @enumerable
          readonly: not @mutable
        }

### toJSON

This call creates a new copy of the current `Property.content`
completely detached/unbound to the underlying data schema. It's main
utility is to represent the current data state for subsequent
serialization/transmission. It accepts optional argument `tag` which
when called with `false` will not tag the produced object with the
property's `@name`.

      toJSON: (tag=true) ->
        copy = (src) ->
          return unless src? and typeof src isnt 'function'
          if typeof src is 'object'
            try res = new src.constructor
            catch then res = {}
            res[k] = copy v for own k, v of src
            return res
          src.constructor.call src, src
        value = copy @get()
        value ?= [] if @kind is 'list'
        if tag
          name = switch
            when @kind is 'list' then @schema.datakey
            else @name
          "#{name}": value
        else value

## Export Property Class

    module.exports = Property

# Property - controller of Object properties

The `Property` class is the *secretive shadowy* element that governs
`Object` behavior and are bound to the `Object` via
`Object.defineProperty`. It acts like a shadow `Proxy/Reflector` to
the `Object` instance and provides tight control via the
`Getter/Setter` interfaces.

The `Property` instances attach themselves to the `Object.__` property
and are rarely accessed directly - but they are the **fundamental**
actors that *actualize* YANG schema compliance into ordinary JS
objects.

Below are list of properties available to every instance of `Property`:

property | type | mapping | description
--- | --- | --- | ---
name   | string | direct | name of the property
schema | object | direct | a schema instance (usually [Yang](./src/yang.listcoffee))
state  | object | direct | *private* object holding internal state
container | object | access(state) | reference to object containing this property
configurable | boolean | getter(state) | defines whether this property can be redefined
enumerable   | boolean | getter(state) | defines whether this property is enumerable
content | any | computed | getter/setter for `state.value`
context | object | computed | dynamically generated using [context](./src/context.coffee)
root  | [Property](./src/property.litcoffee) | computed | dynamically returns the root Property instance
props | array(Property) | computed | returns children Property instances
key   | string/number | computed | conditionally returns unique key for Property if a list item
path  | [XPath](./src/xpath.coffee) | computed | dynamically generate XPath for this Property from root

## Class Property

    debug    = require('debug')('yang:property') if process.env.DEBUG?
    co       = require 'co'
    delegate = require 'delegates'
    context  = require './context'
    XPath    = require './xpath'

    class Property

      @property: (prop, desc) ->
        Object.defineProperty @prototype, prop, desc

      constructor: (@name, @schema={}) ->
        unless this instanceof Property then return new Property arguments...

        @state  =
          value: null
          container: null
          configurable: true
          enumerable: @binding?
          mutable: @schema.config?.valueOf() isnt false

        @schema.kind   ?= 'anydata'
        @schema.config ?= true

        # Bind the get/set functions to call with 'this' bound to this
        # Property instance.  This is needed since native Object
        # Getter/Setter uses the Object itself as 'this'
        @set = @set.bind this
        @get = @get.bind this

        # Below makes this Property adaptive to @schema changes
        unless @kind is 'list' and @name isnt @schema.datakey
          unless @kind isnt 'list' and @schema.parent?.kind is 'list'
            @schema.counter ?= 0
            @schema.counter++
            #console.log "#{@name} listening to schema change #{@schema.counter} times"
            @schema.on? 'change', =>
              debug? "[adaptive] #{@kind}(#{@name}) detected schema change, re-applying data"
              @set @content, force: true

        # soft freeze this instance
        Object.preventExtensions this

      delegate @prototype, 'state'
        .access 'container'
        .getter 'configurable'
        .getter 'enumerable'
        .getter 'mutable'

      delegate @prototype, 'schema'
        .getter 'kind'
        .getter 'type'
        .getter 'binding'

### Computed Properties

      @property 'content',
        get: -> @state.value
        set: (value) -> @set value, force: true

      @property 'context',
        get: ->
          ctx = Object.create(context)
          ctx.property = this
          Object.defineProperty ctx, 'action',
            get: -> @content if @content instanceof Function
          Object.preventExtensions ctx
          return ctx

      @property 'parent', get: -> @container?.__

      @property 'root',
        get: ->
          return this if @kind is 'module'
          if @parent instanceof Property then @parent.root
          else this

      @property 'props',
        get: -> prop for k, prop of @content?.__props__

      @property 'key',
        get: ->
          return unless @schema is @parent?.schema
          switch
            when @content not instanceof Object  then @name + 1
            when @content.hasOwnProperty('@key') then @content['@key']
            when Array.isArray @container
              for idx, item of @container when item is @content
                idx = Number(idx) unless (Number.isNaN (Number idx))
                return idx+1
              return undefined

      # TODO: cache this in @state so that it's not reconstructed every time
      @property 'path',
        get: ->
          return XPath.parse '/', @schema if this is @root
          #debug? "[path] #{@kind}(#{@name}) has #{@key} #{typeof @key}"
          entity = switch typeof @key
            when 'number' then ".[#{@key}]"
            when 'string' then ".[key() = '#{@key}']"
            else switch
              when @kind is 'list' then @schema.datakey
              else @name
          #debug? "[#{@name}] path: #{@parent.name} + #{entity}"
          @parent.path.append entity

## Instance-level methods

      emit: (event) ->
        return if this is @root
        debug? "[emit] '#{event}' from '#{@name}' to '#{@root.name}'"
        @root.emit arguments...

### join (obj)

This call is the primary mechanism via which the `Property` instance
attaches itself to the provided target `obj`. It registers itself into
`obj.__props__` as well as defined in the target `obj` via
`Object.defineProperty`.

      join: (obj, opts={ replace: false, suppress: false }) ->
        return obj unless obj instanceof Object

        # when joining for the first time, apply the data found in the
        # 'obj' into the property instance
        unless @container?
          debug? "[join] #{@kind}(#{@name}) assigning container"
          debug? opts
          @container = obj
          unless opts.replace is true
            opts.suppress = true
            @set obj[@name], opts
          return obj

        debug? "[join] #{@kind}(#{@name}) into #{obj.constructor.name} container"
        if Array.isArray(obj) and Array.isArray(@content)
          debug? @content
          throw @error "cannot join array property into list container"
        if @kind is 'list' and not Array.isArray(obj) and @content? and not Array.isArray(@content)
          throw @error "cannot join non-list array property into containing object"

        unless obj.hasOwnProperty '__props__'
          Object.defineProperty obj, '__props__', value: {}
        obj.__props__[@name] = this
        try Object.defineProperty obj, @name, this
        @emit 'update', this unless opts.suppress
        return obj

### get (pattern)

This is the main `Getter` for the target object's property value. When
called with optional `pattern` it will perform an internal
[find](#find-xpath) operation to traverse/locate that value being
requested instead of returning its own `@content`.

It also provides special handling based on different types of
`@content` currently held.

When `@content` is a function, it will call it with the current
`@context` instance as the bound context for the function being
called.

      get: (pattern) -> switch
        when pattern?
          match = @find pattern
          debug? match
          switch
            when match.length is 1 then match[0].get()
            when match.length > 1  then match.map (x) -> x.get()
            else undefined
        when @kind in [ 'rpc', 'action' ] then @invoke.bind this
        else
          @binding.call @context if @binding?
          # TODO: should utilize yield to resolve promises
          @content

### set (value)

This is the main `Setter` for the target object's property value.  It
utilizes internal `@schema` attribute if available to enforce schema
validations.

      set: (value, opts={ force: false, suppress: false }) ->
        debug? "[set] #{@kind}(#{@name}) enter with:"
        debug? value
        debug? opts
        unless @mutable or not value? or opts.force
          throw @error "cannot set data on read-only element"

        try Object.defineProperty value, '__', configurable: true, value: this
        value = switch
          when @schema.apply?
            @schema.apply value, @context.with(opts)
          else value
        try Object.defineProperty value, '__', value: this

        @state.prev = @state.value
        @state.enumerable = value? or @binding?
        @state.value = value

        try @join @container, opts
        catch e then @state.value = @state.prev; throw e
        debug? "[set] #{@kind}(#{@name}) completed"
        return this

### merge (value)

Performs a granular merge of `value` into existing `@content` if
available, otherwise performs [set](#set-value) operation.

      merge: (value, opts={ replace: true, suppress: false }) ->
        unless typeof @content is 'object' then return @set value

        value = value[@name] if value? and value.hasOwnProperty @name
        return unless typeof value is 'object'

        if Array.isArray @content
          length = @content.length
          debug? "[merge] merging into existing Array(#{length}) for #{@name}"
          value = [ value ] unless Array.isArray value
          try Object.defineProperty value, '__', configurable: true, value: this
          value = @schema.apply value
          value.forEach (item) =>
            item.__.name += length
            item.__.join @content, opts
          # TODO: need to re-apply schema on the 'list'
        else
          # TODO: protect this as a transaction?
          @content[k] = v for k, v of value when @content.hasOwnProperty k
          # TODO: need to reapply schema to self
        return this

### create (value)

A simple convenience wrap around the above [merge](#merge-value) operation.

      create: (value) ->
        @merge value, replace: false
        @emit 'create', this

### remove

The reverse of [join](#join-obj), it will detach itself from the
`@parent` containing object.

      remove: ->
        if @key?
          @container.splice @name, 1
          #delete @parent[@name]
        else
          @state.enumerable = false
          @state.value = undefined unless @kind is 'list'
          @join @container
        @emit 'delete', this
        return this

### find (pattern)

This helper routine can be used to allow traversal to other elements
in the data tree from the relative location of the current `Property`
instance. It returns matching `Property` instances based on the
provided `pattern` in the form of XPATH or YPATH.

It is internally used via [get](#get) and generally used inside
controller logic bound inside the [Yang expression](./yang.litcoffee)
as well as event handler listening on [Model](./model.litcoffee)
events.

It *always* returns an array (empty to denote no match) unless it
encounters an error, in which case it will throw an Error.

      find: (pattern='.', opts={}) ->
        xpath = switch
          when pattern instanceof XPath then pattern
          else XPath.parse pattern, @schema
        debug? "[#{@path}] finding #{pattern} starting with #{xpath.tag}"
        if opts.root or not @container? or xpath.tag not in [ '/', '..' ]
          debug? "[#{@path}] #{@name} applying '#{xpath}'"
          debug? @content
          xpath.apply(@content).props ? []
        else switch
          when xpath.tag is '/'  and @parent? then @parent.find xpath, opts
          when xpath.tag is '..' and @parent? then @parent.find xpath.xpath, opts
          else []

### invoke

A convenience wrap to a Property instance that holds a function to
perform a Promise-based execution.

      invoke: (args...) ->
        try
          ctx = @context
          unless ctx.action?
            throw @error "cannot invoke on a property without function"
          debug? "[invoke] calling #{@name} method"
          # TODO: need to ensure unique instance of 'input' and 'output' for concurrency
          ctx.input = args[0] ? {}
          ctx.action.apply ctx, args
          return co -> yield Promise.resolve ctx.output
        catch e
          return Promise.reject e

### error (msg)

Provides more contextual error message pertaining to the Property instance.

      error: (msg, ctx=this) ->
        at = "#{@path}"
        at += @name if at is '/'
        res = new Error "[#{at}] #{msg}"
        res.name = 'PropertyError'
        res.context = ctx
        return res

### inspect

      inspect: ->
        return {
          name:   @schema.tag ? @name
          kind:   @schema.kind
          key:    @key
          xpath:  @path.toString()
          schema: @schema.toJSON tag: false, extended: true
          active: @enumerable
          readonly: not @mutable
        }

### toJSON

This call creates a new copy of the current `Property.content`
completely detached/unbound to the underlying data schema. It's main
utility is to represent the current data state for subsequent
serialization/transmission. It accepts optional argument `tag` which
when called with `false` will not tag the produced object with the
property's `@name`.

      toJSON: (tag=true) ->
        copy = (src) ->
          return unless src? and typeof src isnt 'function'
          if typeof src is 'object'
            try res = new src.constructor
            catch then res = {}
            res[k] = copy v for own k, v of src
            return res
          src.constructor.call src, src
        value = copy @get()
        value ?= [] if @kind is 'list'
        if tag
          name = switch
            when @kind is 'list' then @schema.datakey
            else @name
          "#{name}": value
        else value

## Export Property Class

    module.exports = Property