user.save() doesn't return a user object now? Oh well.

# User

*User operations.*

    passport = null
    Promise = null

    {Middleware} = require 'middleware'

    {monkeyPatchLogin} = require './login'

    orm = null

    clientModule = require './client'

    exports.pkgmanRegister = (registrar) ->

#### Implements hook `shrubCorePreBootstrap`.

      registrar.registerHook 'shrubCorePreBootstrap', ->

        passport = require 'passport'
        Promise = require 'bluebird'

        orm = require 'shrub-orm'

#### Implements hook `shrubConfigClient`.

      registrar.registerHook 'shrubConfigClient', (req) ->

Send a redacted version of the request user.

        req.user.redactFor req.user if req.user?

#### Implements hook `shrubAuditFingerprint`.

      registrar.registerHook 'shrubAuditFingerprint', (req) ->

User (ID).

        user: if req?.user?.id? then req.user.id

#### Implements hook `shrubHttpMiddleware`.

      registrar.registerHook 'shrubHttpMiddleware', ->

        label: 'Load user using passport'
        middleware: userMiddleware()

#### Implements hook `shrubOrmCollections`.

      registrar.registerHook 'shrubOrmCollections', ->

        crypto = require 'server/crypto'

        autoIname = (values, cb) ->
          values.iname = values.name.toLowerCase()
          cb()

Invoke the client hook implementation.

        collections = clientModule.shrubOrmCollections()

        {
          'shrub-group': Group
          'shrub-group-permission': GroupPermission
          'shrub-user': User
          'shrub-user-group': UserGroup
          'shrub-user-permission': UserPermission
        } = collections

###### TODO: Finish collections doc.

Case-insensitivized name.

        Group.attributes.iname =
          type: 'string'
          size: 24
          index: true

        Group.autoCreatedAt = false
        Group.autoUpdatedAt = false

        Group.beforeCreate = autoIname
        Group.beforeUpdate = autoIname

        GroupPermission.autoCreatedAt = false
        GroupPermission.autoUpdatedAt = false

        User.beforeCreate = autoIname
        User.beforeUpdate = autoIname

Case-insensitivized name.

        User.attributes.iname =
          type: 'string'
          size: 24
          index: true

Hash of the plaintext password.

        User.attributes.passwordHash =
          type: 'string'

A token which can be used to reset the user's password (once).

        User.attributes.resetPasswordToken =
          type: 'string'
          size: 48
          index: true

A 512-bit salt used to cryptographically hash the user's password.

        User.attributes.salt =
          type: 'string'
          size: 128

Update a user's last accessed time. Return the user for chaining.

        User.attributes.touch = ->
          @lastAccessed = (new Date()).toISOString()
          this

        User.attributes.populateAll = ->
          self = this

          Group_ = orm.collection 'shrub-group'

          @permissions = @permissions.map ({permission}) -> permission

          promises = for {group}, index in @groups
            do (group, index) ->
              Group_.findOne(id: group).populateAll().then (group_) ->
                self.groups[index] = group_

          Promise.all(promises).then -> self

        User.attributes.toJSON = ->
          O = @toObject()
          O.groups = @groups
          O.permissions = @permission
          O

## User#register

* (string) `name` - Name of the new user.
* (string) `email` - Email address of the new user.
* (string) `password` - The new user's password.

*Register a user in the system.*

        User.register = (name, email, password) ->

          @create(name: name).then((user) ->

Encrypt the email.

            crypto.encrypt(email.toLowerCase()).then((encryptedEmail) ->

              user.email = encryptedEmail

Set the password encryption details.

              crypto.hasher plaintext: password

            ).then((hashed) ->

              user.plaintext = hashed.plaintext
              user.salt = hashed.salt.toString 'hex'
              user.passwordHash = hashed.key.toString 'hex'

Generate a one-time login token.

              crypto.randomBytes 24

            ).then((token) ->

              user.resetPasswordToken = token.toString 'hex'
              user.save()

            ).then -> return user
          )

        User.redactors = [(redactFor) ->
          self = this

          delete self.iname
          delete self.plaintext if self.plaintext?
          delete self.salt
          delete self.passwordHash
          delete self.resetPasswordToken

          for group in self.groups

            for permission in group.permissions ? []

              delete permission.group
              delete permission.id

            delete group.iname
            delete group.id

          Promise.resolve().then ->
            return unless self.email?

Different redacted means full email redaction.

            if redactFor.id isnt self.id
              delete self.email
              return

Decrypt the e-mail if redacting for the same user.

            crypto.decrypt(self.email).then (email) ->
              self.email = email

        ]

        UserGroup.autoCreatedAt = false
        UserGroup.autoUpdatedAt = false

        UserGroup.attributes.populateAll = ->
          self = this

          Group_ = orm.collection 'shrub-group'
          Group_.findOne(id: self.group).populateAll().then (group_) ->
            self.group = group_

            return self

        UserGroup.attributes.depopulateAll = ->
          @group = @group.id

          return this

        UserPermission.autoCreatedAt = false
        UserPermission.autoUpdatedAt = false

        collections

#### Implements hook `shrubOrmCollectionsAlter`.

      registrar.registerHook 'shrubOrmCollectionsAlter', (collections) ->
        clientModule.shrubOrmCollectionsAlter collections

        for identity, collection of collections
          do (identity, collection) ->

            collection.redactors ?= []
            collection.attributes.redactFor = (user) ->
              redacted = @toJSON()
              redacted.toJSON = undefined

              Promise.all(
                for redactor in collection.redactors
                  redactor.call redacted, user
              ).then -> redacted

#### Implements hook `shrubConfigServer`.

      registrar.registerHook 'shrubConfigServer', ->

        beforeLoginMiddleware: []

        afterLoginMiddleware: []

        beforeLogoutMiddleware: [
          'shrub-user'
        ]

        afterLogoutMiddleware: [
          'shrub-user'
        ]

