review changes

review changes

# Batman Helpers

Batman includes a number of useful, general purpose helper functions and references. They can all be found attached to the `Batman` object and can optionally be exported into the global namespace with a `$` prefix.

## container

`Batman.container` points to either the `window` object if running in the browser, or the `global` object if running in node. This is useful if you want to add something to the global scope in all environments.

## @typeOf(object) : string

`typeOf` determines a more specific type of an `object` than the native `typeof` operator in JavaScript. This is useful for a number of situations like dealing with `Object` promoted strings and numbers, or arrays which look like `object`s to `typeof`. Use `typeOf` when you need more than `"object"` from `typeof`.

_Note_: `typeOf` is substantially slower than `typeof`. `typeOf` works in a somewhat hackish manner by getting the `Object::toString` representation of the object and slicing it to retrieve the name of the constructor.

    test 'typeOf returns "String" for both strings and Object strings', ->
      primitive = "test"
      objectified = new String("test")
      equal typeof primitive, "string"
      equal typeof objectified, "object"
      equal Batman.typeOf(primitive), "String"
      equal Batman.typeOf(objectified), "String"

    test 'typeOf returns Array for arrays', ->
      array = [];
      equal typeof array, "object"
      equal Batman.typeOf(array), "Array"

## @mixin(subject, objects...) : subject

`mixin`, occasionally known elsewhere as `extend` or `merge`, flattens a series of objects onto the subject. Key/value pairs on objects passed as later arguments (arguments with a higher index) take precedence over earlier arguments. Returns the `subject` passed in with the new values.

`mixin` also has special properties that make it different than the canonical `extend` functions:

 1. If the `subject` has a `set` function, `subject.set(key, value)` will be used to apply keys instead of `subject[key] = value`. This means that if the subject is a `Batman.Object`, observers and thus bindings on the object will be notified when other (Batmanified or not) objects are mixed into it.
 2. If a mixed-in `object` has an `initialize` function defined, that function will be called and passed the `subject`. This is useful for custom extension logic, similar to `self.included` in Ruby. For this reason, the keys `initialize` and `uninitialize` are skipped by `mixin`.
 3. `mixin` only iterates over keys for which the `hasOwnProperty` test passes.

