fixing tempalte

fixing tempalte

handlebars = require 'handlebars'
    aws = require 'aws-sdk'
    shortid = require 'shortid'

Setup the connection to s3

    aws.config.update {
        accessKeyId: process.env["AWS_ACCESS_KEY"],
        secretAccessKey: process.env["AWS_SECRET_KEY"]
    }
    aws.config.region = 'us-east-1'
    s3 = new aws.S3()

Set the name for your domain.

    module.exports = (url) ->
      DOMAIN = process.env["DOMAIN"] || "shortcake.com"
      BUCKET = process.env["BUCKET"] || DOMAIN
      GA_ID = process.env["GA_ID"]
      shortid.characters "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ()"

This is our basic HTML template that we'll use for doing the redirect. It's just
going to send the user to another page as soon as it's loaded. There's no server
since it's just a static file, but everything will be tracked by GA.

      source = """

          <title>{{url}}</title>

      """

      template = handlebars.compile(source)

Data should just be the user's Google Analytics Id and the URL that they want to
minify.

      data = { url: url, ga_id: GA_ID }
      html = template(data)

We're going to generate a shortlink for the URL. [`shortid`](https://github.com/dylang/shortid) does a pretty good job at balancing between being short, and also
being unique!

      _id = shortid.generate()

We could break up the files by the [first 3 letters in the id](http://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html).
This makes it much easier for S3 to distribute the bucket (and greatly improves performance).
...but we're not going to do this yet.

      params = {
          Bucket: BUCKET,
          Key: _id
          ACL: "public-read",
          Body: html
      }
      s3.putObject params, (err, data) ->
          if err
              console.log err

We'll keep a running record of all links we've shortened and throw it into a
basic HTML page just to make it easy to do lookups.

      source = """

          <title>Shortened Links</title>

Shortened Links

              {{#objs}}

{{ Key }}

              {{/objs}}

      """
      params = { Bucket: BUCKET }
      s3.listObjects params, (err, objs) ->
        if err
            console.log "[ERROR]: could not list objects: " + err
        objs = objs.Contents.filter (obj) ->
          ! /index.html$/.test obj.Key
        html = handlebars.compile(source)({ objs: objs })
        params = {
            Bucket: BUCKET,
            Key: "index.html",
            ACL: "public-read",
            Body: html
        }
        s3.putObject params, (err, data) ->
            if err
                console.log "[ERROR]: " + err
        console.error "Visit: https://s3.amazonaws.com/#{BUCKET}/index.html"

handlebars = require 'handlebars'
    aws = require 'aws-sdk'
    shortid = require 'shortid'

Setup the connection to s3

    aws.config.update {
        accessKeyId: process.env["AWS_ACCESS_KEY"],
        secretAccessKey: process.env["AWS_SECRET_KEY"]
    }
    aws.config.region = 'us-east-1'
    s3 = new aws.S3()

Set the name for your domain.

    module.exports = (url) ->
      DOMAIN = process.env["DOMAIN"] || "shortcake.com"
      BUCKET = process.env["BUCKET"] || DOMAIN
      GA_ID = process.env["GA_ID"]
      shortid.characters "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ()"

This is our basic HTML template that we'll use for doing the redirect. It's just
going to send the user to another page as soon as it's loaded. There's no server
since it's just a static file, but everything will be tracked by GA.

      source = """

          <title>{{url}}</title>

      """

      template = handlebars.compile(source)

Data should just be the user's Google Analytics Id and the URL that they want to
minify.

      data = { url: url, ga_id: GA_ID }
      html = template(data)

We're going to generate a shortlink for the URL. [`shortid`](https://github.com/dylang/shortid) does a pretty good job at balancing between being short, and also
being unique!

      _id = shortid.generate()

We could break up the files by the [first 3 letters in the id](http://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html).
This makes it much easier for S3 to distribute the bucket (and greatly improves performance).
...but we're not going to do this yet.

      params = {
          Bucket: BUCKET,
          Key: _id
          ACL: "public-read",
          Body: html
      }
      s3.putObject params, (err, data) ->
          if err
              console.log err

We'll keep a running record of all links we've shortened and throw it into a
basic HTML page just to make it easy to do lookups.

      source = """

          <title>Shortened Links</title>

Shortened Links

              {{#objs}}

{{ Key }}

              {{/objs}}

      """
      params = { Bucket: BUCKET }
      s3.listObjects params, (err, objs) ->
        if err
            console.log "[ERROR]: could not list objects: " + err
        objs = objs.Contents.filter (obj) ->
          ! /index.html$/.test obj.Key
        html = handlebars.compile(source)({ objects: objs })
        params = {
            Bucket: BUCKET,
            Key: "index.html",
            ACL: "public-read",
            Body: html
        }
        s3.putObject params, (err, data) ->
            if err
                console.log "[ERROR]: " + err
        console.error "Visit: https://s3.amazonaws.com/#{BUCKET}/index.html"


Measurements don't need a position attribute; they're sorted by date submitted.

`Measurements` are ...

    @Measurements = new Mongo.Collection 'measurements'

    Measurements.allow
      update: (userId, measurement) -> false
      remove: (userId, measurement) -> false

Insert a new measurement

    @insertMeasurement = (measurementAttributes) ->
      validateMeasurement measurementAttributes
      measurement = _.extend measurementAttributes,
        submitted: new Date()
        kind: 'measurement'

Create the measurement

      measurement._id = Measurements.insert measurement
      return measurement._id

`Measurements` are ...

    @Measurements = new Mongo.Collection 'measurements'

    Measurements.allow
      update: (userId, measurement) -> false
      remove: (userId, measurement) -> false

Insert a new measurement

    @insertMeasurement = (measurementAttributes) ->
      validateMeasurement measurementAttributes
      measurement = _.extend measurementAttributes,
        submitted: new Date()
        position: 0
        kind: 'measurement'

Update the positions of the existing measurements

      Measurements.update({_id: m._id}, {$set: {position: m.position+1}}) for m in Measurements.find({projectId: measurement.projectId}).fetch()

Create the measurement

      measurement._id = Measurements.insert measurement
      return measurement._id


Replace hard coded query logic

Replace hard coded query logic

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

    sentry      = require './db/sentry'
    mongo       = require './db/mongo'

    collections = [
      'turer'
      'steder'
      'grupper'
      'områder'
      'bilder'
      'arrangementer'
    ]

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

## PARAM {collection}

    exports.param = (req, res, next, col) ->
      if col not in ['turer', 'steder', 'grupper', 'områder', 'bilder', 'arrangementer']
        return res.json 404, message: 'Objekttype ikke funnet'

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

      next()

## OPTIONS /{collection}

    exports.options = (req, res, next) ->
      res.setHeader 'Access-Control-Expose-Headers', [
        'ETag', 'Location', 'Last-Modified', 'Count-Return', 'Count-Total'
      ].join(', ')
      res.setHeader 'Access-Control-Max-Age', 86400
      res.setHeader 'Access-Control-Allow-Headers', 'Content-Type'
      res.setHeader 'Access-Control-Allow-Methods', 'HEAD, GET, POST'
      res.send 204

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

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

### Query

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

Prevent private documents for other API user from being returned when quering
`tilbyder` and `status` fields.

      for key, val of req.query
        switch key
          when 'status'
            req.db.query.tilbyder = req.user if val not in ['Offentlig', 'Slettet']
            break
          when 'tilbyder'
            req.db.query.status = 'Offentlig' if val isnt req.user
            break
          else
            if key.substr(0,6) is 'privat'
              req.db.query.tilbyder = req.user
              break

Apply default access control unless `status` or `tilbyder` fields are already
queried.

      if not req.db.query.tilbyder or req.db.query.status
        req.db.query.$or = [{status: 'Offentlig'}, {tilbyder: req.usr}]

#### ToDo

 * Limit number of private fields?
 * Limit depth of private fields?

`NB` This section is looping through all of the url query parameters and hence
should take the queries from above sections into concideration so we don't need
to do double amount of work.

      for key, val of req.query when key.substr(0,7) is 'privat.'
        if /^[a-zæøåA-ZÆØÅ0-9_.]+$/.test key
          val = parseFloat(val) if not isNaN val # @TODO(starefossen) is this acceptable?
          query.tilbyder = req.usr
          query[key] = val

Limit queries to own documents or public documents i.e. where `doc.status` is
`Offentlig` if not `query.tilbyder` is set.

      query['$or'] = [{status: 'Offentlig'}, {tilbyder: req.usr}] if not query.tilbyder

Only project a few fields to since lists are mostly used intermediate before
fetching the entire document.

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

Set up MongoDB options.

      options =
        limit: Math.min((parseInt(req.query.limit, 10) or 20), 50)
        skip: parseInt(req.query.skip, 10) or 0
        sort: 'endret'

Retrive matching documents from MongoDB.

      cursor = req.db.col.find(query, fields, options)
      cursor.count (err, total) ->
        return next err if err
        res.set 'Count-Return', Math.min(options.limit, total)
        res.set 'Count-Total', total
        return res.send 204 if req.method is 'HEAD'
        return res.json documents: [], count: 0, total: 0 if total is 0
        res.set 'Content-Type', 'application/json; charset=utf-8'

Calculate number of rows returned since we don't know that in advanced (due to
the nature of streaming).

        count = Math.min(options.limit, Math.max(total - options.skip, 0))

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

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

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

## POST /{collection}

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

      req.body.tilbyder = req.usr

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

            sentry.captureDocumentError req, err

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

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

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

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

    sentry      = require './db/sentry'
    mongo       = require './db/mongo'

    collections = [
      'turer'
      'steder'
      'grupper'
      'områder'
      'bilder'
      'arrangementer'
    ]

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

## PARAM {collection}

    exports.param = (req, res, next, col) ->
      if col not in ['turer', 'steder', 'grupper', 'områder', 'bilder', 'arrangementer']
        return res.json 404, message: 'Objekttype ikke funnet'

      req.type = col
      req.db   = col: mongo[col]

      next()

## OPTIONS /{collection}

    exports.options = (req, res, next) ->
      res.setHeader 'Access-Control-Expose-Headers', [
        'ETag', 'Location', 'Last-Modified', 'Count-Return', 'Count-Total'
      ].join(', ')
      res.setHeader 'Access-Control-Max-Age', 86400
      res.setHeader 'Access-Control-Allow-Headers', 'Content-Type'
      res.setHeader 'Access-Control-Allow-Methods', 'HEAD, GET, POST'
      res.send 204

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

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

### ?tag=`String`

      if typeof req.query.tag is 'string' and req.query.tag isnt ''
        if req.query.tag.charAt(0) is '!' and req.query.tag.length > 1
          query['tags.0'] = $ne: req.query.tag.substr(1)
        else
          query['tags.0'] = req.query.tag

### ?grupper=`String`

      if typeof req.query.gruppe is 'string' and req.query.gruppe isnt ''
        query['grupper'] = req.query.gruppe

### ?after=`Mixed`

      if typeof req.query.after is 'string' and req.query.after isnt ''
        time = req.query.after

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

        time = new Date time

        if time.toString() isnt 'Invalid Date'
          query.endret = $gte: time.toISOString()

### ?bbox=`min_lng`,`min_lat`,`max_lng`,`max_lat`

      if typeof req.query.bbox is 'string' and req.query.bbox.split(',').length is 4
        bbox = req.query.bbox.split(',')
        bbox[i] = parseFloat(val) for val, i in bbox

        query.geojson =
          '$geoWithin':
            '$geometry':
              type: 'Polygon'
              coordinates: [[
                [bbox[0], bbox[1]]
                [bbox[2], bbox[1]]
                [bbox[2], bbox[3]]
                [bbox[0], bbox[3]]
                [bbox[0], bbox[1]]
              ]]

### ?privat.`String`=`String`

This allows the user to filter documents based on private document properties.
This will autumaticly limit returned documents to those owned by the current
user.

#### ToDo

 * Limit number of private fields?
 * Limit depth of private fields?

`NB` This section is looping through all of the url query parameters and hence
should take the queries from above sections into concideration so we don't need
to do double amount of work.

      for key, val of req.query when key.substr(0,7) is 'privat.'
        if /^[a-zæøåA-ZÆØÅ0-9_.]+$/.test key
          val = parseFloat(val) if not isNaN val # @TODO(starefossen) is this acceptable?
          query.tilbyder = req.usr
          query[key] = val

Limit queries to own documents or public documents i.e. where `doc.status` is
`Offentlig` if not `query.tilbyder` is set.

      query['$or'] = [{status: 'Offentlig'}, {tilbyder: req.usr}] if not query.tilbyder

Only project a few fields to since lists are mostly used intermediate before
fetching the entire document.

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

Set up MongoDB options.

      options =
        limit: Math.min((parseInt(req.query.limit, 10) or 20), 50)
        skip: parseInt(req.query.skip, 10) or 0
        sort: 'endret'

Retrive matching documents from MongoDB.

      cursor = req.db.col.find(query, fields, options)
      cursor.count (err, total) ->
        return next err if err
        res.set 'Count-Return', Math.min(options.limit, total)
        res.set 'Count-Total', total
        return res.send 204 if req.method is 'HEAD'
        return res.json documents: [], count: 0, total: 0 if total is 0
        res.set 'Content-Type', 'application/json; charset=utf-8'

Calculate number of rows returned since we don't know that in advanced (due to
the nature of streaming).

        count = Math.min(options.limit, Math.max(total - options.skip, 0))

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

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

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