#### Implements hook `shrubRpcRoutesAlter`.

      registrar.registerHook 'shrubRpcRoutesAlter', (routes) ->

        {spliceRouteMiddleware} = require 'shrub-rpc'

        loadUserMiddleware = (req, res, next) ->

          req.loadUser = (done) -> req.loadSession ->

            userMiddleware_ = new Middleware()
            for fn in userMiddleware()
              userMiddleware_.use fn

            userMiddleware_.dispatch req, res, (error) ->
              return next error if error?
              done()

          next()

        loadUserMiddleware.weight = -4999

        for path, route of routes
          route.middleware.unshift loadUserMiddleware
          spliceRouteMiddleware route, 'shrub-user', userMiddleware()

        return

#### Implements hook `shrubSocketConnectionMiddleware`.

      registrar.registerHook 'shrubSocketConnectionMiddleware', ->

        label: 'Load user using passport'

Join a channel for the username.

        middleware: userMiddleware().concat (req, res, next) ->

          return req.socket.join req.user.name, next if req.user.id?

          next()

#### Implements hook `shrubUserBeforeLogoutMiddleware`.

      registrar.registerHook 'shrubUserBeforeLogoutMiddleware', ->

        label: 'Tell client to log out, and leave the user channel'
        middleware: [

          (req, next) ->

            return next() unless req.socket?

Tell client to log out.

            req.socket.emit 'shrub-user/logout'

Leave the user channel.

            if req.user.id?
              req.socket.leave req.user.name, next
            else
              next()

        ]

#### Implements hook `shrubUserAfterLogoutMiddleware`.

      registrar.registerHook 'shrubUserAfterLogoutMiddleware', ->

        label: 'Instantiate anonymous user'
        middleware: [

          (req, next) ->
            req.instantiateAnonymous().then(-> next()).catch next

        ]

      registrar.recur [
        'forgot', 'login', 'logout', 'register', 'reset'
      ]

## loadByName

(string) `name` - The name of the user to load.

*Load a user by name.*

    exports.loadByName = (name) ->

      User = orm.collection 'shrub-user'
      User.findOne(iname: name.toLowerCase()).populateAll()

    userMiddleware = -> [

      (req, res, next) ->

        req.instantiateAnonymous = ->

          @user = orm.collection('shrub-user').instantiate()

Add to anonymous group.

          @user.groups = [
            orm.collection('shrub-user-group').instantiate group: 2
          ]

          @user.populateAll()

        next()

Passport middleware.

      passport.initialize()
      passport.session()

Monkey patch.

      monkeyPatchLogin

Set the user into the request.

      (req, res, next) ->
        promise = if req.user?
          Promise.resolve()
        else
          req.instantiateAnonymous()

        promise.then(-> next()).catch next

Save the user at the end of the request.

      (req, res, next) ->

        end = res.end
        res.end = (data, encoding) ->
          res.end = end

          return res.end data, encoding unless req.user.id

          req.user.save().finally -> res.end data, encoding

        next()

    ]

# User

*User operations.*

    passport = null
    Promise = null

    {Middleware} = require 'middleware'

    {monkeyPatchLogin} = require './login'

    orm = null

    clientModule = require './client'

    exports.pkgmanRegister = (registrar) ->

#### Implements hook `shrubCorePreBootstrap`.

      registrar.registerHook 'shrubCorePreBootstrap', ->

        passport = require 'passport'
        Promise = require 'bluebird'

        orm = require 'shrub-orm'

#### Implements hook `shrubConfigClient`.

      registrar.registerHook 'shrubConfigClient', (req) ->

Send a redacted version of the request user.

        req.user.redactFor req.user if req.user?

#### Implements hook `shrubAuditFingerprint`.

      registrar.registerHook 'shrubAuditFingerprint', (req) ->

User (ID).

        user: if req?.user?.id? then req.user.id

#### Implements hook `shrubHttpMiddleware`.

      registrar.registerHook 'shrubHttpMiddleware', ->

        label: 'Load user using passport'
        middleware: userMiddleware()

#### Implements hook `shrubOrmCollections`.

      registrar.registerHook 'shrubOrmCollections', ->

        crypto = require 'server/crypto'

        autoIname = (values, cb) ->
          values.iname = values.name.toLowerCase()
          cb()

Invoke the client hook implementation.

        collections = clientModule.shrubOrmCollections()

        {
          'shrub-group': Group
          'shrub-group-permission': GroupPermission
          'shrub-user': User
          'shrub-user-group': UserGroup
          'shrub-user-permission': UserPermission
        } = collections

###### TODO: Finish collections doc.

Case-insensitivized name.

        Group.attributes.iname =
          type: 'string'
          size: 24
          index: true

        Group.autoCreatedAt = false
        Group.autoUpdatedAt = false

        Group.beforeCreate = autoIname
        Group.beforeUpdate = autoIname

        GroupPermission.autoCreatedAt = false
        GroupPermission.autoUpdatedAt = false

        User.beforeCreate = autoIname
        User.beforeUpdate = autoIname

Case-insensitivized name.

        User.attributes.iname =
          type: 'string'
          size: 24
          index: true

Hash of the plaintext password.

        User.attributes.passwordHash =
          type: 'string'

A token which can be used to reset the user's password (once).

        User.attributes.resetPasswordToken =
          type: 'string'
          size: 48
          index: true

A 512-bit salt used to cryptographically hash the user's password.

        User.attributes.salt =
          type: 'string'
          size: 128

Update a user's last accessed time. Return the user for chaining.

        User.attributes.touch = ->
          @lastAccessed = (new Date()).toISOString()
          this

        User.attributes.populateAll = ->
          self = this

          Group_ = orm.collection 'shrub-group'

          @permissions = @permissions.map ({permission}) -> permission

          promises = for {group}, index in @groups
            do (group, index) ->
              Group_.findOne(id: group).populateAll().then (group_) ->
                self.groups[index] = group_

          Promise.all(promises).then -> self

        User.attributes.toJSON = ->
          O = @toObject()
          O.groups = @groups
          O.permissions = @permission
          O