_Note_: `mixin` is destructive to (only) the first argument. If you need a non-destructive version of `mixin`, just pass an empty object as the first object, and all keys from the successive arguments will be applied to the empty object.

    test 'mixin merges argument objects', ->
      subject = {}
      deepEqual Batman.mixin(subject, {fit: true}, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject"
      deepEqual subject, {fit: true, fly: true, funky: true}, "the subject is modified destructively"

    test 'mixin merges argument objects', ->
      unmodified = {fit: true}
      deepEqual Batman.mixin({}, unmodified, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject"
      deepEqual unmodified, {fit: true}, "argument objects are untouched allowing non-destructive merge"

    test 'mixed in objects passed as higher indexed arguments take precedence', ->
      subject = {}
      deepEqual Batman.mixin(subject, {x: 1, y: 1}, {x: 2}), {x: 2, y: 1}

## @unmixin(subject, objects...) : subject

`unmixin` "unmerges" the passed objects from the `subject`. If a key exists on any of the `objects` it will be `delete`d from the `subject`. Returns the `subject`.

`unmixin`, similar to `mixin`, supports calling an `uninitialize` function for each of the `objects` being unmixed in. If an `uninitialize` function exists on each

    test 'unmixin removes keys found on the unmixined objects on the subject', ->
      subject = {fit: true, fly: true, funky: true}
      deepEqual Batman.unmixin(subject, {fit: true}, {fly: true}), {funky: true}, "unmixin returns the subject"
      deepEqual subject, {funky: true}, "the subject is destructively modified."

## @functionName(function) : string

`functionName` returns the name of a given function, if any. Works with Internet Explorer 7/8/9, FireFox, Chrome, and Safari.

    test 'functionName returns the name of a given function', ->
      equal Batman.functionName("".toString), 'toString'

## @isChildOf(parent : HTMLElement, child : HTMLElement) : boolean

`isChildOf` is a simple DOM helper which returns a boolean describing if the passed `child` node can be found in the descendants of the passed `parent` node.

## @setImmediate(callback : Function) : object

`setImmediate` (and its sister `clearImmediate`) are a more efficient version of `setTimeout(callback, 0)`. Due to timer resolution issues, setTimeout passed a timeout of 0 doesn't actually execute the function as soon as the JS execution stack has been emptied, but at minimum 4ms and maxmium 25ms after. For this reason Batman provides a cross browser implementation of `setImmediate` which does its best to call the callback immediately after the stack empties. Batman's `setImmediate` polyfill uses the native version if available, `window.postmessage` trickery if supported, and falls back on `setTimeout(->, 0)`.

`setImmediate` returns a handle which can be passed to `clearImmediate` to cancel the future calling of the callback.

## @clearImmediate(handle)

`clearImmediate` stops the calling of a callback in the future when passed its `handle` (which is returned from the `setImmediate` call used to enqueue it).

## @forEach(iterable : object, iterator : Function[, context : Object])

The `forEach` Batman helper is a universal iteration helper. When passed an `iterable` object, the helper will call the `iterator` (optionally in the `context`) for each item in the `iterable`. The `iterable` can be:

 1. something which has its own `forEach`, in which case the `iterator` will just be passed to `iterable.forEach`.
 2. an array like object, in which case a JavaScript `for(;;)` loop will be used to iterate over each entry
 3. or an object, in which case a JavaScript `for-in` loop will be used to iterate over each entry.

The `forEach` helper is useful for iterating over objects when the type of those objects isn't guaranteed.

    test 'forEach iterates over objects with forEach defined', ->
      results = []
      set = new Batman.SimpleSet('a')
      Batman.forEach(set, (x) -> results.push(x))
      deepEqual results, ['a']

    test 'forEach iterates over array like objects', ->
      results = []
      ArrayLike = ->
      ArrayLike:: = []
      imitation = new ArrayLike
      Array::push.call(imitation, "a")
      Array::push.call(imitation, "b")
      Batman.forEach(imitation, (x) -> results.push(x))
      deepEqual results, ['a', 'b']

    test 'forEach iterates over objects', ->
      result = {}
      object = {x: true}
      Batman.forEach(object, (key, val) -> result[key] = val)
      deepEqual result, object

## @objectHasKey(object, key) : boolean

`objectHasKey` returns a boolean describing the presence of the `key` in the passed `object`. `objectHasKey` delegates to the `object`'s `hasKey` function if present, and otherwise just does a check using the JavaScript `in` operator.

    test 'objectHasKey verifies if a key is present in an object', ->
      subject = {fit: true}
      ok Batman.objectHasKey(subject, 'fit')
      equal Batman.objectHasKey(subject, 'flirty'), false

    test 'objectHasKey verifies if a key is present in an object with `hasKey` defined', ->
      subject = new Batman.SimpleHash {fit: true}
      ok Batman.objectHasKey(subject, 'fit')
      equal Batman.objectHasKey(subject, 'flirty'), false

## @contains(object, item) : boolean

`contains` returns a boolean describing if the given `object` has member `item`. Membership in this context is defined as:

 + the result of `object.has(item)` if the `object` has a `has` function defined
 + the result of `item in object` if the `object` is arraylike
 + the result of the Batman.objectHasKey otherwise

_Note_: When passed an object without a `has` function, `contains` will return `true` if the `object` has `item` as a *`key`*, not as a value at any key.

`contains` is useful for checking item membership when the type of the object can't be relied on.

## @get(object, key) : value

`get` is a general purpose function for retrieving the value from a `key` on an `object` of an indeterminate type. This is useful if code needs to work with both `Batman.Object`s and Plain Old JavaScript Objects. `get` has the following semantics:

 + if the `object` has a `get` function defined, return the result of `object.get(key)`
 + if the object does not have a `get` function defined, use an ephemeral `Batman.Property` to retrieve the key. This is equivalent to `object[key]` for single segment `key`s, but if the `key` is multi-segment (example: 'product.customer.name'), `get` will do nested gets until the either `undefined` or the end of the keypath is reached.

    test 'get returns the value at a key on a POJO', ->
      subject = {fit: true}
      equal Batman.get(subject, 'fit'), true
      equal Batman.get(subject, 'flirty'), undefined

    test 'get returns the value at a key on a Batman.Object', ->
      subject = Batman {fit: true}
      equal Batman.get(subject, 'fit'), true
      equal Batman.get(subject, 'flirty'), undefined

    test 'get returns the value at a deep key on a POJO', ->
      subject = {customer: {name: "Joe"}}
      equal Batman.get(subject, 'customer.name'), "Joe"
      equal Batman.get(subject, 'customer.age'), undefined

    test 'get returns the value at a deep key on a Batman.Object', ->
      subject = Batman {customer: {name: "Joe"}}
      equal Batman.get(subject, 'customer.name'), "Joe"
      equal Batman.get(subject, 'customer.age'), undefined

## @getPath(base, segments) : string

`getPath` returns the hash value denoted by the specified path, which consists of an array of nested hash keys. See examples below for more detail.

    test "takes a base and an array of keys and returns the corresponding nested value", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      equal Batman.getPath(@complexObject, ['hash', 'foo', 'bar']), 'nested value'
      equal Batman.getPath(@complexObject, ['hash', 'foo.bar']), 'flat value'
      strictEqual Batman.getPath(@complexObject, ['hash', 'not-foo', 'bar']), undefined

    test "returns just the base if the key array is empty", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(@complexObject, []), @complexObject
      strictEqual Batman.getPath(null, []), null

    test "returns undefined if the base is null-ish", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(null, ['foo']), undefined
      strictEqual Batman.getPath(undefined, ['foo']), undefined

    test "returns falsy values", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(num: 0, ['num']), 0
      strictEqual Batman.getPath(thing: null, ['thing']), null

## @escapeHTML(input) : string

`escapeHTML` takes a string of unknown origin and makes it safe for display on a web page by encoding control characters in HTML into their HTML entities.

*Warning*: Do not rely on `escapeHTML` to purge unsafe data from user submitted content. While `escapeHTML` is applied to every binding's contents by default, it should not be your only line of defence against script injection attacks.

    test 'escapeHTML encodes special characters into HTML entities', ->
      equal Batman.escapeHTML("& < > \" '"), "& < > " '"

# Batman.helpers

`Batman.helpers` is a namespace for Batman's helpful string manipulation helpers.

_Note_: Batman's pluralization functions mirror those of Rails' exactly.

## @ordinalize(value : [number|string]) : string

`ordinalize` converts a given integer into an ordinal string for describing position in a list, like 1st, 2nd, or 20th.

    test 'ordinalize converts numbers to their ordinal form', ->
      equal Batman.helpers.ordinalize(1), "1st"
      equal Batman.helpers.ordinalize("2"), "2nd"
      equal Batman.helpers.ordinalize(1002), "1002nd"
      equal Batman.helpers.ordinalize("1003"), "1003rd"
      equal Batman.helpers.ordinalize(-11), "-11th"
      equal Batman.helpers.ordinalize(-1021), "-1021st"

## @singularize(pluralString : string) : string

`singularize` converts the plural form of a word to a singular form.

    test 'singularize converts plural words to singular words', ->
      equal Batman.helpers.singularize("posts"), "post"
      equal Batman.helpers.singularize("octopi"), "octopus"
      equal Batman.helpers.singularize("sheep"), "sheep"
      equal Batman.helpers.singularize("word"), "word"
      equal Batman.helpers.singularize("CamelOctopi"), "CamelOctopus"

## @pluralize(singularString : string) : string

`pluralize` converts the singular form of a word to the plural form.

    test 'pluralize converts plural words to singular words', ->
      equal Batman.helpers.pluralize("post"), "posts"
      equal Batman.helpers.pluralize("octopus"), "octopi"
      equal Batman.helpers.pluralize("sheep"), "sheep"
      equal Batman.helpers.pluralize("words"), "words"
      equal Batman.helpers.pluralize("CamelOctopus"), "CamelOctopi"

## @camelize(name [, lowercaseFirstLetter = false]) : string

`camelize` converts the passed `name` to UpperCamelCase. If the second argument is passed as `true`, then lowerCamelCase is returned.

    test 'camelize returns the CamelCase version of an under_scored word', ->
      equal Batman.helpers.camelize("batman_object"), "BatmanObject"
      equal Batman.helpers.camelize("batman_object", true), "batmanObject"

## @underscore(string) : string

`underscore` returns the underscored version of a CamelCase word.

    test 'underscore converts CamelCase to under_scores', ->
      equal Batman.helpers.underscore("BatmanObject"), "batman_object"

## @capitalize(string) : string

`capitalize` does a word-wise capitalization of a phrase or word.

    test 'capitalize makes the first letter of each word in the string uppercase', ->
      equal Batman.helpers.capitalize("batmanObject"), "BatmanObject"
      equal Batman.helpers.capitalize("batman object"), "Batman Object"
      equal Batman.helpers.capitalize("AlreadyCapitalized"), "AlreadyCapitalized"

## @trim(string) : string

`trim` trims a string getting rid of extra white space around the string or returning an empty string if it is null.

    test 'trim gets rid of space around a string', ->
      equal Batman.helpers.trim("TrimRight "), "TrimRight"
      equal Batman.helpers.trim(" TrimLeft"), "TrimLeft"
      equal Batman.helpers.trim(" TrimBothSides "), "TrimBothSides"
      equal Batman.helpers.trim("AlreadyTrimmed"), "AlreadyTrimmed"

    test 'trim turns a null or undefined string into an empty string', ->
      equal Batman.helpers.trim(null), ""
      equal Batman.helpers.trim(undefined), ""

## @interpolate(stringOrObject, keys) : string

`interpolate` interpolates a string, filling it in with values matching interpolation keys, similar to printf variants.

    test 'interpolate fills in key values globally in a string', ->
      equal Batman.helpers.interpolate("%{field} must be at least %{count} characters in order for %{field} to be valid", count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

    test 'interpolate fills in key values globally in an object-embedded string', ->
      equal Batman.helpers.interpolate({'other': "%{field} must be at least %{count} characters in order for %{field} to be valid"}, count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

    test 'interpolate fills in key values globally in an object-embedded string, embedded by key count', ->
      equal Batman.helpers.interpolate({3: "%{field} must be at least %{count} characters in order for %{field} to be valid"}, count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

## @humanize(string) : string

`humanize` reformats a string that has a programmatic meaning (camelcased, underscored, "_id" suffixed) to make human-readable by separating concatenated words and/or getting rid of the "_id" suffix.

    test 'humanize replaces underscores and dashes with empty space', ->
      equal Batman.helpers.humanize('underscored_string'), 'Underscored string'
      equal Batman.helpers.humanize('dash-separated-string'), 'Dash separated string'

    test 'humanize splits camel cased words by empty space', ->
      equal Batman.helpers.humanize('CamelCasedString'), 'Camel cased string'

    test 'humanize title-cases the first word', ->
      equal Batman.helpers.humanize('lower case sentence'), 'Lower case sentence'

    test 'humanize lower-cases all words after the first one', ->
      equal Batman.helpers.humanize('Mixed Case SENTENCE'), 'Mixed case sentence'

    test 'humanize gets rid of _id suffix', ->
      equal Batman.helpers.humanize('identifying_string_id'), 'Identifying string'

# Batman Helpers

Batman includes a number of useful, general purpose helper functions and references. They can all be found attached to the `Batman` object and can optionally be exported into the global namespace with a `$` prefix.

## container

`Batman.container` points to either the `window` object if running in the browser, or the `global` object if running in node. This is useful if you want to add something to the global scope in all environments.

## @typeOf(object) : string

`typeOf` determines a more specific type of an `object` than the native `typeof` operator in JavaScript. This is useful for a number of situations like dealing with `Object` promoted strings and numbers, or arrays which look like `object`s to `typeof`. Use `typeOf` when you need more than `"object"` from `typeof`.

_Note_: `typeOf` is substantially slower than `typeof`. `typeOf` works in a somewhat hackish manner by getting the `Object::toString` representation of the object and slicing it to retrieve the name of the constructor.

    test 'typeOf returns "String" for both strings and Object strings', ->
      primitive = "test"
      objectified = new String("test")
      equal typeof primitive, "string"
      equal typeof objectified, "object"
      equal Batman.typeOf(primitive), "String"
      equal Batman.typeOf(objectified), "String"

    test 'typeOf returns Array for arrays', ->
      array = [];
      equal typeof array, "object"
      equal Batman.typeOf(array), "Array"

## @mixin(subject, objects...) : subject

`mixin`, occasionally known elsewhere as `extend` or `merge`, flattens a series of objects onto the subject. Key/value pairs on objects passed as later arguments (arguments with a higher index) take precedence over earlier arguments. Returns the `subject` passed in with the new values.

`mixin` also has special properties that make it different than the canonical `extend` functions:

 1. If the `subject` has a `set` function, `subject.set(key, value)` will be used to apply keys instead of `subject[key] = value`. This means that if the subject is a `Batman.Object`, observers and thus bindings on the object will be notified when other (Batmanified or not) objects are mixed into it.
 2. If a mixed-in `object` has an `initialize` function defined, that function will be called and passed the `subject`. This is useful for custom extension logic, similar to `self.included` in Ruby. For this reason, the keys `initialize` and `uninitialize` are skipped by `mixin`.
 3. `mixin` only iterates over keys for which the `hasOwnProperty` test passes.

_Note_: `mixin` is destructive to (only) the first argument. If you need a non-destructive version of `mixin`, just pass an empty object as the first object, and all keys from the successive arguments will be applied to the empty object.

    test 'mixin merges argument objects', ->
      subject = {}
      deepEqual Batman.mixin(subject, {fit: true}, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject"
      deepEqual subject, {fit: true, fly: true, funky: true}, "the subject is modified destructively"

    test 'mixin merges argument objects', ->
      unmodified = {fit: true}
      deepEqual Batman.mixin({}, unmodified, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject"
      deepEqual unmodified, {fit: true}, "argument objects are untouched allowing non-destructive merge"

    test 'mixed in objects passed as higher indexed arguments take precedence', ->
      subject = {}
      deepEqual Batman.mixin(subject, {x: 1, y: 1}, {x: 2}), {x: 2, y: 1}

## @unmixin(subject, objects...) : subject

`unmixin` "unmerges" the passed objects from the `subject`. If a key exists on any of the `objects` it will be `delete`d from the `subject`. Returns the `subject`.

`unmixin`, similar to `mixin`, supports calling an `uninitialize` function for each of the `objects` being unmixed in. If an `uninitialize` function exists on each

    test 'unmixin removes keys found on the unmixined objects on the subject', ->
      subject = {fit: true, fly: true, funky: true}
      deepEqual Batman.unmixin(subject, {fit: true}, {fly: true}), {funky: true}, "unmixin returns the subject"
      deepEqual subject, {funky: true}, "the subject is destructively modified."

## @functionName(function) : string

`functionName` returns the name of a given function, if any. Works with Internet Explorer 7/8/9, FireFox, Chrome, and Safari.

    test 'functionName returns the name of a given function', ->
      equal Batman.functionName("".toString), 'toString'

## @isChildOf(parent : HTMLElement, child : HTMLElement) : boolean

`isChildOf` is a simple DOM helper which returns a boolean describing if the passed `child` node can be found in the descendants of the passed `parent` node.

## @setImmediate(callback : Function) : object

`setImmediate` (and its sister `clearImmediate`) are a more efficient version of `setTimeout(callback, 0)`. Due to timer resolution issues, setTimeout passed a timeout of 0 doesn't actually execute the function as soon as the JS execution stack has been emptied, but at minimum 4ms and maxmium 25ms after. For this reason Batman provides a cross browser implementation of `setImmediate` which does its best to call the callback immediately after the stack empties. Batman's `setImmediate` polyfill uses the native version if available, `window.postmessage` trickery if supported, and falls back on `setTimeout(->, 0)`.

`setImmediate` returns a handle which can be passed to `clearImmediate` to cancel the future calling of the callback.

## @clearImmediate(handle)

`clearImmediate` stops the calling of a callback in the future when passed its `handle` (which is returned from the `setImmediate` call used to enqueue it).

## @forEach(iterable : object, iterator : Function[, context : Object])

The `forEach` Batman helper is a universal iteration helper. When passed an `iterable` object, the helper will call the `iterator` (optionally in the `context`) for each item in the `iterable`. The `iterable` can be:

 1. something which has its own `forEach`, in which case the `iterator` will just be passed to `iterable.forEach`.
 2. an array like object, in which case a JavaScript `for(;;)` loop will be used to iterate over each entry
 3. or an object, in which case a JavaScript `for-in` loop will be used to iterate over each entry.

The `forEach` helper is useful for iterating over objects when the type of those objects isn't guaranteed.

    test 'forEach iterates over objects with forEach defined', ->
      results = []
      set = new Batman.SimpleSet('a')
      Batman.forEach(set, (x) -> results.push(x))
      deepEqual results, ['a']

    test 'forEach iterates over array like objects', ->
      results = []
      ArrayLike = ->
      ArrayLike:: = []
      imitation = new ArrayLike
      Array::push.call(imitation, "a")
      Array::push.call(imitation, "b")
      Batman.forEach(imitation, (x) -> results.push(x))
      deepEqual results, ['a', 'b']

    test 'forEach iterates over objects', ->
      result = {}
      object = {x: true}
      Batman.forEach(object, (key, val) -> result[key] = val)
      deepEqual result, object

## @objectHasKey(object, key) : boolean

`objectHasKey` returns a boolean describing the presence of the `key` in the passed `object`. `objectHasKey` delegates to the `object`'s `hasKey` function if present, and otherwise just does a check using the JavaScript `in` operator.

    test 'objectHasKey verifies if a key is present in an object', ->
      subject = {fit: true}
      ok Batman.objectHasKey(subject, 'fit')
      equal Batman.objectHasKey(subject, 'flirty'), false

    test 'objectHasKey verifies if a key is present in an object with `hasKey` defined', ->
      subject = new Batman.SimpleHash {fit: true}
      ok Batman.objectHasKey(subject, 'fit')
      equal Batman.objectHasKey(subject, 'flirty'), false

## @contains(object, item) : boolean

`contains` returns a boolean describing if the given `object` has member `item`. Membership in this context is defined as:

 + the result of `object.has(item)` if the `object` has a `has` function defined
 + the result of `item in object` if the `object` is arraylike
 + the result of the Batman.objectHasKey otherwise

_Note_: When passed an object without a `has` function, `contains` will return `true` if the `object` has `item` as a *`key`*, not as a value at any key.

`contains` is useful for checking item membership when the type of the object can't be relied on.

## @get(object, key) : value

`get` is a general purpose function for retrieving the value from a `key` on an `object` of an indeterminate type. This is useful if code needs to work with both `Batman.Object`s and Plain Old JavaScript Objects. `get` has the following semantics:

 + if the `object` has a `get` function defined, return the result of `object.get(key)`
 + if the object does not have a `get` function defined, use an ephemeral `Batman.Property` to retrieve the key. This is equivalent to `object[key]` for single segment `key`s, but if the `key` is multi-segment (example: 'product.customer.name'), `get` will do nested gets until the either `undefined` or the end of the keypath is reached.

    test 'get returns the value at a key on a POJO', ->
      subject = {fit: true}
      equal Batman.get(subject, 'fit'), true
      equal Batman.get(subject, 'flirty'), undefined

    test 'get returns the value at a key on a Batman.Object', ->
      subject = Batman {fit: true}
      equal Batman.get(subject, 'fit'), true
      equal Batman.get(subject, 'flirty'), undefined

    test 'get returns the value at a deep key on a POJO', ->
      subject = {customer: {name: "Joe"}}
      equal Batman.get(subject, 'customer.name'), "Joe"
      equal Batman.get(subject, 'customer.age'), undefined

    test 'get returns the value at a deep key on a Batman.Object', ->
      subject = Batman {customer: {name: "Joe"}}
      equal Batman.get(subject, 'customer.name'), "Joe"
      equal Batman.get(subject, 'customer.age'), undefined

## @getPath(base, segments) : string

`getPath` returns the hash value denoted by the specified path, which consists of an array of nested hash keys. See examples below for more detail.

    test "takes a base and an array of keys and returns the corresponding nested value", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      equal Batman.getPath(@complexObject, ['hash', 'foo', 'bar']), 'nested value'
      equal Batman.getPath(@complexObject, ['hash', 'foo.bar']), 'flat value'
      strictEqual Batman.getPath(@complexObject, ['hash', 'not-foo', 'bar']), undefined

    test "returns just the base if the key array is empty", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(@complexObject, []), @complexObject
      strictEqual Batman.getPath(null, []), null

    test "returns undefined if the base is null-ish", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(null, ['foo']), undefined
      strictEqual Batman.getPath(undefined, ['foo']), undefined

    test "returns falsy values", ->
      @complexObject = new Batman.Object
        hash: new Batman.Hash
          foo: new Batman.Object(bar: 'nested value'),
          "foo.bar": 'flat value'
      strictEqual Batman.getPath(num: 0, ['num']), 0
      strictEqual Batman.getPath(thing: null, ['thing']), null

## @escapeHTML(string) : string

`escapeHTML` takes a string of unknown origin and makes it safe for display on a web page by encoding control characters in HTML into their HTML entities.

*Warning*: Do not rely on `escapeHTML` to purge unsafe data from user submitted content. While `escapeHTML` is applied to every binding's contents by default, it should not be your only line of defence against script injection attacks.

    test 'escapeHTML encodes special characters into HTML entities', ->
      equal Batman.escapeHTML("& < > \" '"), "& < > " '"

# Batman.helpers

`Batman.helpers` is a namespace for Batman's helpful string manipulation helpers.

_Note_: Batman's pluralization functions mirror those of Rails' exactly.

## @ordinalize(value : [number|string]) : string

`ordinalize` converts a given integer into an ordinal string for describing position in a list, like 1st, 2nd, or 20th.

    test 'ordinalize converts numbers to their ordinal form', ->
      equal Batman.helpers.ordinalize(1), "1st"
      equal Batman.helpers.ordinalize("2"), "2nd"
      equal Batman.helpers.ordinalize(1002), "1002nd"
      equal Batman.helpers.ordinalize("1003"), "1003rd"
      equal Batman.helpers.ordinalize(-11), "-11th"
      equal Batman.helpers.ordinalize(-1021), "-1021st"

## @singularize(pluralString : string) : string

`singularize` converts the plural form of a word to a singular form.

    test 'singularize converts plural words to singular words', ->
      equal Batman.helpers.singularize("posts"), "post"
      equal Batman.helpers.singularize("octopi"), "octopus"
      equal Batman.helpers.singularize("sheep"), "sheep"
      equal Batman.helpers.singularize("word"), "word"
      equal Batman.helpers.singularize("CamelOctopi"), "CamelOctopus"

## @pluralize(singularString : string) : string

`pluralize` converts the singular form of a word to the plural form.

    test 'pluralize converts plural words to singular words', ->
      equal Batman.helpers.pluralize("post"), "posts"
      equal Batman.helpers.pluralize("octopus"), "octopi"
      equal Batman.helpers.pluralize("sheep"), "sheep"
      equal Batman.helpers.pluralize("words"), "words"
      equal Batman.helpers.pluralize("CamelOctopus"), "CamelOctopi"

## @camelize(string [, lowercaseFirstLetter = false]) : string

`camelize` converts the passed `string` to UpperCamelCase. If the second argument is passed as `true`, then lowerCamelCase is returned.

    test 'camelize returns the CamelCase version of an under_scored word', ->
      equal Batman.helpers.camelize("batman_object"), "BatmanObject"
      equal Batman.helpers.camelize("batman_object", true), "batmanObject"

## @underscore(string) : string

`underscore` returns the underscored version of a CamelCase word.

    test 'underscore converts CamelCase to under_scores', ->
      equal Batman.helpers.underscore("BatmanObject"), "batman_object"

## @capitalize(string) : string

`capitalize` does a word-wise capitalization of a phrase or word.

    test 'capitalize makes the first letter of each word in the string uppercase', ->
      equal Batman.helpers.capitalize("batmanObject"), "BatmanObject"
      equal Batman.helpers.capitalize("batman object"), "Batman Object"
      equal Batman.helpers.capitalize("AlreadyCapitalized"), "AlreadyCapitalized"

## @trim(string) : string

`trim` trims a string getting rid of extra white space around the string or returning an empty string if it is null.

    test 'trim gets rid of space around a string', ->
      equal Batman.helpers.trim("TrimRight "), "TrimRight"
      equal Batman.helpers.trim(" TrimLeft"), "TrimLeft"
      equal Batman.helpers.trim(" TrimBothSides "), "TrimBothSides"
      equal Batman.helpers.trim("AlreadyTrimmed"), "AlreadyTrimmed"

    test 'trim turns a null or undefined string into an empty string', ->
      equal Batman.helpers.trim(null), ""
      equal Batman.helpers.trim(undefined), ""

## @interpolate(stringOrObject, keys) : string

`interpolate` interpolates a string, filling it in with values matching interpolation keys, similar to printf variants.

    test 'interpolate fills in key values globally in a string', ->
      equal Batman.helpers.interpolate("%{field} must be at least %{count} characters in order for %{field} to be valid", count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

    test 'interpolate fills in key values globally in an object-embedded string', ->
      equal Batman.helpers.interpolate({'other': "%{field} must be at least %{count} characters in order for %{field} to be valid"}, count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

    test 'interpolate fills in key values globally in an object-embedded string, embedded by key count', ->
      equal Batman.helpers.interpolate({3: "%{field} must be at least %{count} characters in order for %{field} to be valid"}, count: "3", field: "name"), "name must be at least 3 characters in order for name to be valid"

## @humanize(string) : string

`humanize` reformats a string that has a programmatic meaning (camelcased, underscored, "_id" suffixed) to make human-readable by separating concatenated words and/or getting rid of the "_id" suffix.

    test 'humanize replaces underscores and dashes with empty space', ->
      equal Batman.helpers.humanize('underscored_string'), 'Underscored string'
      equal Batman.helpers.humanize('dash-separated-string'), 'Dash separated string'

    test 'humanize splits camel cased words by empty space', ->
      equal Batman.helpers.humanize('CamelCasedString'), 'Camel cased string'

    test 'humanize title-cases the first word', ->
      equal Batman.helpers.humanize('lower case sentence'), 'Lower case sentence'

    test 'humanize lower-cases all words after the first one', ->
      equal Batman.helpers.humanize('Mixed Case SENTENCE'), 'Mixed case sentence'

    test 'humanize gets rid of _id suffix', ->
      equal Batman.helpers.humanize('identifying_string_id'), 'Identifying string'


Revise\!

Revise\!

# /api/App Components/Batman.Controller

All controllers in a batman.js app extend `Batman.Controller`. `Batman.Controller`s are responsible for executing _actions_ which fire off requests for model data, render views, or redirect to other actions in response to navigation state changes. `Batman.Controller` is inspired by [ActionController](http://guides.rubyonrails.org/action_controller_overview.html), a key component of Ruby on Rails.

## Controller Directory

`Batman.Controller`s are singletons and each controller's instance is available at `@sharedController`. Controller instances are also available at `MyApp.get('controllers')`. You can get the controller instance by the part of its name before "Controller", downcased. For example:

```
Alfred.TodosController.get('sharedController') # => TodosController instance
Alfred.get('controllers.todos')                # => TodosController instance
```

## Actions

Each `Batman.Controller` declares functions which represent actions to be run on a particular page. Typically, these functions will fetch data or render views:

```coffeescript
class Alfred.TodosController extends Batman.Controller
  routingKey: 'todos'
  index: (params) ->
    @set('todos', Alfred.Todo.get('all'))
    # will automatically render Alfred.TodosIndexView with source `todos/index`

  show: (params) ->
    Alfred.Todo.find params.id, (err, record) ->
      throw err if err
      @set('todo', record)
    # will automatically render Alfred.TodosShowView with source `todos/show`
```

Actions are passed a `params` argument, which is an object containing URL parameters. Named route parameters (eg, `todos/:id`) and query string parameters (`?key=value`) are accessible in `params`.

## routingKey and Minification

Each `Batman.Controller` must have a `routingKey`, which is used by the batman.js router. It also makes the app minification-safe. You can set it inside the controller definition:

```coffeescript
class Alfred.TodosController extends Batman.Controller
  routingKey: 'todos'
```

An app will fail to run if a controller's `routingKey` is not defined. To disable this requirement, set `Batman.config.minificationErrors` to false.

## Error Handling

`Batman.Controller` has a built-in structure for handling errors that occur during controller actions. Inside a controller definition, you can map errors to handlers using `@catchError`. Then, you can wrap a storage operation callback with `@errorHandler` to have any errors caught by the defined handlers. If you use `@errorHandler`, errors without declared handlers will be thrown (since they aren't passed to the callback).

```coffeescript
class Alfred.TodosController extends Batman.Controller
  routingKey: 'todos'
  # declare a handler with `@catchError`:
  @catchError Batman.StorageAdapter.NotFoundError, with: 'render404'

  show: (params) ->
    # wrap your storage operation in `@errorHandler`
    App.Todo.find params.id, @errorHandler (record, env) =>
      @set('todo', record)

  # error is passed to the handler
  render404: (error) -> @render(source: "errors/404")
```

Usually, you want to declare error handlers in `MyApp.ApplicationController`, then extend `MyApp.ApplicationController` in your other controllers:

```coffeescript
class Alfred.ApplicationController extends Batman.Controller
  @catchError Batman.StorageAdapter.NotFoundError, with: 'render404'
  # etc...

class Alfred.TodosController extends Alfred.ApplicationController
  # will use ApplicationController's handlers for @errorHandler
```

For a full list of storage errors, see [`Batman.StorageAdapter` Errors](/docs/api/batman.storageadapter_errors.html).

## ::render([options : [Object|boolean]])

`render` is used to control the rendering of the current action. Unless specified otherwise, `Batman.Controller` actions render automatically.

Passing `false` will prevent the current action from rendering the view.

```coffeescript
class Alfred.TodosController extends Batman.Controller
  actionWithNoRender: ->
    @render(false) # no view will be rendered

  actionWithDelayedRender: ->
    setTimeout (-> @render()), 1000 # view rendered after 1 second
    @render(false)
```

By passing an `options` object, you may override the default `view`, `source`, `viewClass` or `yield` block.

```coffeescript
class Alfred.TodosController extends Batman.Controller
  actionWithCustomSource: ->
    @render(source: 'errors/404')

  actionWithCustomYield: ->
    @render(into: 'not-main')
```

For more information on yield blocks, see [`Controller::.defaultRenderYield`](/docs/api/batman.controller.html#prototype_property_defaultrenderyield).

## @beforeAction([options : [string|Object],] filter : [string|Function])

Declares that a function should be executed should be executed before this controller's actions. If any `beforeAction` filter returns `false` or calls [`@redirect`](/docs/api/batman.controller.html#prototype_function_redirect), the controller action won't be executed. `@beforeAction` accepts:

-  `options` representing which action(s) to execute the filter before (optional)
-  `filter`, either a string naming a function _or_ a function to execute.

The `options` argument can take three forms:

 1. `undefined`: this filter should be executed before all actions.
 2. `String`: this filter should be executed before the action named by the string.
 3. `Object`: this filter should be executed before the actions named by an Array at the `only` key in the options object, or before all actions excluding those named by an Array at the `except` key in the options object.

    test "@beforeAction allows declaration of filters to execute before an action", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @beforeAction only: "index", -> results.push "before!"
        index: ->
          results.push "action!"
          @render false

      controller = TestController.get("sharedController")
      controller.dispatch "index"
      equal results[0], "before!"
      equal results[1], "action!"

    test "@beforeAction allows declaration of named filters to execute before an action", ->
      class TodoController extends Batman.Controller
        routingKey: "test"
        @beforeAction only: "show", "fetchTodo"
        fetchTodo: -> @set("todo", {isDone: true})
        show: ->
          @render false

      controller = TodoController.get("sharedController")
      controller.dispatch "show"
      deepEqual controller.get("todo"), {isDone: true}

    test "@beforeAction allows whitelisting or blacklisting filters to execute before an action", ->
      class TodoController extends Batman.Controller
        routingKey: "test"
        @beforeAction only: ["show", "edit"], "fetchTodo"
        @beforeAction except: ["index"], "prepareNewTodo"
        fetchTodo: -> @set "todo", {isDone: true}
        prepareNewTodo: -> @set "newTodo", {isDone: false}
        index: -> @render false
        show: -> @render false

      controller = TodoController.get("sharedController")
      controller.dispatch "show"
      deepEqual controller.get("todo"), {isDone: true}
      deepEqual controller.get("newTodo"), {isDone: false}

    test "@beforeAction allows declaration of filters to execute before all actions", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @beforeAction -> results.push "before!"
        index: ->
          results.push "action!"
          @render false

      controller = TestController.get("sharedController")
      controller.dispatch "index"
      equal results[0], "before!"
      equal results[1], "action!"

## @afterAction([options : [string|Object],] filter : [string|Function])

Declares that a function should be executed after this controller's actions (and after any `render` or `redirect`s). `@afterAction` accepts `options` and `filter` (see [`@beforeAction`](/docs/api/batman.controller.html#class_function_beforeaction)).

    test "@afterAction allows declaration of filters to execute after an action", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @afterAction only: "index", -> results.push "after!"
        index: ->
          results.push "action!"
          @render false

      controller = TestController.get("sharedController")
      controller.dispatch "index"
      equal results[0], "action!"
      equal results[1], "after!"

    test "@afterAction allows declaration of named filters to execute after an action", ->
      result = null

      class TodoController extends Batman.Controller
        routingKey: "test"
        @afterAction only: "create", "notify"
        notify: -> result = "Todo created successfully."
        create: -> @render false

      controller = TodoController.get("sharedController")
      controller.dispatch "create"
      equal result, "Todo created successfully."

    test "@afterAction allows whitelisting or blacklisting filters to execute after an action", ->
      result = null

      class TodoController extends Batman.Controller
        routingKey: "test"
        @afterAction only: ["create", "update"], "notify"
        index: -> @render false
        create: -> @render false
        notify: -> result = "Todo created successfully."

      controller = TodoController.get("sharedController")
      controller.dispatch "index"
      equal result, null
      controller.dispatch "create"
      equal result, "Todo created successfully."

    test "@afterAction allows declaration of filters to execute after all actions", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @afterAction -> results.push "after!"
        index: ->
          results.push "action!"
          @render false

      controller = TestController.get("sharedController")
      controller.dispatch "index"
      equal results[0], "action!"
      equal results[1], "after!"

## ::executeAction(action: string[, params: Object])

Runs the `action` specified, including all applicable `@beforeAction`s and `@afterAction`s. Optionally, `params` can be passed into the new action for processing. If no params are passed, it will default to the params of the current action.

    test "executeAction will run the action and all @beforeAction and @afterAction filters", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @beforeAction only: "index", -> results.push "before!"
        @afterAction only: "index", -> results.push "after!"
        index: ->
          results.push "action!"
          @render false
        other: ->
          @executeAction("index")

      controller = TestController.get("sharedController")
      controller.dispatch "other"
      deepEqual results, ["before!", "action!", "after!"]

## ::redirect(options: [string|Object])

Properly handles the controller filter chain and loads a new page with [`Batman.redirect`](/docs/api/batman.html#class_function_redirect). See [`Batman.redirect`](/docs/api/batman.html#class_function_redirect) for allowed `options`. If `options` is an object, the current controller will be added as the object's `controller` attribute (if it isn't already present), so calling `@redirect({action: "show", id: itemId})` is equivalent to calling `@redirect({action: 'show', id: itemId, controller: 'items'})`.

## ::scrollToHash([hash: string])

`scrollToHash` emulates the native scrolling behaviour of the browser by allowing the URL to link to a specific ID on the page. The code will look for an element with a matching ID to `hash` if it is provided otherwise using the `#` parameter in the URL. If an element is found, the page will scroll to the matching element.

## ::.autoScrollToHash[= true] : boolean

Specifies if the controller should automatically scroll to the element with ID equal to the `hash` parameter. This behaviour emulates the native behaviour of the same nature, but is implemented in Batman so the functionality works after each dispatch (instead of each page refresh) and when Batman is using hash bang routing.

## ::.defaultRenderYield[= 'main'] : string

`defaultRenderYield` specifies which yield (see [`data-yield`](/docs/api/batman.view_bindings.html#data-yield)) a controller should automatically render into if no yield is declared.

## @catchError(errorClasses..., {with : [String|Array]})

Declares that errors which are `instanceof` any of `errorClasses` should be handled by `with` when using [`@errorHandler`](/docs/api/batman.controller.html#class_function_errorhandler). `with` is the name of a prototype function (or an array of names) which will be invoked with the error. See ["Error Handling"](/docs/api/batman.controller.html#error_handling).

## @errorHandler(callback)

Wraps a storage action, passing the first argument (`error`) to error handlers declared with [`@catchError`](/docs/api/batman.controller.html#class_function_catcherror). Callback should take arguments `result, env` (`error` is not present because it was passed to error handlers). See ["Error Handling"](/docs/api/batman.controller.html#error_handling).

# /api/App Components/Batman.Controller

All controllers in a batman.js app extend `Batman.Controller`. `Batman.Controller`s are responsible for executing _actions_ which fire off requests for model data, render views, or redirect to other actions in response to navigation state changes. `Batman.Controller` is inspired by [ActionController](http://guides.rubyonrails.org/action_controller_overview.html), a key component of Ruby on Rails.

## Controller Directory

`Batman.Controller`s are singletons and each controller's instance is available at `@%sharedController`. Controller instances are also available at `App.get('controllers')`. You can get the controller instance by the part of its name before "Controller", downcased. For example:

```
Alfred.TodosController.get('sharedController') # => TodosController instance
Alfred.get('controllers.todos')                # => TodosController instance
```

## Actions

Each `Batman.Controller` should have instance functions which can fetchdata and often render a view (or views):

```coffeescript
class Alfred.TodosController extends Batman.Controller
  routingKey: 'todos'
  index: (params) ->
    @set('todos', Alfred.Todo.get('all'))
    # will automatically render Alfred.TodosIndexView with source `todos/index`

  show: (params) ->
    Alfred.Todo.find params.id, (err, record) ->
      throw err if err
      @set('todo', record)
    # will automatically render Alfred.TodosShowView with source `todos/show`
```

Actions take a `params` argument, which is a JavaScript object containing URL parameters. Named route parameters (eg, `todos/:id`) and query string parameters (`?key=value`) are accessible in `params`.

## routingKey and Minification

All `Batman.Controller` prototypes must have a `routingKey`. It is a string which serves as the controller's name and is used by the batman.js router. It also makes the app minification-safe. You can set it inside the controller definition:

```coffeescript
class Alfred.TodosController extends Batman.Controller
  routingKey: 'todos'
```

An app will fail to run if a controller's `routingKey` is not defined on the prototype. To disable this requirement, set `Batman.config.minificationErrors` to false.

## Error Handling

`Batman.Controller` has a built-in structure for handling errors that occur during controller actions. Inside a controller definition, you can map errors to handlers using `@catchError`. Then, when you make a storage operation (such as reading, saving, or destroying), you can wrap your callback in `@errorHandler` so that, if an error is present, it will be handled by the assigned handler. Errors without declared handlers will be thrown.

```coffeescript
class App.TodosController extends Batman.Controller
  routingKey: 'todos'
  # declare a handler with `@catchError`:
  @catchError Batman.StorageAdapter.NotFoundError, with: 'render404'

  show: (params) ->
    # wrap your storage operation in `@errorHandler`
    App.Todo.find params.id, @errorHandler (record, env) =>
      @set('todo', record)

  # error is passed to the handler
  render404: (error) -> @render(source: "errors/404")
```

For a full list of storage errors, see [`Batman.StorageAdapter` Errors](/docs/api/batman.storageadapter_errors.html).

## ::render([options : [Object|boolean]])

`render` is used to control the rendering of the current action. Unless specified otherwise, `Batman.Controller` actions render automatically.

Passing `false` will prevent the current action from rendering the view.

```coffeescript
class Alfred.TodosController extends Batman.Controller
  nonVisibleAction: ->
    @render(false) # no view will be rendered

  delayedRender: ->
    setTimeout (-> @render()), 1000 # view rendered after 1 second
    @render(false)
```

By passing an `options` object, you may override the default `view`, `source`, `viewClass` or `yield` block.

```coffeescript
class Alfred.TodosController extends Batman.Controller
  customSource: ->
    @render(source: 'errors/404')

  customYield: ->
    @render(into: 'not-main')
```

For more information on yield blocks, see [`Controller::.defaultRenderYield`](/docs/api/batman.controller.html#prototype_property_defaultrenderyield).

## @beforeAction([options : [string|Object],] filter : [string|Function])

Declares that a function should be executed before the body of a controller action. If any `beforeAction` filter returns `false` or calls [`@redirect`](/docs/api/batman.controller.html#prototype_function_redirect), the controller action won't be executed. `@beforeAction` accepts:

-  `options` representing which action(s) to execute the filter before (optional)
-  `filter`, either a string naming a function _or_ a function to execute.

The `options` argument can take three forms:

 1. `undefined`: this filter should be executed before all actions.
 2. `String`: this filter should be executed before the action named by the string.
 3. `Object`: this filter should be executed before the actions named by an Array at the `only` key in the options object, or before all actions excluding those named by an Array at the `except` key in the options object.

    test "@beforeAction allows declaration of filters to execute before an action", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @beforeAction only: "index", -> results.push "before!"
        index: ->
          results.push "action!"
          @render false

      controller = TestController.get("sharedController")
      controller.dispatch "index"
      equal results[0], "before!"
      equal results[1], "action!"

    test "@beforeAction allows declaration of named filters to execute before an action", ->
      class TodoController extends Batman.Controller
        routingKey: "test"
        @beforeAction only: "show", "fetchTodo"
        fetchTodo: -> @set("todo", {isDone: true})
        show: ->
          @render false

      controller = TodoController.get("sharedController")
      controller.dispatch "show"
      deepEqual controller.get("todo"), {isDone: true}

    test "@beforeAction allows whitelisting or blacklisting filters to execute before an action", ->
      class TodoController extends Batman.Controller
        routingKey: "test"
        @beforeAction only: ["show", "edit"], "fetchTodo"
        @beforeAction except: ["index"], "prepareNewTodo"
        fetchTodo: -> @set "todo", {isDone: true}
        prepareNewTodo: -> @set "newTodo", {isDone: false}
        index: -> @render false
        show: -> @render false

      controller = TodoController.get("sharedController")
      controller.dispatch "show"
      deepEqual controller.get("todo"), {isDone: true}
      deepEqual controller.get("newTodo"), {isDone: false}

    test "@beforeAction allows declaration of filters to execute before all actions", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @beforeAction -> results.push "before!"
        index: ->
          results.push "action!"
          @render false

      controller = TestController.get("sharedController")
      controller.dispatch "index"
      equal results[0], "before!"
      equal results[1], "action!"

## @afterAction([options : [string|Object],] filter : [string|Function])

Declares that a function should be executed after the body of a controller action (and therefore after any `render` or `redirect`s). `@afterAction` accepts `options` and `filter` (see [`@beforeAction`](/docs/api/batman.controller.html#class_function_beforeaction)).

    test "@afterAction allows declaration of filters to execute after an action", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @afterAction only: "index", -> results.push "after!"
        index: ->
          results.push "action!"
          @render false

      controller = TestController.get("sharedController")
      controller.dispatch "index"
      equal results[0], "action!"
      equal results[1], "after!"

    test "@afterAction allows declaration of named filters to execute after an action", ->
      result = null

      class TodoController extends Batman.Controller
        routingKey: "test"
        @afterAction only: "create", "notify"
        notify: -> result = "Todo created successfully."
        create: -> @render false

      controller = TodoController.get("sharedController")
      controller.dispatch "create"
      equal result, "Todo created successfully."

    test "@afterAction allows whitelisting or blacklisting filters to execute after an action", ->
      result = null

      class TodoController extends Batman.Controller
        routingKey: "test"
        @afterAction only: ["create", "update"], "notify"
        index: -> @render false
        create: -> @render false
        notify: -> result = "Todo created successfully."

      controller = TodoController.get("sharedController")
      controller.dispatch "index"
      equal result, null
      controller.dispatch "create"
      equal result, "Todo created successfully."

    test "@afterAction allows declaration of filters to execute after all actions", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @afterAction -> results.push "after!"
        index: ->
          results.push "action!"
          @render false

      controller = TestController.get("sharedController")
      controller.dispatch "index"
      equal results[0], "action!"
      equal results[1], "after!"

## ::executeAction(action: string[, params: Object])

Runs the `action` specified, including all applicable `@beforeAction`s and `@afterAction`s. Optionally, `params` can be passed into the new action for processing. If no params are passed, it will default to the params of the current action.

    test "executeAction will run the action and all @beforeAction and @afterAction filters", ->
      results = []

      class TestController extends Batman.Controller
        routingKey: "test"
        @beforeAction only: "index", -> results.push "before!"
        @afterAction only: "index", -> results.push "after!"
        index: ->
          results.push "action!"
          @render false
        other: ->
          @executeAction("index")

      controller = TestController.get("sharedController")
      controller.dispatch "other"
      deepEqual results, ["before!", "action!", "after!"]

## ::redirect(options: [string|Object])

Properly handles the controller filter chain and loads a new page with [`Batman.redirect`](/docs/api/batman.html#class_function_redirect). See [`Batman.redirect`](/docs/api/batman.html#class_function_redirect) for allowed `options`. If `options` is an object, the current controller will be added as the object's `controller` attribute (if it isn't already present), so calling `@redirect({action: "show", id: itemId})` is equivalent to calling `@redirect({action: 'show', id: itemId, controller: 'items'})`.

## ::scrollToHash([hash: string])

`scrollToHash` emulates the native scrolling behaviour of the browser by allowing the URL to link to a specific ID on the page. The code will look for an element with a matching ID to `hash` if it is provided otherwise using the `#` parameter in the URL. If an element is found, the page will scroll to the matching element.

## ::.autoScrollToHash[= true] : boolean

Specifies if the controller should automatically scroll to the element with ID equal to the `hash` parameter. This behaviour emulates the native behaviour of the same nature, but is implemented in Batman so the functionality works after each dispatch (instead of each page refresh) and when Batman is using hash bang routing.

## ::.defaultRenderYield[= 'main'] : string

`defaultRenderYield` specifies which yield (see [`data-yield`](/docs/api/batman.view_bindings.html#data-yield)) a controller should automatically render into if no yield is declared.

## @catchError(errorClasses..., {with : [String|Array]})

Declares that errors which are `instanceof` any of `errorClasses` should be handled by `with` when using [`@errorHandler`](/docs/api/batman.controller.html#class_function_errorhandler). `with` is the name of a prototype function (or an array of names) which will be invoked with the error. See ["Error Handling"](/docs/api/batman.controller.html#error_handling).

## @errorHandler(callback)

Wraps a storage action, passing the first argument (`error`) to error handlers declared with [`@catchError`](/docs/api/batman.controller.html#class_function_catcherror). Callback should take arguments `result, env` (`error` is not present because it was passed to error handlers). See ["Error Handling"](/docs/api/batman.controller.html#error_handling).


added links

added links

# cnl tests
* Parent: [Index][Parent]

## [Engine Test][EngineTest]

    test = require './engine'
    test (require '../engine'), 'Engine'

## [Element Test][ElementTest]

    test = require './element'
    test (require '../element'), 'Element'
    test (require '../component'), 'Component'

## [Shape Test][ShapeTest]

    test = require './shape'
    test (require '../shape'), 'Shape'

## [Pen Test][PenTest]

    test = require './pen'
    test (require '../pen'), 'Pen'

## [Style Test][StyleTest]

    test = require './style'
    test (require '../style'), 'Style'

[Parent]: ../index.litcoffee

[Engine]: ../engine.litcoffee
[Element]: ../element.litcoffee
[Shape]: ../shape.litcoffee
[Pen]: ../pen.litcoffee
[Style]: ../style.litcoffee

[EngineTest]: ./engine.litcoffee
[ElementTest]: ./element.litcoffee
[ShapeTest]: ./shape.litcoffee
[PenTest]: ./pen.litcoffee
[StyleTest]: ./style.litcoffee

# ecl tests
* Parent: [Index][Parent]

## [Engine Test][EngineTest]

    test = require './engine'
    test (require '../engine'), 'Engine'

## [Element Test][ElementTest]

    test = require './element'
    test (require '../element'), 'Element'
    test (require '../component'), 'Component'

## [Shape Test][ShapeTest]

    test = require './shape'
    test (require '../shape'), 'Shape'

## [Pen Test][PenTest]

    test = require './pen'
    test (require '../pen'), 'Pen'

## [Style Test][StyleTest]

    test = require './style'
    test (require '../style'), 'Style'

[Engine]: ../evented.litcoffee
[EngineTest]: ./evented.litcoffee
[Parent]: ../index.litcoffee


sucessful login redirects to directory

sucessful login redirects to directory

# Application Routes

The purpose of a route is to connect URL resources with underlying code.  I
also do explicit data normalization (input -> dictionary to pass to method/dict
from method -> view) at this stage.

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

Also note, app.models contians:

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

    module.exports = (app) ->

## Function cache(res, seconds)

Set cache headers on this response.

Usage: `cache(res,86400)`

      cache = (res,seconds) ->
        res.header 'Cache-control', 'Cache-Control:public, max-age='+seconds

## Function nocache(res)

Set uncacheable headers on this response

Usage: `nocache(res)`

      nocache = (res) ->
        res.header 'Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'

## GET /

Render the main index page.

      app.get '/', (req, res) ->
        cache res, 2592000
        res.redirect '/page/index'

## GET /page/:name

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

      app.get '/page/:name', (req, res) ->
        app.models.page.get req.params.name, (err, page) ->
          if err
            console.error 'page '+req.params.name+' not found in db, showing 500'
            nocache res
            res.render 'error/500'
          else
            cache res, 86400
            res.render 'page/dynamic', page

## POST /page/_post

Post a page into the database

WARNING no security! .. then again, it doesn't read input at the moment either.
<!-- ' -->

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

## GET /login

Render static pages at /view/login/index.html

And never cache the login page, it breaks csrf

      app.get '/login', (req, res) ->
        nocache res
        res.render 'login'

## POST /login/_post

Do Login action, and redirect as appropriate.

      app.post '/login/_post', (req, res) ->
        nocache res
        require('../login').checkUser req.body.username, req.body.password, (user, securityLevel) ->
          console.info 'login callback = '+user+', '+securityLevel
          req.session.user = user
          req.session.securityLevel = securityLevel
          res.cookie 'nocache', '1'
          if securityLevel
            res.redirect '/directory'
          else
            res.redirect '/login'

## GET /login/clear

Logout from the website by clearing the session

      app.get '/login/clear', (req, res) ->
        nocache res
        req.session.destroy (err) ->
          res.clearCookie 'nocache'
          res.redirect '/login'

## GET /directory

Download PDF to browser

      app.get '/directory', (req, res) ->
        nocache res
        if req.session.securityLevel
          request = require 'request'
          request app.directory_url, (err, cbres, data) ->
            if err or cbres.statusCode != 200
              res.render 500, 'Error retrieving full directory'
            else
              dir = JSON.parse data
              page =
                name: 'Full Directory'
                title: 'Full Directory'
                directory: dir.data
                helpers:
                  month: (num) ->
                    return [null, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][num]
              res.render 'directory', page
        else
          res.redirect '/login'

## GET /directory/directory.pdf

Download PDF to browser

      app.get '/directory/directory.pdf', (req, res) ->
        nocache res
        if req.session.securityLevel
          res.download 'protected/directory.pdf'
        else
          res.redirect '/login'

## Copying

This software is released under the ISC License.

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

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

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

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

# Application Routes

The purpose of a route is to connect URL resources with underlying code.  I
also do explicit data normalization (input -> dictionary to pass to method/dict
from method -> view) at this stage.

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

Also note, app.models contians:

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

    module.exports = (app) ->

## Function cache(res, seconds)

Set cache headers on this response.

Usage: `cache(res,86400)`

      cache = (res,seconds) ->
        res.header 'Cache-control', 'Cache-Control:public, max-age='+seconds

## Function nocache(res)

Set uncacheable headers on this response

Usage: `nocache(res)`

      nocache = (res) ->
        res.header 'Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'

## GET /

Render the main index page.

      app.get '/', (req, res) ->
        cache res, 2592000
        res.redirect '/page/index'

## GET /page/:name

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

      app.get '/page/:name', (req, res) ->
        app.models.page.get req.params.name, (err, page) ->
          if err
            console.error 'page '+req.params.name+' not found in db, showing 500'
            nocache res
            res.render 'error/500'
          else
            cache res, 86400
            res.render 'page/dynamic', page

## POST /page/_post

Post a page into the database

WARNING no security! .. then again, it doesn't read input at the moment either.
<!-- ' -->

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

## GET /login

Render static pages at /view/login/index.html

And never cache the login page, it breaks csrf

      app.get '/login', (req, res) ->
        nocache res
        res.render 'login'

## POST /login/_post

Do Login action, and redirect as appropriate.

      app.post '/login/_post', (req, res) ->
        nocache res
        require('../login').checkUser req.body.username, req.body.password, (user, securityLevel) ->
          console.info 'login callback = '+user+', '+securityLevel
          req.session.user = user
          req.session.securityLevel = securityLevel
          res.cookie 'nocache', '1'
          if securityLevel
            res.redirect '/'
          else
            res.redirect '/login'

## GET /login/clear

Logout from the website by clearing the session

      app.get '/login/clear', (req, res) ->
        nocache res
        req.session.destroy (err) ->
          res.clearCookie 'nocache'
          res.redirect '/login'

## GET /directory

Download PDF to browser

      app.get '/directory', (req, res) ->
        nocache res
        if req.session.securityLevel
          request = require 'request'
          request app.directory_url, (err, cbres, data) ->
            if err or cbres.statusCode != 200
              res.render 500, 'Error retrieving full directory'
            else
              dir = JSON.parse data
              page =
                name: 'Full Directory'
                title: 'Full Directory'
                directory: dir.data
                helpers:
                  month: (num) ->
                    return [null, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][num]
              res.render 'directory', page
        else
          res.redirect '/login'

## GET /directory/directory.pdf

Download PDF to browser

      app.get '/directory/directory.pdf', (req, res) ->
        nocache res
        if req.session.securityLevel
          res.download 'protected/directory.pdf'
        else
          res.redirect '/login'

## Copying

This software is released under the ISC License.

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

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

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

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


Add the zone selection output

Add the zone selection output

Container Manager
=================

This manager defines the visibility of the individual display
containers. It is driven chiefly by the display selector.

Like all managers, it is run on a jQuery callback so that it executes
after the DOM has already been loaded.

    $ ->

We bind the selected display's equivalence to the string 'default'
for the visibility of the default container.

        SelectedDisplay.map( (x) -> x is 'default' )
                       .assign $('#default'), 'toggle'

It is left as an exercise to the reader to guess how the rest of these go...

        SelectedDisplay.map( (x) -> x is 'no-entry' )
                       .assign $('#no-entry'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'kill-origin' )
                       .assign $('#kill-origin'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'match-display' )
                       .assign $('#match-display'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'match-sched' )
                       .assign $('#match-sched'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'league-state' )
                       .assign $('#league-state'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'knockout-state' )
                       .assign $('#knockout-state'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'next-match' )
                       .assign $('#next-match'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'day-sched' )
                       .assign $('#day-sched'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'layout' )
                       .assign $('#layout'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'arena-entrance' )
                       .assign $('#arena-entrance'), 'toggle'
        SelectedDisplay.map( (x) -> /zone-/.match x )
                       .assign $('#zone'), 'toggle'

Selected Zone
-------------

We get the selected zone from the selected display.

        window.SelectedZone =
            SelectedDisplay.map( (x) -> /zone-([0-3])/.exec x )
                           .filter( (x) -> x? )
                           .map( (x) -> parseInt(x[1]) )
                           .toProperty(0)
                           .skipDuplicates()

Container Manager
=================

This manager defines the visibility of the individual display
containers. It is driven chiefly by the display selector.

Like all managers, it is run on a jQuery callback so that it executes
after the DOM has already been loaded.

    $ ->

We bind the selected display's equivalence to the string 'default'
for the visibility of the default container.

        SelectedDisplay.map( (x) -> x is 'default' )
                       .assign $('#default'), 'toggle'

It is left as an exercise to the reader to guess how the rest of these go...

        SelectedDisplay.map( (x) -> x is 'no-entry' )
                       .assign $('#no-entry'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'kill-origin' )
                       .assign $('#kill-origin'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'match-display' )
                       .assign $('#match-display'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'match-sched' )
                       .assign $('#match-sched'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'league-state' )
                       .assign $('#league-state'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'knockout-state' )
                       .assign $('#knockout-state'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'next-match' )
                       .assign $('#next-match'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'day-sched' )
                       .assign $('#day-sched'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'zone' )
                       .assign $('#zone'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'layout' )
                       .assign $('#layout'), 'toggle'
        SelectedDisplay.map( (x) -> x is 'arena-entrance' )
                       .assign $('#arena-entrance'), 'toggle'


<script> with absolute src fixed on win

<script> with absolute src fixed on win

# script

    'use strict'

    fs = require 'fs-extra'
    os = require 'os'
    utils = require 'src/utils'
    pathUtils = require 'path'
    {getFilePath} = require './links'

    OUT_DIR = 'build/scripts/'
    DEFAULT_FILE_EXT = '.js'

    isCoffee = (path) -> /\.(?:coffee|litcoffee|coffee\.md)$/.test(path)

    module.exports = (File) -> (file) ->
        if file instanceof File.Iterator
            return

        scripts = []

        for tag, i in file.node.queryAll('script')
            omit = false
            for attr of tag.attrs
                if attr not in ['src', 'href', 'filename']
                    omit = true
                    break
            if omit
                continue

            tag.parent = null

            {src} = tag.attrs
            src ||= tag.attrs.href

            if src
                # file
                filename = getFilePath File, file, src
                script = fs.readFileSync filename, 'utf-8'
            else
                # tag
                script = tag.stringifyChildren()
                {filename} = tag.attrs
                if isCoffee(filename)
                    script = "`module.exports = function(){`\n\n#{script}\n\n`};`"
                else
                    script = "module.exports = function(){\n\n#{script}\n\n};"

            filePath = file.path

            # support win absolute paths
            if pathUtils.isAbsolute(filePath)
                filePath = encodeURIComponent filePath

            name = "#{filePath}##{i}"
            extname = (filename and pathUtils.extname(filename)) or DEFAULT_FILE_EXT
            path = pathUtils.join OUT_DIR, name + extname
            fs.outputFileSync path, script
            File.Scripts.scripts[name] = require path
            scripts.push name

        file.scripts = new File.Scripts file, scripts

        return

# Glossary

- [script](#neftscript)

# script

    'use strict'

    fs = require 'fs-extra'
    os = require 'os'
    utils = require 'src/utils'
    pathUtils = require 'path'
    {getFilePath} = require './links'

    OUT_DIR = 'build/scripts/'
    DEFAULT_FILE_EXT = '.js'

    isCoffee = (path) -> /\.(?:coffee|litcoffee|coffee\.md)$/.test(path)

    module.exports = (File) -> (file) ->
        if file instanceof File.Iterator
            return

        scripts = []

        for tag, i in file.node.queryAll('script')
            omit = false
            for attr of tag.attrs
                if attr not in ['src', 'href', 'filename']
                    omit = true
                    break
            if omit
                continue

            tag.parent = null

            {src} = tag.attrs
            src ||= tag.attrs.href

            if src
                # file
                filename = getFilePath File, file, src
                script = fs.readFileSync filename, 'utf-8'
            else
                # tag
                script = tag.stringifyChildren()
                {filename} = tag.attrs
                if isCoffee(filename)
                    script = "`module.exports = function(){`\n\n#{script}\n\n`};`"
                else
                    script = "module.exports = function(){\n\n#{script}\n\n};"

            name = "#{file.path}##{i}"
            extname = (filename and pathUtils.extname(filename)) or DEFAULT_FILE_EXT
            path = pathUtils.join OUT_DIR, name + extname
            fs.outputFileSync path, script
            File.Scripts.scripts[name] = require path
            scripts.push name

        file.scripts = new File.Scripts file, scripts

        return

# Glossary

- [script](#neftscript)


server socket

server socket

    _self = this

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

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

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

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

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

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

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

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

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

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

      return params

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

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

      return params

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

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

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

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

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

     _onError: (e) -> console.log e

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

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

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

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

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

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

     ports:
      WorkerPort: WorkerPort
      SocketPort: SocketPort
      ServerSocketPort: ServerSocketPort

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

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

    _self = this

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

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

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

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

    class Port
     constructor: ->
      @handlers = {}
      @callsCache = {}
      @callsCounter = 0

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

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

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

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

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

      return params

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

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

      return params

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

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

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

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

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

     _onError: (e) -> console.log e

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

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

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

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

     ports:
      WorkerPort: WorkerPort
      SocketPort: SocketPort

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

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


remove autoplay on noembeds

remove autoplay on noembeds

# Exclusive posts

`PostFormSchema` is used to generate autoforms, we'll submit to methods and fill
in the reset of the data.

    @PostFormSchema = new SimpleSchema
      message:
        type: String
        max: 200
        autoform:
          rows: 3
      link:
        type: String
        regEx: SimpleSchema.RegEx.Url
        optional: yes

    PostSchema = new SimpleSchema
      name:
        type: String
      message:
        type: String
        max: 200
      link:
        type: String
        regEx: SimpleSchema.RegEx.Url
        optional: yes
      html:
        type: String
        optional: yes
      timeCreated:
        type: Date
        autoValue: ->
          if this.isInsert
            return moment.utc().toDate()
          else
            this.unset()
      connectionId:
        type: String
        max: 20
      ticketNumber:
        type: Number
        index: 1

    @Posts = new Meteor.Collection 'posts'
    Posts.attachSchema PostSchema

## Posts Meteor Methods

    Meteor.methods
      postMessage: (doc) ->

        check doc, PostFormSchema

Return on the client

        return if Meteor.isClient

Get the queueMeta

        queueMeta = QueueMeta.findOne()
        throw new Meteor.Error 'queueMeta', 'Could not find queueMeta.' if not queueMeta?

Check that the connection is theOnlyConnectionAllowedIn

        isAllowedIn = this.connection.id is queueMeta.theOnlyConnectionAllowedIn?.connectionId
        throw new Meteor.Error 'no-access', "You're not allowed to do that until you're in." if not isAllowedIn

Check that the connection hasn't already posted a message

        throw new Meteor.Error 'dup-post', "You're only allowed to post once." if queueMeta.hasCurrentConnectionPosted

Check that the connection hasn't expired

        throw new Meteor.Error 'expired', "You're time expired, post faster." if queueMeta.timeCurrentTicketExpires < moment.utc().toDate()

Mark queueMeta as hasCurrentConnectionPosted

        criteria = _id: queueMeta._id
        update = $set: hasCurrentConnectionPosted: yes

Try to get noembed content

        callback = (error, result) ->
          console.log error if error?
          if not error? and result?.data?.html? then html = result.data.html

Post message

          post =
            name: queueMeta.theOnlyConnectionAllowedIn.name
            message: doc.message
            link: doc.link
            connectionId: queueMeta.theOnlyConnectionAllowedIn.connectionId
            ticketNumber: queueMeta.theOnlyConnectionAllowedIn.ticketNumber

          if html?
            _.extend post, html: html
            _.extend update.$set, html: html
          else
            _.extend update, $unset: html: 'unset'

          QueueMeta.update criteria, update
          Posts.insert post

        if doc.link?
          getUrl = "http://noembed.com/embed?url=#{doc.link}"
          HTTP.get getUrl, callback
        else
          callback()

# Exclusive posts

`PostFormSchema` is used to generate autoforms, we'll submit to methods and fill
in the reset of the data.

    @PostFormSchema = new SimpleSchema
      message:
        type: String
        max: 200
        autoform:
          rows: 3
      link:
        type: String
        regEx: SimpleSchema.RegEx.Url
        optional: yes

    PostSchema = new SimpleSchema
      name:
        type: String
      message:
        type: String
        max: 200
      link:
        type: String
        regEx: SimpleSchema.RegEx.Url
        optional: yes
      html:
        type: String
        optional: yes
      timeCreated:
        type: Date
        autoValue: ->
          if this.isInsert
            return moment.utc().toDate()
          else
            this.unset()
      connectionId:
        type: String
        max: 20
      ticketNumber:
        type: Number
        index: 1

    @Posts = new Meteor.Collection 'posts'
    Posts.attachSchema PostSchema

## Posts Meteor Methods

    Meteor.methods
      postMessage: (doc) ->

        check doc, PostFormSchema

Return on the client

        return if Meteor.isClient

Get the queueMeta

        queueMeta = QueueMeta.findOne()
        throw new Meteor.Error 'queueMeta', 'Could not find queueMeta.' if not queueMeta?

Check that the connection is theOnlyConnectionAllowedIn

        isAllowedIn = this.connection.id is queueMeta.theOnlyConnectionAllowedIn?.connectionId
        throw new Meteor.Error 'no-access', "You're not allowed to do that until you're in." if not isAllowedIn

Check that the connection hasn't already posted a message

        throw new Meteor.Error 'dup-post', "You're only allowed to post once." if queueMeta.hasCurrentConnectionPosted

Check that the connection hasn't expired

        throw new Meteor.Error 'expired', "You're time expired, post faster." if queueMeta.timeCurrentTicketExpires < moment.utc().toDate()

Mark queueMeta as hasCurrentConnectionPosted

        criteria = _id: queueMeta._id
        update = $set: hasCurrentConnectionPosted: yes

Try to get noembed content

        callback = (error, result) ->
          console.log error if error?
          if not error? and result?.data?.html? then html = result.data.html

Post message

          post =
            name: queueMeta.theOnlyConnectionAllowedIn.name
            message: doc.message
            link: doc.link
            connectionId: queueMeta.theOnlyConnectionAllowedIn.connectionId
            ticketNumber: queueMeta.theOnlyConnectionAllowedIn.ticketNumber

          if html?
            _.extend post, html: html
            _.extend update.$set, html: html
          else
            _.extend update, $unset: html: 'unset'

          QueueMeta.update criteria, update
          Posts.insert post

        if doc.link?
          getUrl = "http://noembed.com/embed?url=#{doc.link}&autoplay=1&auto_play=1"
          HTTP.get getUrl, callback
        else
          callback()


Target is optional.

Target is optional.

A `subject_metric` is valid if it has a valid target.

    @validateSubjectMetric = (subjectMetric) ->

First, check that the subject metric has the right structure and is a valid
item.

      check subjectMetric, Match.ObjectIncluding
        target: Match.Optional(Number)

Next, check that the target is zero or greater.

      errors = {}
      if subjectMetric.target < 0
        errors.target = TAPi18n.__ "Please provide a positive target value"
      return errors

A `subject_metric` is valid if it has a valid target.

    @validateSubjectMetric = (subjectMetric) ->

First, check that the subject metric has the right structure and is a valid
item.

      check subjectMetric, Match.ObjectIncluding
        target: Number

Next, check that the target is zero or greater.

      errors = {}
      if subjectMetric.target < 0
        errors.target = TAPi18n.__ "Please provide a positive target value"
      return errors