## POST /{collection}

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

      req.body.tilbyder = req.usr

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

            sentry.captureDocumentError req, err

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

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

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


NativeItem.defineProperty predefined types added

# Native

    'use strict'

    utils = require 'src/utils'
    log = require 'src/log'
    assert = require 'src/assert'
    nativeBridge = require 'src/native'
    colorUtils = require 'src/renderer/utils/color'

# **Class** Native : *Item*

    module.exports = (Renderer, Impl, itemUtils) ->

        class Native extends Renderer.Item
            @__name__ = 'Native'
            @__path__ = 'Renderer.Native'

            @New = (component, opts) ->
                item = new this
                itemUtils.Object.initialize item, component, opts
                @Initialize? item
                item

## Native.defineProperty(*Object* config)

Defines new property with the given name.

For each property, signal `onXXXChange` is created,
where `XXX` is the given name.

`config` parameter must be an object with specified keys:
- `enabled` - whether it's supported on current platform,
- `name` - name of the property,
- `type` - type of predefined condifuration described below,
- `defaultValue`,
- `setter`,
- `getter`,
- `developmentSetter`
- `implementationValue` - function returning value passed to the implementation.

### Predefined types

            PROPERTY_TYPES = Object.create null

#### text

            PROPERTY_TYPES.text = ->
                defaultValue: ''
                developmentSetter: (val) ->
                    assert.isString val