## User#register

* (string) `name` - Name of the new user.
* (string) `email` - Email address of the new user.
* (string) `password` - The new user's password.

*Register a user in the system.*

        User.register = (name, email, password) ->

          @create(name: name).then((user) ->

Encrypt the email.

            crypto.encrypt(email.toLowerCase()).then((encryptedEmail) ->

              user.email = encryptedEmail

Set the password encryption details.

              crypto.hasher plaintext: password

            ).then((hashed) ->

              user.plaintext = hashed.plaintext
              user.salt = hashed.salt.toString 'hex'
              user.passwordHash = hashed.key.toString 'hex'

Generate a one-time login token.

              crypto.randomBytes 24

            ).then (token) ->

              user.resetPasswordToken = token.toString 'hex'

              user.save()
          )

        User.redactors = [(redactFor) ->
          self = this

          delete self.iname
          delete self.plaintext if self.plaintext?
          delete self.salt
          delete self.passwordHash
          delete self.resetPasswordToken

          for group in self.groups

            for permission in group.permissions ? []

              delete permission.group
              delete permission.id

            delete group.iname
            delete group.id

          Promise.resolve().then ->
            return unless self.email?

Different redacted means full email redaction.

            if redactFor.id isnt self.id
              delete self.email
              return

Decrypt the e-mail if redacting for the same user.

            crypto.decrypt(self.email).then (email) ->
              self.email = email

        ]

        UserGroup.autoCreatedAt = false
        UserGroup.autoUpdatedAt = false

        UserGroup.attributes.populateAll = ->
          self = this

          Group_ = orm.collection 'shrub-group'
          Group_.findOne(id: self.group).populateAll().then (group_) ->
            self.group = group_

            return self

        UserGroup.attributes.depopulateAll = ->
          @group = @group.id

          return this

        UserPermission.autoCreatedAt = false
        UserPermission.autoUpdatedAt = false

        collections

#### Implements hook `shrubOrmCollectionsAlter`.

      registrar.registerHook 'shrubOrmCollectionsAlter', (collections) ->
        clientModule.shrubOrmCollectionsAlter collections

        for identity, collection of collections
          do (identity, collection) ->

            collection.redactors ?= []
            collection.attributes.redactFor = (user) ->
              redacted = @toJSON()
              redacted.toJSON = undefined

              Promise.all(
                for redactor in collection.redactors
                  redactor.call redacted, user
              ).then -> redacted

#### Implements hook `shrubConfigServer`.

      registrar.registerHook 'shrubConfigServer', ->

        beforeLoginMiddleware: []

        afterLoginMiddleware: []

        beforeLogoutMiddleware: [
          'shrub-user'
        ]

        afterLogoutMiddleware: [
          'shrub-user'
        ]

#### Implements hook `shrubRpcRoutesAlter`.

      registrar.registerHook 'shrubRpcRoutesAlter', (routes) ->

        {spliceRouteMiddleware} = require 'shrub-rpc'

        loadUserMiddleware = (req, res, next) ->

          req.loadUser = (done) -> req.loadSession ->

            userMiddleware_ = new Middleware()
            for fn in userMiddleware()
              userMiddleware_.use fn

            userMiddleware_.dispatch req, res, (error) ->
              return next error if error?
              done()

          next()

        loadUserMiddleware.weight = -4999

        for path, route of routes
          route.middleware.unshift loadUserMiddleware
          spliceRouteMiddleware route, 'shrub-user', userMiddleware()

        return

#### Implements hook `shrubSocketConnectionMiddleware`.

      registrar.registerHook 'shrubSocketConnectionMiddleware', ->

        label: 'Load user using passport'

Join a channel for the username.

        middleware: userMiddleware().concat (req, res, next) ->

          return req.socket.join req.user.name, next if req.user.id?

          next()

#### Implements hook `shrubUserBeforeLogoutMiddleware`.

      registrar.registerHook 'shrubUserBeforeLogoutMiddleware', ->

        label: 'Tell client to log out, and leave the user channel'
        middleware: [

          (req, next) ->

            return next() unless req.socket?

Tell client to log out.

            req.socket.emit 'shrub-user/logout'

Leave the user channel.

            if req.user.id?
              req.socket.leave req.user.name, next
            else
              next()

        ]

#### Implements hook `shrubUserAfterLogoutMiddleware`.

      registrar.registerHook 'shrubUserAfterLogoutMiddleware', ->

        label: 'Instantiate anonymous user'
        middleware: [

          (req, next) ->
            req.instantiateAnonymous().then(-> next()).catch next

        ]

      registrar.recur [
        'forgot', 'login', 'logout', 'register', 'reset'
      ]

## loadByName

(string) `name` - The name of the user to load.

*Load a user by name.*

    exports.loadByName = (name) ->

      User = orm.collection 'shrub-user'
      User.findOne(iname: name.toLowerCase()).populateAll()

    userMiddleware = -> [

      (req, res, next) ->

        req.instantiateAnonymous = ->

          @user = orm.collection('shrub-user').instantiate()

Add to anonymous group.

          @user.groups = [
            orm.collection('shrub-user-group').instantiate group: 2
          ]

          @user.populateAll()

        next()

Passport middleware.

      passport.initialize()
      passport.session()

Monkey patch.

      monkeyPatchLogin

Set the user into the request.

      (req, res, next) ->
        promise = if req.user?
          Promise.resolve()
        else
          req.instantiateAnonymous()

        promise.then(-> next()).catch next

Save the user at the end of the request.

      (req, res, next) ->

        end = res.end
        res.end = (data, encoding) ->
          res.end = end

          return res.end data, encoding unless req.user.id

          req.user.save().finally -> res.end data, encoding

        next()

    ]


remove router underscore dependency

remove router underscore dependency

#Weya.Router.litcoffee

    @Weya = {} unless @Weya?
    Weya = @Weya

    if exports?
     Weya = require './weya'
     Weya.Base = require './weya_base'

    if not Weya.Base?
     throw new Error 'Weya.Base not found'

##Weya.Router

Routing with hash tags or pushState

    class Router extends Weya.Base
     optionalParam: /\((.*?)\)/g
     namedParam: /(\(\?)?:\w+/g
     splatParam: /\*\w+/g
     escapeRegExp: /[\-{}\[\]+?.,\\\^$|#\s]/g

     @extend()

     @initialize ->
      @_bindRoutes()
      @_event = null
      @_history = []

     _routes: {}

####Register router

`'analyse/:analysis': 'onAnalyse'`

`dashboard': ['auth', 'dashboard']`

This will match urls of the form `analyse/\*` and call method @onAnalyse
with the parameter. Second route will first call @auth if it returns
`true` @dashboard will be called.

The most specific route should be at bottom. Routers can added or overridden
in subclasses

     @routes: (routes) ->
      temp = @::_routes
      @::_routes = {}
      @::_routes[k] = v for k, v of temp
      @::_routes[k] = v for k, v of routes

####Starts routing
Option silent will not trigger an event for the current url

     start: (options) ->
      Weya.history.start options
      fragment = Weya.history.getFragment()

      if options?.silent is on
       @_history.push fragment: fragment, title: document.title

####Goes to previous page if exists

     back: ->
      if @_history.length > 1
       Weya.history.back()

####Whether it is possible to go to previous page

     canBack: ->
      if @_history.length > 1 and Weya.history.canBack()
       return true
      else
       return false

####Registers a route

     route: (route, name) ->
      if (Object.prototype.toString.call route) isnt '[object RegExp]'
       route = @_routeToRegExp route

Registers the route with Weya.history

      Weya.history.route route, (fragment, event) =>
       args = @_extractParameters route, fragment
       @_event = event

Handle state

       if @_event?.type is "popstate"
        @_history.pop()
        if @_history.length is 0
         @_history.push
          fragment: fragment
          title: document.title
          state: @getState()
       else
        @_history.push
         fragment: fragment
         title: document.title
         state: @getState()

Calls callbacks in order

       callbacks = name
       callbacks = [callbacks] if not Array.isArray callbacks

       for callback in callbacks
        if (typeof callback) is 'string'
         callback = @[callback]
        break unless callback.apply this, args

####Gets the current HTML5 History state

     getState: ->
      if @_event?.originalEvent?.state?
       return @_event.originalEvent.state
      else
       return null

####Navigate to a new URL, while setting HTML5 History state

     navigate: (fragment, options) ->
      options = {} unless options
      if options.replace
       @_history.pop()
      if not options.trigger
       @_history.push
        fragment: fragment
        title: options.title
        state: options.state

      Weya.history.navigate fragment, options

Bind routes

     _bindRoutes: ->
      for route, name of @_routes
       @route route, name

Convert a route string into a regular expression, suitable for matching

     _routeToRegExp: (route) ->
      route = route.replace @escapeRegExp, '\\$&'
                   .replace @noptionalParam, '(?:$1)?'
                   .replace @namedParam, (match, optional) ->
                    if optional then match else '([^\/]+)'
                   .replace @splatParam, '(.*?)'

       return new RegExp "^#{route}$"

Extract parameters from a route regex and URL

     _extractParameters: (route, fragment) ->
      params = route.exec(fragment).slice(1)
      for p, i in params when p?
       params[i] = decodeURIComponent p

## History class

    class History extends Weya.Base
     @extend()

Strip leading hash/slash and trailing space

     routeStripper: /^[#\/]|\s+$/g

Strip leading and trailing slashes

     rootStripper: /^\/+|\/+$/g

Remove trailing slash

     trailingSlash: /\/$/

Strip urls of hash and query

     pathStripper: /[?#].*$/

     @initialize ->
      @handlers = []
      @checkUrl = @checkUrl.bind this
      @history = window.history
      @location = window.location
      @stateList = []

Hash check interval

     interval: 50

Get hash value

     getHash: ->
      match = @location.href.match /#(.*)$/
      return (if match? then match[1] else '')

Get emulated state

     getEmulateState: ->
      if @stateList.length > 0
       return @stateList[@stateList.length - 1]
      else
       return {fragment: ""}

     popEmulateState: -> @stateList.pop()

     pushEmulateState: (state, title, fragment) ->
      @stateList.push
       state: state
       title: title
       fragment: fragment

Get the URL fragment

     getFragment: (fragment, forcePushState) ->
      if not fragment?
       if @_emulateState
        fragment = @getEmulateState().fragment
       else if @_hasPushState or not @_wantsHashChange or forcePushState
        fragment = @location.pathname
        root = @root.replace @trailingSlash, ''
        if (fragment.indexOf root) is 0
         fragment = fragment.slice root.length
       else
        fragment = @getHash()

      return fragment.replace @routeStripper, ''

####Goto the previous page

     back: ->
      if @_emulateState is on
       @popEmulateState()
       @loadUrl null, null
      else
       @history?.back?()

####Can back?

     canBack: ->
      if @_emulateState is on
       return @stateList.length > 1
      else
       return @history?.back?

####Start listening to events

     start: (options) ->
      History.started = true

      opt = {root: '/'}
      opt[k] = v for k, v of @options
      opt[k] = v for k, v of options
      @options = opt
      @root = @options.root
      @_emulateState = @options.emulateState is on
      @_wantsHashChange = @_emulateState is off and @options.hashChange isnt off
      @_wantsPushState = @_emulateState is off and @options.pushState is on
      @_hasPushState = @_wantsPushState is on and @history?.pushState?
      if @_emulateState and @options.start?
       @pushEmulateState @options.start.state,
        @options.start.title
        @options.start.fragment

      @fragment = @getFragment()

Normalize root to always include a leading and trailing slash.

      @root = "/#{@root}/".replace @rootStripper, '/'

Depending on whether we're using pushState or hashes, and whether
'onhashchange' is supported, determine how we check the URL state.

      if @_hasPushState
       $(window).on 'popstate', @checkUrl
      else if @_wantsHashChange and window.onhashchange?
       $(window).on 'hashchange', @checkUrl
      else if @_wantsHashChange
       @_checkUrlInterval = setInterval @checkUrl, @interval

      if not @options.silent
       @loadUrl null, null

####Add a listener to a router

     route: (route, callback) ->
      @handlers.unshift route: route, callback: callback

Check current url for changes

     checkUrl: (e) ->
      fragment = @getFragment()
      return if fragment is @fragment
      @loadUrl fragment, e

Call callbacks of matching route

     loadUrl: (fragment, e) ->
      fragment = @fragment = @getFragment fragment
      for handler in @handlers
       if handler.route.test fragment
        return handler.callback fragment, e

      return

####Navigate to a URL
Triggers a route is option trigger is on
Replaces the url is option replace is on
Sets state and title

     navigate: (fragment, options) ->
      return false if not History.started

      fragment = @getFragment(fragment or '')
      url = @root + fragment

Strip the fragment of the query and hash for matching.

      fragment = fragment.replace @pathStripper, ''

      return if @fragment is fragment
      @fragment = fragment

Don't include a trailing slash on the root.

      if fragment is '' and url isnt '/'
       url = url.slice 0, -1

If pushState is available, we use it to set the fragment as a real URL.

      if @_emulateState
       if options.replace is on
        @popEmulateState()
       state = {}
       state = options.state if options.state?
       title = ''
       title = options.title if options.title?
       @pushEmulateState state, title, fragment
      else if @_hasPushState
       method = if options.replace then 'replaceState' else 'pushState'
       state = {}
       state = options.state if options.state?
       title = ''
       title = options.title if options.title?
       @history[method] state, title, url

If hash changes haven't been explicitly disabled, update the hash
fragment to store history.

      else if @_wantsHashChange
       @_updateHash @location, fragment, options.replace

If you've told us that you explicitly don't want fallback hashchange-
based history, then `navigate` becomes a page refresh.

      else
       return @location.assign url
      if options.trigger
       return @loadUrl fragment, null

Update the hash

     _updateHash: (location, fragment, replace) ->
      if replace
       href = location.href.replace /(javascript:|#).*$/, ''
       location.replace "#{href}##{fragment}"
      else
       location.hash = "##{fragment}"

    Weya.Router = Router
    Weya.history = new History

    if module?
     module.exports = Weya.Router

#Weya.Router.litcoffee

    @Weya = {} unless @Weya?
    Weya = @Weya

    if exports?
     Weya = require './weya'
     Weya.Base = require './weya_base'

    if not Weya.Base?
     throw new Error 'Weya.Base not found'

##Weya.Router

Routing with hash tags or pushState

    class Router extends Weya.Base
     optionalParam: /\((.*?)\)/g
     namedParam: /(\(\?)?:\w+/g
     splatParam: /\*\w+/g
     escapeRegExp: /[\-{}\[\]+?.,\\\^$|#\s]/g

     @extend()

     @initialize ->
      @_bindRoutes()
      @_event = null
      @_history = []

     _routes: {}

####Register router

`'analyse/:analysis': 'onAnalyse'`

`dashboard': ['auth', 'dashboard']`

This will match urls of the form `analyse/\*` and call method @onAnalyse
with the parameter. Second route will first call @auth if it returns
`true` @dashboard will be called.

The most specific route should be at bottom. Routers can added or overridden
in subclasses

     @routes: (routes) ->
      @::_routes = _.clone @::_routes
      for k, v of routes
       @::_routes[k] = v

####Starts routing
Option silent will not trigger an event for the current url

     start: (options) ->
      Weya.history.start options
      fragment = Weya.history.getFragment()

      if options?.silent is on
       @_history.push fragment: fragment, title: document.title

####Goes to previous page if exists

     back: ->
      if @_history.length > 1
       Weya.history.back()

####Whether it is possible to go to previous page

     canBack: ->
      if @_history.length > 1 and Weya.history.canBack()
       return true
      else
       return false

####Registers a route

     route: (route, name) ->
      (route = @_routeToRegExp route) if not _.isRegExp route

Registers the route with Weya.history

      Weya.history.route route, (fragment, event) =>
       args = @_extractParameters route, fragment
       @_event = event

Handle state

       if @_event?.type is "popstate"
        @_history.pop()
        if @_history.length is 0
         @_history.push
          fragment: fragment
          title: document.title
          state: @getState()
       else
        @_history.push
         fragment: fragment
         title: document.title
         state: @getState()

Calls callbacks in order

       callbacks = name
       callbacks = [callbacks] if not Array.isArray callbacks

       for callback in callbacks
        if (typeof callback) is 'string'
         callback = @[callback]
        break unless callback.apply this, args

####Gets the current HTML5 History state

     getState: ->
      if @_event?.originalEvent?.state?
       return @_event.originalEvent.state
      else
       return null

####Navigate to a new URL, while setting HTML5 History state

     navigate: (fragment, options) ->
      options = {} unless options
      if options.replace
       @_history.pop()
      if not options.trigger
       @_history.push
        fragment: fragment
        title: options.title
        state: options.state

      Weya.history.navigate fragment, options

Bind routes

     _bindRoutes: ->
      for route, name of @_routes
       @route route, name

Convert a route string into a regular expression, suitable for matching

     _routeToRegExp: (route) ->
      route = route.replace @escapeRegExp, '\\$&'
                   .replace @noptionalParam, '(?:$1)?'
                   .replace @namedParam, (match, optional) ->
                    if optional then match else '([^\/]+)'
                   .replace @splatParam, '(.*?)'

       return new RegExp "^#{route}$"

Extract parameters from a route regex and URL

     _extractParameters: (route, fragment) ->
      params = route.exec(fragment).slice(1)
      return _.map params, (param) ->
       if param then decodeURIComponent(param) else null

## History class

    class History extends Weya.Base
     @extend()

Strip leading hash/slash and trailing space

     routeStripper: /^[#\/]|\s+$/g

Strip leading and trailing slashes

     rootStripper: /^\/+|\/+$/g

Remove trailing slash

     trailingSlash: /\/$/

Strip urls of hash and query

     pathStripper: /[?#].*$/

     @initialize ->
      @handlers = []
      _.bindAll this, 'checkUrl'
      @history = window.history
      @location = window.location
      @stateList = []

Hash check interval

     interval: 50

Get hash value

     getHash: ->
      match = @location.href.match /#(.*)$/
      return (if match? then match[1] else '')

Get emulated state

     getEmulateState: ->
      if @stateList.length > 0
       return @stateList[@stateList.length - 1]
      else
       return {fragment: ""}

     popEmulateState: -> @stateList.pop()

     pushEmulateState: (state, title, fragment) ->
      @stateList.push
       state: state
       title: title
       fragment: fragment

Get the URL fragment

     getFragment: (fragment, forcePushState) ->
      if not fragment?
       if @_emulateState
        fragment = @getEmulateState().fragment
       else if @_hasPushState or not @_wantsHashChange or forcePushState
        fragment = @location.pathname
        root = @root.replace @trailingSlash, ''
        if (fragment.indexOf root) is 0
         fragment = fragment.slice root.length
       else
        fragment = @getHash()

      return fragment.replace @routeStripper, ''

####Goto the previous page

     back: ->
      if @_emulateState is on
       @popEmulateState()
       @loadUrl null, null
      else
       @history?.back?()

####Can back?

     canBack: ->
      if @_emulateState is on
       return @stateList.length > 1
      else
       return @history?.back?

####Start listening to events

     start: (options) ->
      History.started = true

      @options = _.extend {root: '/'}, @options, options
      @root = @options.root
      @_emulateState = @options.emulateState is on
      @_wantsHashChange = @_emulateState is off and @options.hashChange isnt off
      @_wantsPushState = @_emulateState is off and @options.pushState is on
      @_hasPushState = @_wantsPushState is on and @history?.pushState?
      if @_emulateState and @options.start?
       @pushEmulateState @options.start.state,
        @options.start.title
        @options.start.fragment

      @fragment = @getFragment()

Normalize root to always include a leading and trailing slash.

      @root = "/#{@root}/".replace @rootStripper, '/'

Depending on whether we're using pushState or hashes, and whether
'onhashchange' is supported, determine how we check the URL state.

      if @_hasPushState
       $(window).on 'popstate', @checkUrl
      else if @_wantsHashChange and window.onhashchange?
       $(window).on 'hashchange', @checkUrl
      else if @_wantsHashChange
       @_checkUrlInterval = setInterval @checkUrl, @interval

      if not @options.silent
       @loadUrl null, null

####Add a listener to a router

     route: (route, callback) ->
      @handlers.unshift route: route, callback: callback

Check current url for changes

     checkUrl: (e) ->
      fragment = @getFragment()
      return if fragment is @fragment
      @loadUrl fragment, e

Call callbacks of matching route

     loadUrl: (fragment, e) ->
      fragment = @fragment = @getFragment fragment
      return _.any this.handlers, (handler) ->
       if handler.route.test fragment
        handler.callback fragment, e
        return true
       else
        return false

####Navigate to a URL
Triggers a route is option trigger is on
Replaces the url is option replace is on
Sets state and title

     navigate: (fragment, options) ->
      return false if not History.started

      fragment = @getFragment(fragment or '')
      url = @root + fragment

Strip the fragment of the query and hash for matching.

      fragment = fragment.replace @pathStripper, ''

      return if @fragment is fragment
      @fragment = fragment

Don't include a trailing slash on the root.

      if fragment is '' and url isnt '/'
       url = url.slice 0, -1

If pushState is available, we use it to set the fragment as a real URL.

      if @_emulateState
       if options.replace is on
        @popEmulateState()
       state = {}
       state = options.state if options.state?
       title = ''
       title = options.title if options.title?
       @pushEmulateState state, title, fragment
      else if @_hasPushState
       method = if options.replace then 'replaceState' else 'pushState'
       state = {}
       state = options.state if options.state?
       title = ''
       title = options.title if options.title?
       @history[method] state, title, url

If hash changes haven't been explicitly disabled, update the hash
fragment to store history.

      else if @_wantsHashChange
       @_updateHash @location, fragment, options.replace

If you've told us that you explicitly don't want fallback hashchange-
based history, then `navigate` becomes a page refresh.

      else
       return @location.assign url
      if options.trigger
       return @loadUrl fragment, null

Update the hash

     _updateHash: (location, fragment, replace) ->
      if replace
       href = location.href.replace /(javascript:|#).*$/, ''
       location.replace "#{href}##{fragment}"
      else
       location.hash = "##{fragment}"

    Weya.Router = Router
    Weya.history = new History

    if module?
     module.exports = Weya.Router


Fix undefined varaible "error"

Fix undefined varaible "error"

    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

      if not req.get('Origin') or not (req.get('Origin') 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 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 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

    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

      if not req.get('Origin') or not (req.get('Origin') 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 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 raven.middleware.express sentry
    app.use (err, req, res, next) ->
      if not err.status or err.status >= 500
        sentry.captureError error

        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


removed some unneeded variable assignments

# Batman Request
 Request is an abstraction around XHR requests.  It can be used with a variety of platforms, and is implemented the platform layer files.

## Platform Request Implmentation Libraries:
  Depending on the platform library the underlying request implementation will change:

 - `batman.jquery.js`: jQuery XHR
 - `batman.node.js`: "querystring" and "url"
 - `batman.solo.js`: Reqwest.js

 _note_ For the tests there are some mocked responses, in a normal non testing situation you would not use these

## How to use Batman Request

  Batman.Request can be used a variety of ways.  This is the underlying library that the `rest_storage_adapater` uses to communicate.

  In other cases where you want to communicate with a server that isn't your default storage_adapter you would use it like:

    test "Send a simple Batman.Request", ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse( "GET", "http://batmanjs.org", ->{})
      new Batman.Request
        url: "http://batmanjs.org"
        method: "GET"
        success: (result) ->
          ok true

## Lifecycle events
 There are a variety of lifecycle events that get fired in a `Request`.  Each of these are passed in as parameters in the options object in the `Request` constructor.

 1. `loading()`: fired before `send()` has been called.

    test "Demonstrate the loading event", 1, ->
       new Batman.Request
         url: "http:://batmanjs.org"
         loading: ->
           ok true, "After send but before the request is made"

 2. `loaded()`: fired after the request is complete

     test "Demo the loaded event", 1, ->
       Batman.Request.setupMockedResponse()
       Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {})
       new Batman.Request
         url: "http://batmanjs.org"
         loaded: ->
           ok true, "Loaded"

 3. `error( xhr )`: fired when an error is detected,  `xhr` is the platform defined xhr object with a property named `request` being the `Batman.Request` object.

    test "Demo the error event", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> status: 400)
      new Batman.Request
        url: "http://batmanjs.org"
        error: (xhr) ->
          ok true, "Error"

 4. `success ( response )`: fired when a successful request has been made, `response` is the body of the request response.

    test "Demonstrate the success event", 1,  ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> response: "foo")
      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          equal "foo", response

## Request::status
  The response status, only set after `error` or `success`

    test "Demo the error event", 1, ->
     Batman.Request.setupMockedResponse()
     Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> status: 400)
     new Batman.Request
       url: "http://batmanjs.org"
       error: (xhr) ->
         equal 400, @status

## Request::response
  The response object

    test "Demonstrate the response object", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> response: "foo" )

      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          equal "foo", JSON.parse(@response)

## Request::responseHeaders : PlainObject
  An key/value object with the response headers

    test "responseHeaders", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> responseHeaders: { FOO: "bar" } )

      new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          deepEqual @responseHeaders,{ FOO: "bar" }

## Request( options : PlainObject )

  Constructs a new `Batman.Request` with the supplied options

  `options` description
  1. `url : String`: A string containing the URL to which the request is sent.  Mandatory
  2. `method : String`: The type of request to make eg. `GET`, `POST`.  Default is `GET`

    test "method test", ->
      Batman.Request.addMockedResponse("POST", "http://batmanjs.org", -> response : { FOO: "BAR" })
      new Batman.Request
        url: "http://batmanjs.org"
        method: "POST"
        data: { some: "data" }
        success: (response) ->
          ok true

  3. `data`: Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests.
  4. `contentType`: the content type to send, defaults to `application/x-www-form-urlencoded`

    test "contentType default", ->
       req = new Batman.Request
       equal req.contentType, "application/x-www-form-urlencoded"

  5. `autosend`: controls whether the request will be sent automatically, deafults to `true`

    test "autosend test", ->
      response_sent = false
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        success: (response) ->
          response_sent = true
      ok !response_sent
      req.send()
      ok response_sent

  6. `username`: username for authentication
  7. `password`: password for authentication
  8. `type`: The type of data you're expecting from the server, defaults to `json`

## Request::send( [data] )
  If `autosend` is `false`, this will initiate the request.  The data object passed in will take priority over the one specified in the `options` object

    test "request.send()", ->
      beforeResponse = (req, data) ->
        deepEqual data, { other: "thing" }
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {beforeResponse: beforeResponse} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        data: { some: "data" }
      req.send( { other: "thing" } )

# Batman Request
 Request is an abstraction around XHR requests.  It can be used with a variety of platforms, and is implemented the platform layer files.

## Platform Request Implmentation Libraries:
  Depending on the platform library the underlying request implementation will change:

 - `batman.jquery.js`: jQuery XHR
 - `batman.node.js`: "querystring" and "url"
 - `batman.solo.js`: Reqwest.js

 _note_ For the tests there are some mocked responses, in a normal non testing situation you would not use these

## How to use Batman Request

  Batman.Request can be used a variety of ways.  This is the underlying library that the `rest_storage_adapater` uses to communicate.

  In other cases where you want to communicate with a server that isn't your default storage_adapter you would use it like:

    test "Send a simple Batman.Request", ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse( "GET", "http://batmanjs.org", ->{})
      req = new Batman.Request
        url: "http://batmanjs.org"
        method: "GET"
        success: (result) ->
          ok true

## Lifecycle events
 There are a variety of lifecycle events that get fired in a `Request`.  Each of these are passed in as parameters in the options object in the `Request` constructor.

 1. `loading()`: fired before `send()` has been called.  Anaglous to the jQuery `beforeSend`

    test "Demonstrate the loading event", 1, ->
       req = new Batman.Request
         url: "http:://batmanjs.org"
         loading: ->
           ok true, "After send but before the request is made"

 2. `loaded()`: fired after the request is complete, Anaglous to the jQuery `complete`

     test "Demo the loaded event", 1, ->
       Batman.Request.setupMockedResponse()
       Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {})
       req = new Batman.Request
         url: "http://batmanjs.org"
         loaded: ->
           ok true, "Loaded"

 3. `error( xhr )`: fired when an error is detected,  `xhr` is the `XMLHttpRequest` object with `request` being the `Batman.Request` object.

    test "Demo the error event", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> status: 400)
      req = new Batman.Request
        url: "http://batmanjs.org"
        error: (xhr) ->
          ok true, "Error"

 4. `success ( response )`: fired when a successful request has been made, `response` is the body of the request response.

    test "Demonstrate the loading event", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> response: "foo")
      req = new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          equal "foo", response

## Request::status
  The response status, only set after `error` or `success`

    test "Demo the error event", 1, ->
     Batman.Request.setupMockedResponse()
     Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> status: 400)
     req = new Batman.Request
       url: "http://batmanjs.org"
       error: (xhr) ->
         equal 400, @status

## Request::response
  The response object

    test "Demonstrate the response object", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> response: "foo" )

      req = new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          equal "foo", JSON.parse(@response)

## Request::responseHeaders : PlainObject
  An key/value object with the response headers

    test "responseHeaders", 1, ->
      Batman.Request.setupMockedResponse()
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> responseHeaders: { FOO: "bar" } )

      req = new Batman.Request
        url: "http://batmanjs.org"
        success: (response) ->
          deepEqual @responseHeaders,{ FOO: "bar" }