#### number

            PROPERTY_TYPES.number = ->
                defaultValue: 0
                developmentSetter: (val) ->
                    assert.isFloat val

#### boolean

            PROPERTY_TYPES.boolean = ->
                defaultValue: false
                developmentSetter: (val) ->
                    assert.isBoolean val

#### color

            PROPERTY_TYPES.color = (config) ->
                defaultValue: ''
                developmentSetter: (val) ->
                    assert.isString val
                implementationValue: (val) ->
                    colorUtils.toRGBAHex val, config.defaultValue

            @defineProperty = (config) ->
                itemName = @__name__
                properties = @_properties ||= []
                config = utils.clone config

                assert.isString itemName, '''
                    NativeItem.__name__ unique name must be specified
                '''
                assert.isObject config, '''
                    NativeItem.defineProperty config parameter must be an object
                '''
                assert.isString config.name, '''
                    NativeItem property name must be a string
                '''
                assert.isNotDefined properties[config.name], """
                    Property #{config.name} is already defined
                """
                assert.isDefined PROPERTY_TYPES[config.type], """
                    Unknown property type #{config.type}
                """ if config.type

                # type
                if typeConfigFunc = PROPERTY_TYPES[config.type]
                    typeConfig = typeConfigFunc(config)
                    for key, val of typeConfig
                        if key not of config
                            config[key] = val

                # constructor
                config.constructor = @

                # internalName
                config.internalName = itemUtils.getPropInternalName config.name

                # implementation
                config.implementation = do ->
                    if config.enabled is false
                        return utils.NOP

                    ctorName = utils.capitalize itemName
                    name = utils.capitalize config.name
                    funcName = "rendererSet#{ctorName}#{name}"
                    (val) ->
                        nativeBridge.callFunction funcName, @_impl.id, val

                # save
                properties.push config

                # create
                itemUtils.defineProperty config

            constructor: ->
                super()

                # save properties with default values
                if properties = @constructor._properties
                    for property in properties
                        @[property.internalName] = property.defaultValue

## Native::set(*String* propName, *Any* val)

            set: (name, val) ->
                assert.isString name

                ctorName = utils.capitalize @constructor.__name__
                id = @_impl.id
                name = utils.capitalize name
                funcName = "rendererSet#{ctorName}#{name}"
                nativeBridge.callFunction funcName, id, val
                return

## Native::call(*String* funcName, *Any* args...)

            call: (name, args...) ->
                assert.isString name

                ctorName = utils.capitalize @constructor.__name__
                id = @_impl.id
                name = utils.capitalize name
                funcName = "rendererCall#{ctorName}#{name}"

                callArgs = [funcName, id, args...]
                nativeBridge.callFunction.apply nativeBridge, callArgs
                return

## Native::on(*String* eventName, *Function* listener)

            # nativeEventName -> item id -> [item, listeners...]
            eventListeners = Object.create null

            createNativeEventListener = (listeners, eventName) -> (id) ->
                unless itemListeners = listeners[id]
                    log.warn "Got a native event '#{eventName}' for an item which " +
                        "didn't register a listener on this event; check if you " +
                        "properly call 'on()' method with a signal listener"
                    return

                length = arguments.length
                args = new Array length - 1
                for i in [0...length-1] by 1
                    args[i] = arguments[i + 1]

                item = itemListeners[0]
                for i in [1...itemListeners.length] by 1
                    itemListeners[i].apply item, args

                return

            on: (name, func) ->
                assert.isString name
                assert.isFunction func

                ctorName = utils.capitalize @constructor.__name__
                name = utils.capitalize name
                eventName = "rendererOn#{ctorName}#{name}"

                unless listeners = eventListeners[eventName]
                    listeners = eventListeners[eventName] = Object.create(null)
                    nativeBridge.on eventName, createNativeEventListener(listeners, eventName)

                itemListeners = listeners[@_impl.id] ?= [@]
                itemListeners.push func
                return

        Native

# Glossary