## Request( options : PlainObject )

  Constructs a new `Batman.Request` with the supplied options

  `options` description
  1. `url : String`: A string containing the URL to which the request is sent.  Mandatory
  2. `method : String`: The type of request to make eg. `GET`, `POST`.  Default is `GET`

    test "method test", ->
      Batman.Request.addMockedResponse("POST", "http://batmanjs.org", -> response : { FOO: "BAR" })
      req = new Batman.Request
        url: "http://batmanjs.org"
        method: "POST"
        data: { some: "data" }
        success: (response) ->
          ok true

  3. `data`: Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests.
  4. `contentType`: the content type to send, defaults to `application/x-www-form-urlencoded`

    test "contentType default", ->
       req = new Batman.Request
       equal req.contentType, "application/x-www-form-urlencoded"

  5. `autosend`: controls whether the request will be sent automatically, deafults to `true`

    test "autosend test", ->
      response_sent = false
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        success: (response) ->
          response_sent = true
      ok !response_sent
      req.send()
      ok response_sent

  6. `username`: username for authentication
  7. `password`: password for authentication
  8. `type`: The type of data you're expecting from the server, defaults to `json`

## Request::send( [data] )
  If `autosend` is `false`, this will initiate the request.  The data object passed in will take priority over the one specified in the `options` object

    test "request.send()", ->
      beforeResponse = (req, data) ->
        deepEqual data, { other: "thing" }
      Batman.Request.addMockedResponse("GET", "http://batmanjs.org", -> {beforeResponse: beforeResponse} )
      req = new Batman.Request
        url: "http://batmanjs.org"
        autosend: false
        data: { some: "data" }
      req.send( { other: "thing" } )