- [Native](#class-native)

# Native

    'use strict'

    utils = require 'src/utils'
    log = require 'src/log'
    assert = require 'src/assert'
    nativeBridge = require 'src/native'

# **Class** Native : *Item*

    module.exports = (Renderer, Impl, itemUtils) ->

        class Native extends Renderer.Item
            @__name__ = 'Native'
            @__path__ = 'Renderer.Native'

            @New = (component, opts) ->
                item = new this
                itemUtils.Object.initialize item, component, opts
                @Initialize? item
                item

            @defineProperty = (opts) ->
                unless opts.hasOwnProperty('constructor')
                    opts.constructor = this
                opts.implementation ?= do ->
                    ctorName = utils.capitalize opts.constructor.__name__
                    name = utils.capitalize opts.name
                    funcName = "rendererSet#{ctorName}#{name}"
                    (val) ->
                        nativeBridge.callFunction funcName, @_impl.id, val
                itemUtils.defineProperty opts

            constructor: ->
                super()

## Native::set(*String* propName, *Any* val)

            set: (name, val) ->
                assert.isString name

                ctorName = utils.capitalize @constructor.__name__
                id = @_impl.id
                name = utils.capitalize name
                funcName = "rendererSet#{ctorName}#{name}"
                nativeBridge.callFunction funcName, id, val
                return

## Native::call(*String* funcName, *Any* args...)

            call: (name, args...) ->
                assert.isString name

                ctorName = utils.capitalize @constructor.__name__
                id = @_impl.id
                name = utils.capitalize name
                funcName = "rendererCall#{ctorName}#{name}"

                callArgs = [funcName, id, args...]
                nativeBridge.callFunction.apply nativeBridge, callArgs
                return

## Native::on(*String* eventName, *Function* listener)

            # nativeEventName -> item id -> [item, listeners...]
            eventListeners = Object.create null

            createNativeEventListener = (listeners, eventName) -> (id) ->
                unless itemListeners = listeners[id]
                    log.warn "Got a native event '#{eventName}' for an item which " +
                        "didn't register a listener on this event; check if you " +
                        "properly call 'on()' method with a signal listener"
                    return

                length = arguments.length
                args = new Array length - 1
                for i in [0...length-1] by 1
                    args[i] = arguments[i + 1]

                item = itemListeners[0]
                for i in [1...itemListeners.length] by 1
                    itemListeners[i].apply item, args

                return

            on: (name, func) ->
                assert.isString name
                assert.isFunction func

                ctorName = utils.capitalize @constructor.__name__
                name = utils.capitalize name
                eventName = "rendererOn#{ctorName}#{name}"

                unless listeners = eventListeners[eventName]
                    listeners = eventListeners[eventName] = Object.create(null)
                    nativeBridge.on eventName, createNativeEventListener(listeners, eventName)

                itemListeners = listeners[@_impl.id] ?= [@]
                itemListeners.push func
                return

        Native

# Glossary

- [Native](#class-native)


docs: nextIfError -> skipIfError

docs: nextIfError -> skipIfError

# Batman.StorageAdapter

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

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

__Protip:__ Consider setting the storage adapter in one `Batman.Model` subclass, then extending that subclass in the rest of your app. Then, you won't have to call `@persist` every time:

```coffeescript
class App.Model extends Batman.Model
  @persist Batman.LocalStorage

class App.Superhero extends App.Model
  # no need to call @persist here!

class App.Sidekick extends App.Model
  # or here!
```

### Batman's Included StorageAdapters

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

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

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

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

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

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

## Subclassing Batman.StorageAdapter

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

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

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

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

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

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

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

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

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

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

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

### Adding Callbacks

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

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

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

## Storage Operations

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

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

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

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

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

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

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

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

## Other Useful Methods

## @skipIfError(wrappedFunction : Function )

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

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

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

      dummyEnv = {error: true}

      functionWithWrapper(dummyEnv, nextFunction)

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

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

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

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

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

# Batman.StorageAdapter

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

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

__Protip:__ Consider setting the storage adapter in one `Batman.Model` subclass, then extending that subclass in the rest of your app. Then, you won't have to call `@persist` every time:

```coffeescript
class App.Model extends Batman.Model
  @persist Batman.LocalStorage

class App.Superhero extends App.Model
  # no need to call @persist here!

class App.Sidekick extends App.Model
  # or here!
```

### Batman's Included StorageAdapters

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

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

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

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

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

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

## Subclassing Batman.StorageAdapter

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

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

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

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

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

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

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

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

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

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

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

### Adding Callbacks

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

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

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

## Storage Operations

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

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

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

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

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

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

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

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

## Other Useful Methods

## @nextIfError(wrappedFunction : Function )

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

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

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

      dummyEnv = {error: true}

      functionWithWrapper(dummyEnv, nextFunction)

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

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

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

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

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


Add error on malformed hyperlink object.

Node-XLSX-Writer
================

Node-XLSX-Writer is written in literate coffeescript. The following is the actual source of the
module.

    fs = require('fs')
    blobs = require('./blobs')
    Archiver = require('archiver')
    numberRegex = /^[1-9\.][\d\.]+$/

    module.exports = class XlsxWriter

### Simple writes

##### XlsxWriter.write(out: String, data: Array, cb: Function)

The simplest way to use Node-XLSX-Writer is to use the write method.

The callback comes directly from `fs.writeFile` and has the arity `(err)`

      # @param {String} out Output file path.
      # @param {Array} data Data to write.
      # @param {Function} cb Callback to call when done. Fed (err).
      @write = (out, data, cb) ->
          writer = new XlsxWriter({out: out})
          writer.addRows(data)
          writer.writeToFile(cb)

### Advanced usage

Node-XLSX-Writer has more advanced features available for better customization
of spreadsheets.

When constructing a writer, pass it an optional file path and customization options.

##### new XlsxWriter([options]: Object) : XlsxWriter

      # Build a writer object.
      # @param {Object} [options] Preparation options.
      # @param {String} [options.out] Output file path.
      # @param {Array}  [options.columns] Column definition. Must be added in constructor.
      # @example options.columns = [
      #     {  width: 30 }, // width is in 'characters'
      #     {  width: 10 }
      # ]
      constructor: (options = {}) ->

        # Support just passing a string path into the constructor.
        if (typeof options == 'string')
          options = {out: options}

        # Set options.
        defaults = {
            defaultWidth: 15
            zip: {
              forceUTC: true # this is required, zips will be unreadable without it
            },
            columns: []
        }
        @options = _extend(defaults, options)

        # Start sheet.
        @_resetSheet()

        # Write column definition.
        @defineColumns(@options.columns)

        # Create Zip.
        zipOptions = @options.zip || {}
        zipOptions.forceUTC = true # force this on in all cases for now, otherwise we're useless
        @zip = Archiver('zip', zipOptions)

        # Archiver attaches an exit listener on the process, we don't want this,
        # it will fire if this object is never finalized.
        @zip.catchEarlyExitAttached = true

        # Hook this passthrough into the zip stream.
        @zip.append(@sheetStream, {name: 'xl/worksheets/sheet1.xml'})

#### Adding rows

Rows are easy to add one by one or all at once. Data types within the sheet will
be inferred from the data types passed to addRow().

##### addRow(row: Object)

Add a single row.

      # @example (javascript)
      # writer.addRow({
      #     "A String Column" : "A String Value",
      #     "A Number Column" : 12345,
      #     "A Date Column" : new Date(1999,11,31)
      # })
      addRow: (row) ->

        # Values in header are defined by the keys of the object we've passed in.
        # They need to be written the first time they're passed in.
        if !@haveHeader
          @_write(blobs.sheetDataHeader)
          @_startRow()
          col = 1
          for key of row
            @_addCell(key, col)
            @cellMap.push(key)
            col += 1
          @_endRow()

          @haveHeader = true

        @_startRow()
        for key, col in @cellMap
          @_addCell(row[key] || "", col + 1)
        @_endRow()

##### addRows(rows: Array)

Rows can be added in batch.

      addRows: (rows) ->
        for row in rows
          @addRow(row)

##### defineColumns(columns: Array)

Column definitions can be easily added, but it *must* be done before rows are added
to prevent a nasty Excel bug.

      # @example (javascript)
      # writer.defineColumns([
      #     {  width: 30 }, // width is in 'characters'
      #     {  width: 10 }
      # ])
      defineColumns: (columns) ->
        if @haveHeader
          throw new Error """
            Columns cannot be added after rows! Unfortunately Excel will crash
            if column definitions come after sheet data. Please move your `defineColumns()`
            call before any `addRow()` calls, or define options.columns in the XlsxWriter
            constructor.
          """
        @options.columns = columns
        # Write column metadata.
        # Would really like to do this at the end so that we don't have to mandate
        # it comes first, but Excel pukes if <cols> comes before <sheetData>.
        @_write(@_generateColumnDefinition())

#### File generation

Once you are done adding rows & defining columns, you have a few options
for generating the file. The `writeToFile` helper is a one-stop-shop for writing
directly to a file using `fs.writeFile`; otherwise, you can pack() manually,
which will return a readable stream.

##### writeToFile([fileName]: String, cb: Function)

Writes data to a file. Convenience method.

If no filename is specified, will attempt to use the one specified in the
constructor as `options.out`.

The callback is fed directly to `fs.writeFile`.

      # @param {String} [fileName] File path to write.
      # @param {Function} cb Callback.
      writeToFile: (fileName, cb) ->
        if fileName instanceof Function
          cb = fileName
          fileName = @options.out
        if !fileName
          throw new Error("Filename required. Supply one in writeToFile() or in options.out.")

        # Create zip, pipe it into a file writeStream.
        zip = @createReadStream(fileName)
        fileStream = fs.createWriteStream(fileName);
        fileStream.once 'finish', cb
        zip.pipe(fileStream)

##### createReadStream([jsZipOptions]: Object) : Stream

Packs the file and returns a readable stream. You can pipe this directly to a file
or response object. Be sure to use 'binary' mode.

Will finalize the sheet & generate shared strings if they haven't been already.

      # @return {Stream} Readable stream with ZIP data.
      createReadStream: (options) ->

        # Finalize sheet if it hasn't been already.
        if (!@finalized)
          @finalize()

        @zip
          .append(blobs.contentTypes, {name: '[Content_Types].xml'})
          .append(blobs.rels, {name: '_rels/.rels'})
          .append(blobs.workbook, {name: 'xl/workbook.xml'})
          .append(blobs.styles, {name: 'xl/styles.xml'})
          .append(blobs.workbookRels, {name: 'xl/_rels/workbook.xml.rels'})
          .append(@relsData, {name: 'xl/worksheets/_rels/sheet1.xml.rels'})
          .append(@stringsData, {name: 'xl/sharedStrings.xml'})
          .finalize()

        return @zip

##### finalize()

Finishes up the sheet & generate shared strings.

      finalize: () ->

        # If there was data, end sheetData
        if @haveHeader
          @_write(blobs.sheetDataFooter)

        # Write relationships data.
        @_write(blobs.worksheetRels(@relationships))

        # End sheet
        @sheetStream.end(blobs.sheetFooter);

        # Generate shared strings
        @_generateStrings()

        # Generate external rels
        @_generateRelationships()

        # Mark this as finished
        @finalized = true

##### dispose()

Cancel use of this writer and close all streams. This is not needed if you've written to a file.

      dispose: () ->
        return if @disposed

        @sheetStream.end()
        @sheetStream.unpipe()
        @zip.unpipe()
        while(@zip.read()) # drain stream
          1; # noop
        delete @zip
        delete @sheetStream
        @disposed = true

#### Internal methods

Adds a cell to the row in progress.

      # @param {String|Number|Date} value Value to write.
      # @param {Number}             col   Column index.
      _addCell: (value = '', col) ->
        row = @currentRow
        cell = @_getCellIdentifier(row, col)

        # Hyperlink support
        if Object.prototype.toString.call(value) == '[object Object]'
          if !value.value || !value.hyperlink
            throw new Error("A hyperlink cell must have both 'value' and 'hyperlink' keys.")
          @_addCell(value.value, col)
          @_createRelationship(cell, value.hyperlink)
          return

        if typeof value == 'number' or numberRegex.test(value)
          @rowBuffer += blobs.numberCell(value, cell)
        else if value instanceof Date
          date = @_dateToOADate(value)
          @rowBuffer += blobs.dateCell(date, cell)
        else
          index = @_lookupString(value)
          @rowBuffer += blobs.cell(index, cell)

Begins a row. Call this before starting any row. Will start a buffer
for all proceeding cells, until @_endRow is called.

      _startRow: () ->
        @rowBuffer = blobs.startRow(@currentRow)
        @currentRow += 1

Ends a row. Will write the row to the sheet.

      _endRow: () ->
        @_write(@rowBuffer + blobs.endRow)

Given row and column indices, returns a cell identifier, e.g. "E20"

      # @param {Number} row  Row index.
      # @param {Number} cell Cell index.
      # @return {String}     Cell identifier.
      _getCellIdentifier: (row, col) ->
        colIndex = ''
        if @cellLabelMap[col]
          colIndex = @cellLabelMap[col]
        else
          if col == 0
            # Provide a fallback for empty spreadsheets
            row = 1
            col = 1

          input = (+col - 1).toString(26)
          while input.length
            a = input.charCodeAt(input.length - 1)
            colIndex = String.fromCharCode(a + if a >= 48 and a <= 57 then 17 else -22) + colIndex
            input = if input.length > 1 then (parseInt(input.substr(0, input.length - 1), 26) - 1).toString(26) else ""
          @cellLabelMap[col] = colIndex

        return colIndex + row

Creates column definitions, if any definitions exist.
This will write column styles, widths, etc.

      # @return {String} Column definition.
      _generateColumnDefinition: () ->
        # <cols/> tag (empty) crashes excel, weeeeee
        if !@options.columns || !@options.columns.length
          return ''

        columnDefinition = ''
        columnDefinition += blobs.startColumns

        idx = 1
        for index, column of @options.columns
          columnDefinition += blobs.column(column.width || @options.defaultWidth, idx)
          idx += 1

        columnDefinition += blobs.endColumns
        return columnDefinition

Generates StringMap XML. Used as a finalization step - don't call this while
building the xlsx is in progress.

Saves string data to this object so it can be written to the zip.

      _generateStrings: () ->
        stringTable = ''
        for string in @strings
          stringTable += blobs.string(@escapeXml(string))
        @stringsData = blobs.stringsHeader(@strings.length) + stringTable + blobs.stringsFooter

Looks up a string inside the internal string map. If it doesn't exist, it will be added to the map.

      # @param {String} value String to look up.
      # @return {Number}      Index within the string map where this string is located.
      _lookupString: (value) ->
        if !@stringMap[value]
          @stringMap[value] = @stringIndex
          @strings.push(value)
          @stringIndex += 1
        return @stringMap[value]

Create a relationship. For now, this is always a hyperlink. This writes to a array that will later be used define the rels.

      _createRelationship: (cell, target) ->
        @relationships.push({cell: cell, target: target})

Generate external relationships data. This is saved into "xl/worksheets/_rels/sheet1.xml.rels".

      _generateRelationships: () ->
        @relsData = blobs.externalWorksheetRels(@relationships)

Converts a Date to an OADate.
See [this stackoverflow post](http://stackoverflow.com/a/15550284/2644351)

      # @param {Date} date Date to convert.
      # @return {Number}   OADate.
      _dateToOADate: (date) ->
        epoch = new Date(1899,11,30)
        msPerDay = 8.64e7

        v = -1 * (epoch - date) / msPerDay;

        # Deal with dates prior to 1899-12-30 00:00:00
        dec = v - Math.floor(v)

        if v < 0 and dec
          v = Math.floor(v) - dec

        return v

Convert an OADate to a Date.

      # @param {Number} oaDate OADate.
      # @return {Date}         Converted date.
      _OADateToDate: (oaDate) ->
        epoch = new Date(1899,11,30)
        msPerDay = 8.64e7

        # Deal with -ve values
        dec = oaDate - Math.floor(oaDate)

        if oaDate < 0 and dec
          oaDate = Math.floor(oaDate) - dec

        return new Date(oaDate * msPerDay + +epoch)

Resets sheet data. Called on initialization.

      _resetSheet: () ->

        # Sheet data storage.
        @sheetData = ''
        @strings = []
        @stringMap = {}
        @stringIndex = 0
        @stringData = null
        @currentRow = 0

        # Cell data storage
        @cellMap = []
        @cellLabelMap = {}

        # Column data storage
        @columns = []

        # Rels data storage
        @relData = ''
        @relationships = []

        # Flags
        @haveHeader = false
        @finalized = false

        # Create sheet stream.
        PassThrough = require('stream').PassThrough
        @sheetStream = new PassThrough()

        # Start off the sheet.
        @_write(blobs.sheetHeader)

Wrapper around writing sheet data.

      # @param {String} data Data to write to the sheet.
      _write: (data) ->
        @sheetStream.write(data)

Utility method for escaping XML - used within blobs and can be used manually.

      # @param {String} str String to escape.
      escapeXml: (str = '') ->
        return str.replace(/&/g, '&')
          .replace(/</g, '<')
          .replace(/>/g, '>')

Simple extend helper.

    _extend = (dest, src) ->
      for key, val of src
        dest[key] = val
      dest

Node-XLSX-Writer
================

Node-XLSX-Writer is written in literate coffeescript. The following is the actual source of the
module.

    fs = require('fs')
    blobs = require('./blobs')
    Archiver = require('archiver')
    numberRegex = /^[1-9\.][\d\.]+$/

    module.exports = class XlsxWriter

### Simple writes

##### XlsxWriter.write(out: String, data: Array, cb: Function)

The simplest way to use Node-XLSX-Writer is to use the write method.

The callback comes directly from `fs.writeFile` and has the arity `(err)`

      # @param {String} out Output file path.
      # @param {Array} data Data to write.
      # @param {Function} cb Callback to call when done. Fed (err).
      @write = (out, data, cb) ->
          writer = new XlsxWriter({out: out})
          writer.addRows(data)
          writer.writeToFile(cb)

### Advanced usage

Node-XLSX-Writer has more advanced features available for better customization
of spreadsheets.

When constructing a writer, pass it an optional file path and customization options.

##### new XlsxWriter([options]: Object) : XlsxWriter

      # Build a writer object.
      # @param {Object} [options] Preparation options.
      # @param {String} [options.out] Output file path.
      # @param {Array}  [options.columns] Column definition. Must be added in constructor.
      # @example options.columns = [
      #     {  width: 30 }, // width is in 'characters'
      #     {  width: 10 }
      # ]
      constructor: (options = {}) ->

        # Support just passing a string path into the constructor.
        if (typeof options == 'string')
          options = {out: options}

        # Set options.
        defaults = {
            defaultWidth: 15
            zip: {
              forceUTC: true # this is required, zips will be unreadable without it
            },
            columns: []
        }
        @options = _extend(defaults, options)

        # Start sheet.
        @_resetSheet()

        # Write column definition.
        @defineColumns(@options.columns)

        # Create Zip.
        zipOptions = @options.zip || {}
        zipOptions.forceUTC = true # force this on in all cases for now, otherwise we're useless
        @zip = Archiver('zip', zipOptions)

        # Archiver attaches an exit listener on the process, we don't want this,
        # it will fire if this object is never finalized.
        @zip.catchEarlyExitAttached = true

        # Hook this passthrough into the zip stream.
        @zip.append(@sheetStream, {name: 'xl/worksheets/sheet1.xml'})

#### Adding rows

Rows are easy to add one by one or all at once. Data types within the sheet will
be inferred from the data types passed to addRow().

##### addRow(row: Object)

Add a single row.

      # @example (javascript)
      # writer.addRow({
      #     "A String Column" : "A String Value",
      #     "A Number Column" : 12345,
      #     "A Date Column" : new Date(1999,11,31)
      # })
      addRow: (row) ->

        # Values in header are defined by the keys of the object we've passed in.
        # They need to be written the first time they're passed in.
        if !@haveHeader
          @_write(blobs.sheetDataHeader)
          @_startRow()
          col = 1
          for key of row
            @_addCell(key, col)
            @cellMap.push(key)
            col += 1
          @_endRow()

          @haveHeader = true

        @_startRow()
        for key, col in @cellMap
          @_addCell(row[key] || "", col + 1)
        @_endRow()

##### addRows(rows: Array)

Rows can be added in batch.

      addRows: (rows) ->
        for row in rows
          @addRow(row)

##### defineColumns(columns: Array)

Column definitions can be easily added, but it *must* be done before rows are added
to prevent a nasty Excel bug.

      # @example (javascript)
      # writer.defineColumns([
      #     {  width: 30 }, // width is in 'characters'
      #     {  width: 10 }
      # ])
      defineColumns: (columns) ->
        if @haveHeader
          throw new Error """
            Columns cannot be added after rows! Unfortunately Excel will crash
            if column definitions come after sheet data. Please move your `defineColumns()`
            call before any `addRow()` calls, or define options.columns in the XlsxWriter
            constructor.
          """
        @options.columns = columns
        # Write column metadata.
        # Would really like to do this at the end so that we don't have to mandate
        # it comes first, but Excel pukes if <cols> comes before <sheetData>.
        @_write(@_generateColumnDefinition())

#### File generation

Once you are done adding rows & defining columns, you have a few options
for generating the file. The `writeToFile` helper is a one-stop-shop for writing
directly to a file using `fs.writeFile`; otherwise, you can pack() manually,
which will return a readable stream.

##### writeToFile([fileName]: String, cb: Function)

Writes data to a file. Convenience method.

If no filename is specified, will attempt to use the one specified in the
constructor as `options.out`.

The callback is fed directly to `fs.writeFile`.

      # @param {String} [fileName] File path to write.
      # @param {Function} cb Callback.
      writeToFile: (fileName, cb) ->
        if fileName instanceof Function
          cb = fileName
          fileName = @options.out
        if !fileName
          throw new Error("Filename required. Supply one in writeToFile() or in options.out.")

        # Create zip, pipe it into a file writeStream.
        zip = @createReadStream(fileName)
        fileStream = fs.createWriteStream(fileName);
        fileStream.once 'finish', cb
        zip.pipe(fileStream)

##### createReadStream([jsZipOptions]: Object) : Stream

Packs the file and returns a readable stream. You can pipe this directly to a file
or response object. Be sure to use 'binary' mode.

Will finalize the sheet & generate shared strings if they haven't been already.

      # @return {Stream} Readable stream with ZIP data.
      createReadStream: (options) ->

        # Finalize sheet if it hasn't been already.
        if (!@finalized)
          @finalize()

        @zip
          .append(blobs.contentTypes, {name: '[Content_Types].xml'})
          .append(blobs.rels, {name: '_rels/.rels'})
          .append(blobs.workbook, {name: 'xl/workbook.xml'})
          .append(blobs.styles, {name: 'xl/styles.xml'})
          .append(blobs.workbookRels, {name: 'xl/_rels/workbook.xml.rels'})
          .append(@relsData, {name: 'xl/worksheets/_rels/sheet1.xml.rels'})
          .append(@stringsData, {name: 'xl/sharedStrings.xml'})
          .finalize()

        return @zip

##### finalize()

Finishes up the sheet & generate shared strings.

      finalize: () ->

        # If there was data, end sheetData
        if @haveHeader
          @_write(blobs.sheetDataFooter)

        # Write relationships data.
        @_write(blobs.worksheetRels(@relationships))

        # End sheet
        @sheetStream.end(blobs.sheetFooter);

        # Generate shared strings
        @_generateStrings()

        # Generate external rels
        @_generateRelationships()

        # Mark this as finished
        @finalized = true

##### dispose()

Cancel use of this writer and close all streams. This is not needed if you've written to a file.

      dispose: () ->
        return if @disposed

        @sheetStream.end()
        @sheetStream.unpipe()
        @zip.unpipe()
        while(@zip.read()) # drain stream
          1; # noop
        delete @zip
        delete @sheetStream
        @disposed = true

#### Internal methods

Adds a cell to the row in progress.

      # @param {String|Number|Date} value Value to write.
      # @param {Number}             col   Column index.
      _addCell: (value = '', col) ->
        row = @currentRow
        cell = @_getCellIdentifier(row, col)

        # Hyperlink support
        if Object.prototype.toString.call(value) == '[object Object]' && value.hyperlink && value.value
          @_addCell(value.value, col)
          @_createRelationship(cell, value.hyperlink)
          return

        if typeof value == 'number' or numberRegex.test(value)
          @rowBuffer += blobs.numberCell(value, cell)
        else if value instanceof Date
          date = @_dateToOADate(value)
          @rowBuffer += blobs.dateCell(date, cell)
        else
          index = @_lookupString(value)
          @rowBuffer += blobs.cell(index, cell)

Begins a row. Call this before starting any row. Will start a buffer
for all proceeding cells, until @_endRow is called.

      _startRow: () ->
        @rowBuffer = blobs.startRow(@currentRow)
        @currentRow += 1

Ends a row. Will write the row to the sheet.

      _endRow: () ->
        @_write(@rowBuffer + blobs.endRow)

Given row and column indices, returns a cell identifier, e.g. "E20"

      # @param {Number} row  Row index.
      # @param {Number} cell Cell index.
      # @return {String}     Cell identifier.
      _getCellIdentifier: (row, col) ->
        colIndex = ''
        if @cellLabelMap[col]
          colIndex = @cellLabelMap[col]
        else
          if col == 0
            # Provide a fallback for empty spreadsheets
            row = 1
            col = 1

          input = (+col - 1).toString(26)
          while input.length
            a = input.charCodeAt(input.length - 1)
            colIndex = String.fromCharCode(a + if a >= 48 and a <= 57 then 17 else -22) + colIndex
            input = if input.length > 1 then (parseInt(input.substr(0, input.length - 1), 26) - 1).toString(26) else ""
          @cellLabelMap[col] = colIndex

        return colIndex + row

Creates column definitions, if any definitions exist.
This will write column styles, widths, etc.

      # @return {String} Column definition.
      _generateColumnDefinition: () ->
        # <cols/> tag (empty) crashes excel, weeeeee
        if !@options.columns || !@options.columns.length
          return ''

        columnDefinition = ''
        columnDefinition += blobs.startColumns

        idx = 1
        for index, column of @options.columns
          columnDefinition += blobs.column(column.width || @options.defaultWidth, idx)
          idx += 1

        columnDefinition += blobs.endColumns
        return columnDefinition

Generates StringMap XML. Used as a finalization step - don't call this while
building the xlsx is in progress.

Saves string data to this object so it can be written to the zip.

      _generateStrings: () ->
        stringTable = ''
        for string in @strings
          stringTable += blobs.string(@escapeXml(string))
        @stringsData = blobs.stringsHeader(@strings.length) + stringTable + blobs.stringsFooter

Looks up a string inside the internal string map. If it doesn't exist, it will be added to the map.

      # @param {String} value String to look up.
      # @return {Number}      Index within the string map where this string is located.
      _lookupString: (value) ->
        if !@stringMap[value]
          @stringMap[value] = @stringIndex
          @strings.push(value)
          @stringIndex += 1
        return @stringMap[value]

Create a relationship. For now, this is always a hyperlink. This writes to a array that will later be used define the rels.

      _createRelationship: (cell, target) ->
        @relationships.push({cell: cell, target: target})

Generate external relationships data. This is saved into "xl/worksheets/_rels/sheet1.xml.rels".

      _generateRelationships: () ->
        @relsData = blobs.externalWorksheetRels(@relationships)

Converts a Date to an OADate.
See [this stackoverflow post](http://stackoverflow.com/a/15550284/2644351)

      # @param {Date} date Date to convert.
      # @return {Number}   OADate.
      _dateToOADate: (date) ->
        epoch = new Date(1899,11,30)
        msPerDay = 8.64e7

        v = -1 * (epoch - date) / msPerDay;

        # Deal with dates prior to 1899-12-30 00:00:00
        dec = v - Math.floor(v)

        if v < 0 and dec
          v = Math.floor(v) - dec

        return v

Convert an OADate to a Date.

      # @param {Number} oaDate OADate.
      # @return {Date}         Converted date.
      _OADateToDate: (oaDate) ->
        epoch = new Date(1899,11,30)
        msPerDay = 8.64e7

        # Deal with -ve values
        dec = oaDate - Math.floor(oaDate)

        if oaDate < 0 and dec
          oaDate = Math.floor(oaDate) - dec

        return new Date(oaDate * msPerDay + +epoch)

Resets sheet data. Called on initialization.

      _resetSheet: () ->

        # Sheet data storage.
        @sheetData = ''
        @strings = []
        @stringMap = {}
        @stringIndex = 0
        @stringData = null
        @currentRow = 0

        # Cell data storage
        @cellMap = []
        @cellLabelMap = {}

        # Column data storage
        @columns = []

        # Rels data storage
        @relData = ''
        @relationships = []

        # Flags
        @haveHeader = false
        @finalized = false

        # Create sheet stream.
        PassThrough = require('stream').PassThrough
        @sheetStream = new PassThrough()

        # Start off the sheet.
        @_write(blobs.sheetHeader)

Wrapper around writing sheet data.

      # @param {String} data Data to write to the sheet.
      _write: (data) ->
        @sheetStream.write(data)

Utility method for escaping XML - used within blobs and can be used manually.

      # @param {String} str String to escape.
      escapeXml: (str = '') ->
        return str.replace(/&/g, '&')
          .replace(/</g, '<')
          .replace(/>/g, '>')

Simple extend helper.

    _extend = (dest, src) ->
      for key, val of src
        dest[key] = val
      dest


Moved initialization of the input system to the index file.

# index

This module is used to run the main event loop.  Please see the "game" module
for details.

    # Required for Webpack to include the stylesheet in the HTML.
    require "./index.sass"

    addEventListener "load", ->
        (require "./input/keyboard.litcoffee")()

        game = require "./game.litcoffee"

        game.load ->
            lastTimestamp = undefined
            tickProgress = 0

            # Ensures the game has ticked once before the first draw.
            game.tick()

            run = (timestamp) ->
                if lastTimestamp isnt undefined
                    tickProgress += (timestamp - lastTimestamp) * 20 / 1000

                lastTimestamp = timestamp

                # If more than a quarter second of ticks have occurred, don't
                # process any more than that, as the game was likely suspended.
                tickProgress = Math.min 5, tickProgress

                while tickProgress >= 1
                    game.tick()
                    tickProgress--

                game.draw tickProgress

                requestAnimationFrame run

                return

            requestAnimationFrame run

            return

        return

# index

This module is used to run the main event loop.  Please see the "game" module
for details.

    # Required for Webpack to include the stylesheet in the HTML.
	require "./index.sass"

	addEventListener "load", ->
		game = require "./game.litcoffee"

		game.load ->
            lastTimestamp = undefined
            tickProgress = 0

            # Ensures the game has ticked once before the first draw.
            game.tick()

            run = (timestamp) ->
                if lastTimestamp isnt undefined
                    tickProgress += (timestamp - lastTimestamp) * 20 / 1000

                lastTimestamp = timestamp

                # If more than a quarter second of ticks have occurred, don't
                # process any more than that, as the game was likely suspended.
                tickProgress = Math.min 5, tickProgress

                while tickProgress >= 1
                    game.tick()
                    tickProgress--

                game.draw tickProgress

                requestAnimationFrame run

                return

            requestAnimationFrame run

            return

        return