document encodesNestedAttributesFor

document encodesNestedAttributesFor

# /api/Extras/Batman.Rails

The `batman.rails` extra provides helpers for using batman.js with [Ruby on Rails](http://rubyonrails.org). It is packaged with batman.js builds on the [downloads page](/download.html).

See the [`batman-rails` gem](https://github.com/batmanjs/batman-rails) for more information about batman.js-Rails integration.

## Configuration

`batman.rails` provides the following default configurations:

```coffeescript
Batman.config.pathToHTML = '/assets/batman/html'
Batman.config.protectFromCSRF = true
Batman.config.metaNameForCSRFToken = 'csrf-token'
```

# /api/Extras/Batman.Rails/Batman.RailsStorage

`Batman.RailsStorage` extends `Batman.RestStorage`. It adds some features for integration with [Ruby on Rails](http://rubyonrails.org). You can use it to persist your `Batman.Model` subclasses:

```coffeescript
class MyApp.MyModel extends Batman.Model
  @persist Batman.RailsStorage
```

## ".json" Request Suffix

When performing storage operations, `Batman.RailsStorage` will add `.json` to URLs if it isn't already present. This way, Rails will always recognize it as `format: :json`.

## Cross-Site Request Forgery Protection

By default, `batman.rails` participates in the Ruby on Rails cross-site request forgery (CSRF) protection scheme. Batman.js will:

- extract the CSRF token from meta-tags (using `Batman.config.metaNameForCSRFToken`)
- `Batman.RailsStorage` will send the CSRF token as a request header, `X-CSRF-Token`, when performing storage operations.

You can disable CSRF protection by setting the configuration to `false`:

```coffeescript
Batman.config.protectFromCSRF = false
```

## Server-Side Errors

After update and create operations, `Batman.RailsStorage` checks for validation errors returned from the server.  The response must have status code `422` (Rails' default for validation errors).

The response JSON may have an `errors` key:

```javascript
{ "errors" : {
    "user" : {
      "email": ["must be unique"],
      "first_name": ["must be present", "must be longer than 3 characters"]
    }
  }
}
```

Or, the errors may be at the top level:

```javascript
{ "user" : {
    "email": ["must be unique"],
    "first_name": ["must be present", "must be longer than 3 characters"]
    }
  }
}
```

Any errors will be added to client-side record according to its key in the JSON.

## Date Encoder

`Batman.Encoders.railsDate` may be used to send and receive dates with the Rails default date serialization:

```coffeescript
  @encode 'last_retrieved_at', Batman.Encoders.railsDate
```

# /api/Extras/Batman.Rails/Batman.RailsStorage ModelMixin

The following functions are mixed into models persisted with `Batman.RailsStorage`.

## @encodeTimestamps(attrs... =['created_at', 'updated_at'])

Pulls `attrs` from JSON, but doesn't send them back to the server. It uses `Batman.Encoders.railsDate`, which is provided by `batman.rails`.

## @encodesNestedAttributesFor(keys...)

Associations named by `keys` will be encoded in the parent model's JSON so that they work with Rails' `accepts_nested_attributes_for`. These associations must have the `saveInline: true` option.

This means:

- data will be sent to the server with an `_attributes` suffix
- after an update, any associated records where `record.get("_destroy")` is truthy will be counted as destroyed. (This assumes that Rails destroyed them on the backend, too.)