Added a couple lvars

Added a couple lvars

We always start with the JavaScript-like magic scoping mojo!

    ((root) ->
      root = this
      _ = root._ || require 'underscore'
      F = {}

Non-determinism
---------------

Non-deterministic functions are functions that can have more (or less) than one result.  As known from logic, a binary relation xRy (where x ∈ X, y ∈ Y) can be represented by a *function*  `X -> PowerSet{Y}`. As usual in computer science, we interpret the set `PowerSet{Y}` as a multi-set (realized as a regular scheme list). Compare with SQL, which likewise uses multisets and sequences were sets are properly called for. Also compare with [Wadler's "representing failure as a list of successes."](http://citeseer.uark.edu:8080/citeseerx/showciting;jsessionid=FF5F5EAA9D94B1A8618C49C37451D762?cid=377301).

Thus, we represent a 'relation' (aka `non-deterministic function') as a regular CoffeeScript function that returns an array of possible results.

First, we define two primitive non-deterministic functions; one of them yields no result whatsoever for any argument; the other merely returns its argument as the sole result.

    F.succeed = (result) -> [result]
    F.fail = _.always []

We build more complex non-deterministic functions by combining the existing ones with the help of the following two combinators.

`(disjunction l r)` returns all the results of `l` and all the results of `r`, or returns no results only if neither `l` nor `r` returned any.  In that sense, it is analogous to the logical disjunctionunction.

    disjunction = (l, r) ->
      (x) -> _.cat(l(x), r(x))

`(conjunction l r)` looks like a functional composition of `r` and `l`.  Only `(l x)` may return several results, so we have to apply `r` to each of them.  Obviously `(conjunction fail f)` and `(conjunction f fail)` are both equivalent to `fail`: they return no results, ever. It that sense, `conjunction` is analogous to the logical conjunction.

    conjunction = (l, r) ->
      (x) -> _.mapcat(l(x), r)

JavaScript allows you to pass any number of arguments to a function.  Therefore, we can make a function `disj` that takes any number of arguments and performs a logical disjunction over each.  This makes this version of Sokuza Kanren a bit more flexible in that it allows you to nest any number of relations therein.

    F.disj = () ->
      return F.fail if _.isEmpty(arguments)
      disjunction(_.first(arguments),
	              F.disj.apply(this, _.rest(arguments)))

Likewise, the `conj` function accepts any number of clauses, nit just two as in the original Sokuza.

    F.conj = () ->
      clauses = _.toArray(arguments)
      return F.succeed if _.isEmpty(clauses)
      return _.first(clauses) if _.size(clauses) is 1
      conjunction(_.first(clauses),
                  (s) -> F.conj.apply(null, _.rest(clauses))(s))

And that's all that there is for the built-in non-deterministic functions.

# Knowledge representation
# ------------------------

One may think of regular variables as "certain knowledge": they give  names to definite values.  A logic variable then stands for "improvable ignorance".  An unbound logic variable represents no  knowledge at all; in other words, it represents the result of a measurement *before* we have done the measurement. A logic variable may be associated with a definite value, like 10. That means definite knowledge.  A logic variable may be associated with a semi-definite value, like `[$X]` where `$X` is an unbound variable. We know something about the original variable: it is associated with the array of one element.  We can't say though what that element is. A logic variable can be associated with another, unbound logic variable. In that case, we still don't know what precisely the original variable stands for. However, we can say that it represents the same thing as the other variable. So, our uncertainty is reduced.

I've chosen to represent logic variables with their own simple type.

    class LVar
      constructor: (@name) ->

I've also created a couple of helper functions for creating new variables and checking "variableness" to decouple the representation a bit.

    F.lvar = (name) -> new LVar(name)
    F.isLVar = (v) -> (v instanceof LVar)

I implement associations of logic variables and their values (aka, *bindings*) as a class holding a simple JavaScript object.  One may say that a substitution represents our current knowledge of the world.

    class Bindings
      constructor: (seed = {}) ->
        @binds = _.merge({}, seed)

There are actually two ways of implementing substitutions as associative list. If the variable `$x` is associated with `$y` and `$y` is associated with `1`, we could represent this knowledge as `{$x: 1, $y 1}`. It is easy to lookup the value associated with the variable then, via a simple access. OTH, if we have the binding `{$x:  $y}` and we wish to add the bindings of `$y` to `1`, we have to make rearrangements so to produce `{$x: 1, $y 1}`, or make the traversal on lookup (which we defer to `find` below).

However, if we use an association-list-like structure (arrays of pairs) then we can just record the associations as we learn them, without modifying the previous ones. If originally we knew `[[$x, $y]]` and later we learned that `$y` is associated with `1`, we can simply prepend the latter association, obtaining `[[$y, 1], [$x, $y]]`. So, adding new knowledge becomes fast. The lookup procedure becomes more complex though, as we have to chase the chains of variables. To obtain the value associated with `$x` in the latter substitution, we first lookup `$x`, obtain `$y` (another logic variable), then lookup `$y` finally obtaining `1`.  I prefer the object-representation since it's more idiomatic, but using an a-list provides an intuitively incremental way of representing knowledge: it is easier to backtrack if we later find out our knowledge leads to a contradiction.

      extend: (lvar, value) ->
        o = {}
        o[lvar.name] = value
        new Bindings(_.merge(@binds, o))
      has: (lvar) ->
        @binds.hasOwnProperty(lvar.name)

Find the value associated with `lvar` in the `Bindings` instance.  Return `lvar` itself if it is unbound. In miniKanren, this function is called `walk`.

    lookup: (lvar) ->
        return lvar if !F.isLVar(lvar)
        return this.lookup(@binds[lvar.name]) if this.has(lvar)
        lvar

Starting with an empty binding is akin to saying that we start with zero knowledge.

    F.ignorance = new Bindings()

For convenience I'll add a couple of readily accessible logic variables:

    F.$x = F.lvar("x")
    F.$y = F.lvar("y")

As mentioned, because we overwrite bindings as we discover them the lookup logic is a little pernicious in the face of logic variables bound to other logic variables.

    find = (v, bindings) ->
      lvar = bindings.lookup(v)
      return lvar if F.isLVar(v)
      if _.isArray(lvar)
        if _.isEmpty(lvar)
          return lvar
        else
          return _.cons(find(_.first(lvar), bindings),
                        find(_.rest(lvar), bindings))
      lvar

# Unification
# -----------

Unification is the process of improving knowledge: or, the process of measurement. That measurement may uncover a contradiction though (things are not what we thought them to be). To be precise, the unification is the statement that two terms are the same. For example, unification of `1` and `1` is successful -- `1` is indeed the same as `1`. That doesn't add however to our knowledge of the world. If the logic variable `$x` is associated with `1` in the current bindings, the unification of `$x` with `2` yields a contradiction (the new measurement is not consistent with the previous measurements / hypotheses). Unification of an unbound logic variable `$x` and `1` improves our knowledge: the "measurement" found that `$x` is actually `1`. We record that fact in the new substitution.

Return the new bindings, or `null` on contradiction:

    F.unify = (l, r, bindings) ->

Find out what `l` actually is given our knowledge contained in `bindings`:

      t1 = bindings.lookup(l)

Find out what `r` actually is given our knowledge contained in `bindings`:

      t2 = bindings.lookup(r)

If `l` and `r` are the same; no new knowledge:

      if _.isEqual(t1, t2)
        return bindings

`l` is an unbound variable:

      if F.isLVar(t1)
        return bindings.extend(t1, t2)

`r` is an unbound variable:

      if F.isLVar(t2)
        return bindings.extend(t2, t1)

If t1 is a pair, so must be `r`.  This means that I can only unify arrays of nested arrays bottoming out on values or objects.  I cannot unify objects at the moment.

      if _.isArray(t1) && _.isArray(t2)
        bindings = F.unify(_.first(t1), _.first(t2), bindings)
        bindings = if (bindings isnt null) then F.unify(_.rest(t1), _.rest(t2),   bindings) else bindings
        return bindings
      return null

# Operational logic
# -----------------

Now we can combine non-deterministic functions (Part 1) and the representation of knowledge (Part 2) into a logic system.  We introduce a `goal` -- a non-deterministic function that takes a substitution and produces 0, 1 or more other bindings (new knowledge). In case the goal produces 0 bindings, we say that the goal failed. We will call any result produced by the goal an "outcome".

The functions `succeed` and `fail` defined earlier are obviously  goals.  The latter is the failing goal. OTH, `succeed` is the trivial successful goal, a tautology that doesn't improve our knowledge of the world. We can now add another primitive goal, the result of a "measurement".  The quantum-mechanical connotations of "the measurement" must be obvious by now.

    F.goal = (l, r) ->
      (bindings) ->
        result = F.unify(l, r, bindings)
        return F.succeed(result) if result isnt null
        return F.fail(bindings)

We also need a way to 'run' a goal, to see what knowledge we can obtain starting from sheer ignorance

    F.run = (goal) ->
      goal(F.ignorance)

# Logic programs
# --------------

We can build more complex goals using lambda-abstractions and previously defined combinators, `conj` and `disj`.  For example, we can define the function `choice` such that `choice(t1, array)` is a goal that succeeds if `t1` is an element of `array`.

    F.choice = ($v, list) ->
      return F.fail if _.isEmpty(list)

      F.disj(F.goal($v, _.first(list)),
                    F.choice($v, _.rest(list)))

The name `choice` should evoke [The Axiom of Choice](http://en.wikipedia.org/wiki/Axiom_of_choice)...

# Exports and sundries
# --------------------

	  if module?
	    module.exports = F
	  else
	    root.F = F

	)(this)

We always start with the JavaScript-like magic scoping mojo!

    ((root) ->
      root = this
      _ = root._ || require 'underscore'
      F = {}

Non-determinism
---------------

Non-deterministic functions are functions that can have more (or less) than one result.  As known from logic, a binary relation xRy (where x ∈ X, y ∈ Y) can be represented by a *function*  `X -> PowerSet{Y}`. As usual in computer science, we interpret the set `PowerSet{Y}` as a multi-set (realized as a regular scheme list). Compare with SQL, which likewise uses multisets and sequences were sets are properly called for. Also compare with [Wadler's "representing failure as a list of successes."](http://citeseer.uark.edu:8080/citeseerx/showciting;jsessionid=FF5F5EAA9D94B1A8618C49C37451D762?cid=377301).

Thus, we represent a 'relation' (aka `non-deterministic function') as a regular CoffeeScript function that returns an array of possible results.

First, we define two primitive non-deterministic functions; one of them yields no result whatsoever for any argument; the other merely returns its argument as the sole result.

    F.succeed = (result) -> [result]
    F.fail = _.always []

We build more complex non-deterministic functions by combining the existing ones with the help of the following two combinators.

`(disjunction l r)` returns all the results of `l` and all the results of `r`, or returns no results only if neither `l` nor `r` returned any.  In that sense, it is analogous to the logical disjunctionunction.

    disjunction = (l, r) ->
      (x) -> _.cat(l(x), r(x))

`(conjunction l r)` looks like a functional composition of `r` and `l`.  Only `(l x)` may return several results, so we have to apply `r` to each of them.  Obviously `(conjunction fail f)` and `(conjunction f fail)` are both equivalent to `fail`: they return no results, ever. It that sense, `conjunction` is analogous to the logical conjunction.

    conjunction = (l, r) ->
      (x) -> _.mapcat(l(x), r)

JavaScript allows you to pass any number of arguments to a function.  Therefore, we can make a function `disj` that takes any number of arguments and performs a logical disjunction over each.  This makes this version of Sokuza Kanren a bit more flexible in that it allows you to nest any number of relations therein.

    F.disj = () ->
      return F.fail if _.isEmpty(arguments)
      disjunction(_.first(arguments),
	              F.disj.apply(this, _.rest(arguments)))

Likewise, the `conj` function accepts any number of clauses, nit just two as in the original Sokuza.

    F.conj = () ->
      clauses = _.toArray(arguments)
      return F.succeed if _.isEmpty(clauses)
      return _.first(clauses) if _.size(clauses) is 1
      conjunction(_.first(clauses),
                  (s) -> F.conj.apply(null, _.rest(clauses))(s))

And that's all that there is for the built-in non-deterministic functions.

# Knowledge representation
# ------------------------

One may think of regular variables as "certain knowledge": they give  names to definite values.  A logic variable then stands for "improvable ignorance".  An unbound logic variable represents no  knowledge at all; in other words, it represents the result of a measurement *before* we have done the measurement. A logic variable may be associated with a definite value, like 10. That means definite knowledge.  A logic variable may be associated with a semi-definite value, like `[$X]` where `$X` is an unbound variable. We know something about the original variable: it is associated with the array of one element.  We can't say though what that element is. A logic variable can be associated with another, unbound logic variable. In that case, we still don't know what precisely the original variable stands for. However, we can say that it represents the same thing as the other variable. So, our uncertainty is reduced.

I've chosen to represent logic variables with their own simple type.

    class LVar
      constructor: (@name) ->

I've also created a couple of helper functions for creating new variables and checking "variableness" to decouple the representation a bit.

    F.lvar = (name) -> new LVar(name)
    F.isLVar = (v) -> (v instanceof LVar)

I implement associations of logic variables and their values (aka, *bindings*) as a class holding a simple JavaScript object.  One may say that a substitution represents our current knowledge of the world.

    class Bindings
      constructor: (seed = {}) ->
        @binds = _.merge({}, seed)

There are actually two ways of implementing substitutions as associative list. If the variable `$x` is associated with `$y` and `$y` is associated with `1`, we could represent this knowledge as `{$x: 1, $y 1}`. It is easy to lookup the value associated with the variable then, via a simple access. OTH, if we have the binding `{$x:  $y}` and we wish to add the bindings of `$y` to `1`, we have to make rearrangements so to produce `{$x: 1, $y 1}`, or make the traversal on lookup (which we defer to `find` below).

However, if we use an association-list-like structure (arrays of pairs) then we can just record the associations as we learn them, without modifying the previous ones. If originally we knew `[[$x, $y]]` and later we learned that `$y` is associated with `1`, we can simply prepend the latter association, obtaining `[[$y, 1], [$x, $y]]`. So, adding new knowledge becomes fast. The lookup procedure becomes more complex though, as we have to chase the chains of variables. To obtain the value associated with `$x` in the latter substitution, we first lookup `$x`, obtain `$y` (another logic variable), then lookup `$y` finally obtaining `1`.  I prefer the object-representation since it's more idiomatic, but using an a-list provides an intuitively incremental way of representing knowledge: it is easier to backtrack if we later find out our knowledge leads to a contradiction.

      extend: (lvar, value) ->
        o = {}
        o[lvar.name] = value
        new Bindings(_.merge(@binds, o))
      has: (lvar) ->
        @binds.hasOwnProperty(lvar.name)

Find the value associated with `lvar` in the `Bindings` instance.  Return `lvar` itself if it is unbound. In miniKanren, this function is called `walk`.

    lookup: (lvar) ->
        return lvar if !F.isLVar(lvar)
        return this.lookup(@binds[lvar.name]) if this.has(lvar)
        lvar

Starting with an empty binding is akin to saying that we start with zero knowledge.

    F.ignorance = new Bindings()

As mentioned, because we overwrite bindings as we discover them the lookup logic is a little pernicious in the face of logic variables bound to other logic variables.

    find = (v, bindings) ->
      lvar = bindings.lookup(v)
      return lvar if F.isLVar(v)
      if _.isArray(lvar)
        if _.isEmpty(lvar)
          return lvar
        else
          return _.cons(find(_.first(lvar), bindings),
                        find(_.rest(lvar), bindings))
      lvar

# Unification
# -----------

Unification is the process of improving knowledge: or, the process of measurement. That measurement may uncover a contradiction though (things are not what we thought them to be). To be precise, the unification is the statement that two terms are the same. For example, unification of `1` and `1` is successful -- `1` is indeed the same as `1`. That doesn't add however to our knowledge of the world. If the logic variable `$x` is associated with `1` in the current bindings, the unification of `$x` with `2` yields a contradiction (the new measurement is not consistent with the previous measurements / hypotheses). Unification of an unbound logic variable `$x` and `1` improves our knowledge: the "measurement" found that `$x` is actually `1`. We record that fact in the new substitution.

Return the new bindings, or `null` on contradiction:

    F.unify = (l, r, bindings) ->

Find out what `l` actually is given our knowledge contained in `bindings`:

      t1 = bindings.lookup(l)

Find out what `r` actually is given our knowledge contained in `bindings`:

      t2 = bindings.lookup(r)

If `l` and `r` are the same; no new knowledge:

      if _.isEqual(t1, t2)
        return bindings

`l` is an unbound variable:

      if F.isLVar(t1)
        return bindings.extend(t1, t2)

`r` is an unbound variable:

      if F.isLVar(t2)
        return bindings.extend(t2, t1)

If t1 is a pair, so must be `r`.  This means that I can only unify arrays of nested arrays bottoming out on values or objects.  I cannot unify objects at the moment.

      if _.isArray(t1) && _.isArray(t2)
        bindings = F.unify(_.first(t1), _.first(t2), bindings)
        bindings = if (bindings isnt null) then F.unify(_.rest(t1), _.rest(t2),   bindings) else bindings
        return bindings
      return null

# Operational logic
# -----------------

Now we can combine non-deterministic functions (Part 1) and the representation of knowledge (Part 2) into a logic system.  We introduce a `goal` -- a non-deterministic function that takes a substitution and produces 0, 1 or more other bindings (new knowledge). In case the goal produces 0 bindings, we say that the goal failed. We will call any result produced by the goal an "outcome".

The functions `succeed` and `fail` defined earlier are obviously  goals.  The latter is the failing goal. OTH, `succeed` is the trivial successful goal, a tautology that doesn't improve our knowledge of the world. We can now add another primitive goal, the result of a "measurement".  The quantum-mechanical connotations of "the measurement" must be obvious by now.

    F.goal = (l, r) ->
      (bindings) ->
        result = F.unify(l, r, bindings)
        return F.succeed(result) if result isnt null
        return F.fail(bindings)

We also need a way to 'run' a goal, to see what knowledge we can obtain starting from sheer ignorance

    F.run = (goal) ->
      goal(F.ignorance)

# Logic programs
# --------------

We can build more complex goals using lambda-abstractions and previously defined combinators, `conj` and `disj`.  For example, we can define the function `choice` such that `choice(t1, array)` is a goal that succeeds if `t1` is an element of `array`.

    F.choice = ($v, list) ->
      return F.fail if _.isEmpty(list)

      F.disj(F.goal($v, _.first(list)),
                    F.choice($v, _.rest(list)))

The name `choice` should evoke [The Axiom of Choice](http://en.wikipedia.org/wiki/Axiom_of_choice)...

# Exports and sundries
# --------------------

	  if module?
	    module.exports = F
	  else
	    root.F = F

	)(this)


you had one job!!

you had one job!!

# /api/Data Structures/Batman.Set/Batman.SetProxy

`Batman.SetProxy` provides a wrapper around a `Batman.Set` which delegates most methods and accessors to the base `Set`. `SetProxy` extends [`Batman.Object`](/docs/api/batman.object.html) and [`Batman.Enumerable`](/docs/api/batman.enumerable.html). To see `Batman.SetProxy` in action, see [`Batman.SetSort`](/docs/api/batman.setsort.html).

## ::constructor(base : Set) SetProxy

Returns a new `SetProxy` tracking `base`.

## Methods delegated to base

- `add`
- `insert`
- `insertWithIndexes`
- `remove`
- `removeWithIndexes`
- `at`
- `find`
- `clear`
- `has`
- `merge`
- `replace`
- `filter`
- `toArray`
- `isEmpty`
- `indexedBy`
- `indexedByUnique`
- `sortedBy`

## Accessors delegated to base

- `first`
- `last`
- `isEmpty`
- `toArray`
- `length`
- `indexedBy`
- `indexedByUnique`
- `sortedBy`
- `sortedByDescending`

# /api/Data Structures/Batman.Set/Batman.SetSort

`Batman.SetSort` is a [`Batman.SetProxy`](/docs/api/batman.setproxy.html) which sorts the members of its `base` by provided `key` and `order`. Through `SetProxy`, `SetSort` extends [`Batman.Object`](/docs/api/batman.object.html) and [`Batman.Enumerable`](/docs/api/batman.enumerable.html). `SetSort`s are generally derived from `Set`s. For example:

    test "SetSorts are sorted proxies of their Sets", ->
      batmobile = new Batman.Object(name: "Batmobile", wheelCount: 4)
      batcycle = new Batman.Object(name: "Batcycle", wheelCount: 2)
      vehicles = new Batman.Set(batmobile, batcycle)

      vehiclesByWheelCount1 = new Batman.SetSort(vehicles, 'wheelCount') # order defaults to 'asc'
      vehiclesByWheelCount2 = vehicles.sortedBy('wheelCount')
      vehiclesByWheelCount3 = vehicles.get('sortedBy.wheelCount')

      batcopter = new Batman.Object(name: "Batcopter", wheelCount: 0)
      vehicles.add(batcopter)

      for setSort in [vehiclesByWheelCount1, vehiclesByWheelCount2, vehiclesByWheelCount3]
        deepEqual setSort.mapToProperty('wheelCount'), [0, 2, 4]

## ::constructor(base : Set, key : String, order : ["asc"|"desc", default "asc"]) : SetSort

Returns a new `SetSort`, tracking `base` and ordering by `key` in the direction of `order`.

## ::.isSorted[= true]

Returns `true`.

## ::.key : String

Returns the key used to sort members of the `SetSort`, as defined in the constructor.

## ::.descending

`true` if `"desc"` was passed to as `order` to the constructor, otherwise `false`. Descending `SetSort`s may also be made by getting `sortedByDescending` from a `Set`:

    test "get sortedByDescending creates a descending SetSort", ->
      batmobile = new Batman.Object(name: "Batmobile", wheelCount: 4)
      batcycle = new Batman.Object(name: "Batcycle", wheelCount: 2)
      vehicles = new Batman.Set(batmobile, batcycle)

      vehiclesByWheelCountDescending1 = new Batman.SetSort(vehicles, 'wheelCount', 'desc')
      vehiclesByWheelCountDescending2 = vehicles.get('sortedByDescending.wheelCount')

      for descendingSetSort in [vehiclesByWheelCountDescending1, vehiclesByWheelCountDescending2]
        equal descendingSetSort.descending, true
        deepEqual descendingSetSort.mapToProperty('wheelCount'), [4, 2]

## ::find(func : Function)

Returns the first item from the `SetSort` for which `func(item)` returns a truthy value.

## ::forEach(func : Function)

Calls `func(item)` for each item in the `SetSort`.

## ::toArray() : Array

Returns a sorted array representation of the `SetSort`'s contents.

## ::merge(other : Set) : SetSort

Returns a new `SetSort` whose items are the union of this `SetSort` and `other`. It will have the same `key` and `order` as the starting `SetSort`.

## ::compare(a, b) : Number

Returns:

- `-1` if `a` has higher precedence than `b`
- `0` if `a` and `b` have equal precedence or if precedence can't be determined
- `1` if `b` has higher precedence than `a`

Precedence is determined by testing for `undefined`, `null`, `false`, `true`, `NaN`, `a < b` and `a > b`. Please see [the source](https://github.com/batmanjs/batman/blob/master/src/set/set_sort.coffee#L91) for implementation details.

Since `::compareElements` delegates to `::compare`, you can acheive custom sorting by overriding `::compare` in a `SetSort` subclass.

## ::compareElements(a, b) : Number

Used by `SetSort` to compare its elements when sorting. Like `::compare`, it returns `-1`, `0`, or `1`. To arrive at this value, it:

- tries `a.get(@key)` (where `@key` is the `SetSort`s key)
- if the resulting value is a function, calls `resultingFunction(a)`
- calls `resultingValue.valueOf()`
- repeats the process for `b`
- delegates to `::compare` to compare the resulting values
- inverts the value if `::.descending`

By following this process, it provides sort values for `Batman.Object`s `a` and `b` according to `@key`.

# /api/Data Structures/Batman.Set/Batman.SetProxy

`Batman.SetProxy` provides a wrapper around a `Batman.Set` which delegates most methods and accessors to the base `Set`. `SetProxy` extends [`Batman.Object`](/docs/api/batman.object.html) and [`Batman.Enumerable`](/docs/api/batman.enumerable.html). To see `Batman.SetProxy` in action, see [`Batman.SetSort`](/docs/api/batman.setsort.html).

## ::constructor(base : Set) SetProxy

Returns a new `SetProxy` tracking `base`.

## Methods delegated to base

- `add`
- `insert`
- `insertWithIndexes`
- `remove`
- `removeWithIndexes`
- `at`
- `find`
- `clear`
- `has`
- `merge`
- `replace`
- `filter`
- `toArray`
- `isEmpty`
- `indexedBy`
- `indexedByUnique`
- `sortedBy`

## Accessors delegated to base

- `first`
- `last`
- `isEmpty`
- `toArray`
- `length`
- `indexedBy`
- `indexedByUnique`
- `sortedBy`
- `sortedByDescending`

# /api/Data Structures/Batman.Set/Batman.SetSort

`Batman.SetSort` is a [`Batman.SetProxy`](/docs/api/batman.setproxy.html) which sorts the members of its `base` by provided `key` and `order`. Through `SetProxy`, `SetSort` extends [`Batman.Object`](/docs/api/batman.object.html) and [`Batman.Enumerable`](/docs/api/batman.enumerable.html). `SetSort`s are generally derived from `Set`s. For example:

    test "SetSorts are sorted proxies of their Sets", ->
      batmobile = new Batman.Object(name: "Batmobile", wheelCount: 4)
      batcycle = new Batman.Object(name: "Batcycle", wheelCount: 2)
      vehicles = new Batman.Set(batmobile, batcycle)

      vehiclesByWheelCount1 = new Batman.SetSort(vehicles, 'wheelCount') # order defaults to 'asc'
      vehiclesByWheelCount2 = vehicles.sortedBy('wheelCount')
      vehiclesByWheelCount3 = vehicles.get('sortedBy.wheelCount')

      batcopter = new Batman.Object(name: "Batcopter", wheelCount: 0)
      vehicles.add(batcopter)

      for setSort in [vehiclesByWheelCount1, vehiclesByWheelCount2, vehiclesByWheelCount3]
        deepEqual setSort.mapToProperty('wheelCount'), [0, 2, 4]

## ::constructor(base : Set, key : String, order : ["asc"|"desc", default "asc"]) : SetSort

Returns a new `SetSort`, tracking `base` and ordering by `key` in the direction of `order`.

## ::.isSorted[= true]

Returns `true`.

## ::.key : String

Returns the used to sort members of the `SetSort`, as defined in the constructor.

## ::.descending

`true` if `"desc"` was passed to as `order` to the constructor, otherwise `false`. Descending `SetSort`s may also be made by getting `sortedByDescending` from a `Set`:

    test "get sortedByDescending creates a descending SetSort", ->
      batmobile = new Batman.Object(name: "Batmobile", wheelCount: 4)
      batcycle = new Batman.Object(name: "Batcycle", wheelCount: 2)
      vehicles = new Batman.Set(batmobile, batcycle)

      vehiclesByWheelCountDescending1 = new Batman.SetSort(vehicles, 'wheelCount', 'desc')
      vehiclesByWheelCountDescending2 = vehicles.get('sortedByDescending.wheelCount')

      for descendingSetSort in [vehiclesByWheelCountDescending1, vehiclesByWheelCountDescending2]
        equal descendingSetSort.descending, true
        deepEqual descendingSetSort.mapToProperty('wheelCount'), [4, 2]

## ::find(func : Function)

Returns the first item from the `SetSort` for which `func(item)` returns a truthy value.

## ::forEach(func : Function)

Calls `func(item)` for each item in the `SetSort`.

## ::toArray() : Array

Returns a sorted array representation of the `SetSort`'s contents.

## ::merge(other : Set) : SetSort

Returns a new `SetSort` whose items are the union of this `SetSort` and `other`. It will have the same `key` and `order` as the starting `SetSort`.

## ::compare(a, b) : Number

Returns:

- `-1` if `a` has higher precedence than `b`
- `0` if `a` and `b` have equal precedence or if precedence can't be determined
- `1` if `b` has higher precedence than `a`

Precedence is determined by testing for `undefined`, `null`, `false`, `true`, `NaN`, `a < b` and `a > b`. Please see [the source](https://github.com/batmanjs/batman/blob/master/src/set/set_sort.coffee#L91) for implementation details.

Since `::compareElements` delegates to `::compare`, you can acheive custom sorting by overriding `::compare` in a `SetSort` subclass.

## ::compareElements(a, b) : Number

Used by `SetSort` to compare its elements when sorting. Like `::compare`, it returns `-1`, `0`, or `1`. To arrive at this value, it:

- tries `a.get(@key)` (where `@key` is the `SetSort`s key)
- if the resulting value is a function, calls `resultingFunction(a)`
- calls `resultingValue.valueOf()`
- repeats the process for `b`
- delegates to `::compare` to compare the resulting values
- inverts the value if `::.descending`

By following this process, it provides sort values for `Batman.Object`s `a` and `b` according to `@key`.


Simplify Angular navigation request object

# Angular on the server

*A sandboxed version of Angular, for clients lacking JS.*

###### TODO: Sandbox pool, might be better handled by [sandboxes](source/server/sandboxes).

    _ = require 'lodash'
    Promise = require 'bluebird'
    url = require 'url'

    config = require 'config'
    debug = require('debug') 'shrub:angular'
    middleware = require 'middleware'
    {Sandbox} = require 'sandboxes'

The middleware dispatched every time sandboxed angular is navigated.

    navigationMiddleware = []

    exports.pkgmanRegister = (registrar) ->

#### Implements hook `endpoint`.

Allow a JSful client to call us back and inform us that we don't need to
hold their sandbox.

      registrar.registerHook 'endpoint', ->

        route: 'shrub.hangup'
        receiver: (req, fn) ->

###### TODO: Cookie-less clients won't have a valid session ID to call with. This should be some other token, perhaps CSRF.

          id = req.session?.id
          if (sandbox = sandboxManager.lookup id)?
            sandbox.close().finally -> fn()
          else
            fn()

#### Implements hook `httpMiddleware`.

If configuration dictates, render the client-side Angular application in a
sandbox.

      registrar.registerHook 'httpMiddleware', (http) ->

        label: 'Render page with Angular'
        middleware: [

          (req, res, next) ->

Skip render in a sandbox?

            settings = config.get 'packageSettings:shrub-angular'
            return next() unless settings.render

Thrown when a request is complete.

            class ResponseComplete extends Error
              constructor: (@message) ->

After the template is rendered, lookup or create the sandbox.

            Promise.resolve(req.delivery).bind({}).then((html) ->

              sandboxManager.lookupOrCreate(
                html
              ,
                cookie: req.headers.cookie

###### TODO: Multiline.

                url: "http://localhost:#{config.get 'packageSettings:shrub-http:port'}/shrub-entry-point"
              ,
                req.session.id
              )

            ).then((@sandbox) ->

Emit the HTML from before the last redirection.

              if (redirectionHtml = @sandbox.redirectionHtml())?
                @sandbox.setRedirectionHtml null
                res.end redirectionHtml
                throw new ResponseComplete()

Check for any new redirection and handle it.

              if (redirectionPath = @sandbox.redirectionPath())?
                @sandbox.setRedirectionPath null
                res.redirect redirectionPath
                throw new ResponseComplete()

              @sandbox.navigate req

            ).then(->

              emission = @sandbox.emitHtml()

If a redirect happened in the sandbox, actually redirect
the browser and save the emission for the next request.

              if (redirectionPath = @sandbox.redirectionPath())?
                @sandbox.setRedirectionPath null
                @sandbox.setRedirectionHtml emission
                res.redirect redirectionPath

Otherwise, just emit.

              else

                req.delivery = emission
                next()

The request was completed early.

            ).catch(ResponseComplete, ->

            ).catch next

      ]

#### Implements hook `bootstrapMiddleware`.

      registrar.registerHook 'bootstrapMiddleware', ->

        label: 'Bootstrap Angular'
        middleware: [

          (next) ->

Always disable sandbox rendering in end-to-end testing mode.

            if config.get 'E2E'
              config.set 'packageSettings:shrub-angular:render', false

#### Invoke hook `angularNavigationMiddleware`.

Load the navigation middleware.

            navigationMiddleware = middleware.fromShortName(
              'angular navigation', 'shrub-angular'
            )

            next()

        ]

#### Implements hook `packageSettings`.

      registrar.registerHook 'packageSettings', ->

Default navigation middleware.

        navigationMiddleware: [
          'shrub-form'
        ]

Should we render in the sandbox?

        render: true

Time-to-live for rendering sandboxes.

        ttl: 1000 * 60 * 5

This class handles instantiation of new sandboxes, as well as providing a
mechanism for registering and looking up persistent sandboxes using an id.

    sandboxManager = new class SandboxManager

## *constructor*

*Initialize the persistent store.*

      constructor: ->

        @_sandboxes = {}

## SandboxManager#create

* (string) `html` - The HTML to use as the sandbox document.
* (string) `cookie` - The cookie to use for the document.
* (optional string) `id` - An ID for looking up this sandbox later.

*Create a sandbox.*

      create: (html, options, id = null) ->

        debug "Creating sandbox ID: #{id}"

        sandbox = new Sandbox()
        sandbox.id = id

## Sandbox#close (monkeypatch)

*Remove from the manager when closing.*

        close = sandbox.close
        sandbox.close = =>
          debug "Closing sandbox ID: #{id}"

          @_sandboxes[id] = null
          close.apply sandbox

Create the document.

        (@_sandboxes[id] = sandbox).createDocument html, options

## SandboxManager#lookup

* (string) `id` - An ID for looking up this sandbox later.

*Look up a sandbox by ID.*

      lookup: (id) -> @_sandboxes[id]?.touch()

## SandboxManager#lookupOrCreate

* (string) `html` - The HTML to use as the sandbox document if creating.
* (string) `cookie` - The cookie to use for the document if creating.
* (optional string) `id` - An ID either for looking up later (if creating), or
  as a search now.

*Look up a sandbox by ID, or create one if none is registered for this
ID.*

      lookupOrCreate: (html, options, id = null) ->

        if (sandbox = @lookup id)?

          Promise.resolve sandbox

        else

          @create(html, options, id).then (sandbox) -> augmentSandbox sandbox

Augment a sandbox with Angular-specific functionality.

    augmentSandbox = (sandbox) ->

## Sandbox#touch

*Reset the time-to-live for a sandbox.*

      ttl = config.get 'packageSettings:shrub-angular:ttl'
      toucher = _.debounce (-> sandbox.close()), ttl
      do sandbox.touch = ->
        debug "Touched sandbox ID: #{id}"

        toucher()
        sandbox

## Sandbox#(setR|r)edirectionHtml

* (string) `html` - The HTML to be deliviered.

*HTML to be delivered upon the next request using this sandbox.*

      redirectionHtml = null
      sandbox.redirectionHtml = -> redirectionHtml
      sandbox.setRedirectionHtml = (html) -> redirectionHtml = html

## Sandbox#(setR|r)edirectionPath

* (string) `path` - The URL path to redirect to.

*The path that the client will be redirected to at the end of this
request.*

      redirectionPath = null
      sandbox.redirectionPath = -> redirectionPath
      sandbox.setRedirectionPath = (path) -> redirectionPath = path

## Sandbox#catchAngularRedirection

* (string) `path` - URL path.

*Check whether Angular was redirected, and set the client redirection
path if it was.*

      sandbox.catchAngularRedirection = (path) ->
        self = this

        @inject [
          '$location'
          ($location) ->

            return if path is $location.url()

            if redirect = self.pathRedirectsTo $location.url()
              self.setRedirectionPath redirect
            else
              self.setRedirectionPath $location.url()

        ]

## Sandbox#checkPathChanges

* (string) `path` - URL path.

*Check whether the navigation path is different than the current Angular
location path. If it is, redirect Angular.*

      sandbox.checkPathChanges = (path) ->
        self = this

        new Promise (resolve) ->

          self.inject [
            '$location', '$rootScope'
            ($location, $rootScope) ->

              # } Nowhere to go?
              return resolve() if path is url.parse(self.url()).path

              # } Navigate Angular to the request path.
              unlisten = $rootScope.$on 'shrub.core.routeRendered', ->
                unlisten()
                resolve()

              $rootScope.$apply -> $location.path path

          ]

## Sandbox#navigate

* (http.IncomingMessage) `req` - The HTTP request object.

*Navigate angular to a path, and dispatch navigation middleware.*

      sandbox.navigate = (req) ->
        self = this

        {path} = url.parse req.url

        @checkPathChanges(path).then ->

          new Promise (resolve, reject) ->

            navigationReq = Object.create req
            navigationReq.sandbox = sandbox

###### TODO: Remove unused `res` parameter.

            navigationMiddleware.dispatch navigationReq, null, (error) =>
              return reject error if error?

              self.catchAngularRedirection path
              resolve()

## Sandbox#pathRedirectsTo

* (string) `path` - URL path.

*Check where a path would be redirected by Angular's router.*

      sandbox.pathRedirectsTo = (path) ->

        routes = null

        @inject [
          '$route'
          ($route) -> routes = $route.routes
        ]

Perfect match.

        if routes[path]?

Does this path redirect? Do an HTTP redirect.

          return routes[path].redirectTo if routes[path].redirectTo?

        else

          match = false

Check for any regexs.

          for key, route of routes
            if route.regexp?.test path

###### TODO: Need to extract params to build redirectTo, it's a small enough mismatch to ignore for now.

              return

Angular's $routeProvider.otherwise() target.

          return routes[null].redirectTo if routes[null]?

## Sandbox#inject

* (mixed) `injectable` - An annotated function to inject with
  dependencies.

*Inject an
[annotated function](http://docs.angularjs.org/guide/di#dependency-annotation)
with dependencies.*

      sandbox.inject = (injectable) ->

        injector = @_window.angular.element(@_window.document).injector()
        injector.invoke injectable

Make sure the socket is dead.

      sandbox.registerCleanupFunction ->

        new Promise (resolve) ->

          sandbox.inject [
            'shrub-socket'
            (socket) ->

              socket.on 'disconnect', -> resolve()
              socket.disconnect()

          ]

      new Promise (resolve) ->

        sandbox.inject [
          '$sniffer', 'shrub-socket'
          ($sniffer, socket) ->

Don't even try HTML 5 history on the server side.

            $sniffer.history = false

Let the socket finish initialization.

            socket.on 'initialized', -> resolve sandbox

        ]

# Angular on the server

*A sandboxed version of Angular, for clients lacking JS.*

###### TODO: Sandbox pool, might be better handled by [sandboxes](source/server/sandboxes).

    _ = require 'lodash'
    Promise = require 'bluebird'
    url = require 'url'

    config = require 'config'
    debug = require('debug') 'shrub:angular'
    middleware = require 'middleware'
    {Sandbox} = require 'sandboxes'

The middleware dispatched every time sandboxed angular is navigated.

    navigationMiddleware = []

    exports.pkgmanRegister = (registrar) ->

#### Implements hook `endpoint`.

Allow a JSful client to call us back and inform us that we don't need to
hold their sandbox.

      registrar.registerHook 'endpoint', ->

        route: 'shrub.hangup'
        receiver: (req, fn) ->

###### TODO: Cookie-less clients won't have a valid session ID to call with. This should be some other token, perhaps CSRF.

          id = req.session?.id
          if (sandbox = sandboxManager.lookup id)?
            sandbox.close().finally -> fn()
          else
            fn()

#### Implements hook `httpMiddleware`.

If configuration dictates, render the client-side Angular application in a
sandbox.

      registrar.registerHook 'httpMiddleware', (http) ->

        label: 'Render page with Angular'
        middleware: [

          (req, res, next) ->

Skip render in a sandbox?

            settings = config.get 'packageSettings:shrub-angular'
            return next() unless settings.render

Thrown when a request is complete.

            class ResponseComplete extends Error
              constructor: (@message) ->

            # } After the template is rendered, lookup or create the sandbox.
            Promise.resolve(req.delivery).bind({}).then((html) ->

              sandboxManager.lookupOrCreate(
                html
              ,
                cookie: req.headers.cookie

###### TODO: Multiline.

                url: "http://localhost:#{config.get 'packageSettings:shrub-http:port'}/shrub-entry-point"
              ,
                req.session.id
              )

            ).then((@sandbox) ->

Emit the HTML from before the last redirection.

              if (redirectionHtml = @sandbox.redirectionHtml())?
                @sandbox.setRedirectionHtml null
                res.end redirectionHtml
                throw new ResponseComplete()

Check for any new redirection and handle it.

              if (redirectionPath = @sandbox.redirectionPath())?
                @sandbox.setRedirectionPath null
                res.redirect redirectionPath
                throw new ResponseComplete()

              {path} = url.parse req.url
              @sandbox.navigate path, req.body

            ).then(->

              emission = @sandbox.emitHtml()

If a redirect happened in the sandbox, actually redirect
the browser and save the emission for the next request.

              if (redirectionPath = @sandbox.redirectionPath())?
                @sandbox.setRedirectionPath null
                @sandbox.setRedirectionHtml emission
                res.redirect redirectionPath

Otherwise, just emit.

              else

                req.delivery = emission
                next()

The request was completed early.

            ).catch(ResponseComplete, ->

            ).catch next

      ]

#### Implements hook `bootstrapMiddleware`.

      registrar.registerHook 'bootstrapMiddleware', ->

        label: 'Bootstrap Angular'
        middleware: [

          (next) ->

Always disable sandbox rendering in end-to-end testing mode.

            if config.get 'E2E'
              config.set 'packageSettings:shrub-angular:render', false

#### Invoke hook `angularNavigationMiddleware`.

Load the navigation middleware.

            navigationMiddleware = middleware.fromShortName(
              'angular navigation', 'shrub-angular'
            )

            next()

        ]

#### Implements hook `packageSettings`.

      registrar.registerHook 'packageSettings', ->

Default navigation middleware.

        navigationMiddleware: [
          'shrub-form'
        ]

Should we render in the sandbox?

        render: true

Time-to-live for rendering sandboxes.

        ttl: 1000 * 60 * 5

This class handles instantiation of new sandboxes, as well as providing a
mechanism for registering and looking up persistent sandboxes using an id.

    sandboxManager = new class SandboxManager

## *constructor*

*Initialize the persistent store.*

      constructor: ->

        @_sandboxes = {}

## SandboxManager#create

* (string) `html` - The HTML to use as the sandbox document.
* (string) `cookie` - The cookie to use for the document.
* (optional string) `id` - An ID for looking up this sandbox later.

*Create a sandbox.*

      create: (html, options, id = null) ->

        debug "Creating sandbox ID: #{id}"

        sandbox = new Sandbox()
        sandbox.id = id

## Sandbox#close (monkeypatch)

*Remove from the manager when closing.*

        close = sandbox.close
        sandbox.close = =>
          debug "Closing sandbox ID: #{id}"

          @_sandboxes[id] = null
          close.apply sandbox

Create the document.

        (@_sandboxes[id] = sandbox).createDocument html, options

## SandboxManager#lookup

* (string) `id` - An ID for looking up this sandbox later.

*Look up a sandbox by ID.*

      lookup: (id) -> @_sandboxes[id]?.touch()

## SandboxManager#lookupOrCreate

* (string) `html` - The HTML to use as the sandbox document if creating.
* (string) `cookie` - The cookie to use for the document if creating.
* (optional string) `id` - An ID either for looking up later (if creating), or
  as a search now.

*Look up a sandbox by ID, or create one if none is registered for this
ID.*

      lookupOrCreate: (html, options, id = null) ->

        if (sandbox = @lookup id)?

          Promise.resolve sandbox

        else

          @create(html, options, id).then (sandbox) -> augmentSandbox sandbox

Augment a sandbox with Angular-specific functionality.

    augmentSandbox = (sandbox) ->

## Sandbox#touch

*Reset the time-to-live for a sandbox.*

      ttl = config.get 'packageSettings:shrub-angular:ttl'
      toucher = _.debounce (-> sandbox.close()), ttl
      do sandbox.touch = ->
        debug "Touched sandbox ID: #{id}"

        toucher()
        sandbox

## Sandbox#(setR|r)edirectionHtml

* (string) `html` - The HTML to be deliviered.

*HTML to be delivered upon the next request using this sandbox.*

      redirectionHtml = null
      sandbox.redirectionHtml = -> redirectionHtml
      sandbox.setRedirectionHtml = (html) -> redirectionHtml = html

## Sandbox#(setR|r)edirectionPath

* (string) `path` - The URL path to redirect to.

*The path that the client will be redirected to at the end of this
request.*

      redirectionPath = null
      sandbox.redirectionPath = -> redirectionPath
      sandbox.setRedirectionPath = (path) -> redirectionPath = path

## Sandbox#catchAngularRedirection

* (string) `path` - URL path.

*Check whether Angular was redirected, and set the client redirection
path if it was.*

      sandbox.catchAngularRedirection = (path) ->
        self = this

        @inject [
          '$location'
          ($location) ->

            return if path is $location.url()

            if redirect = self.pathRedirectsTo $location.url()
              self.setRedirectionPath redirect
            else
              self.setRedirectionPath $location.url()

        ]

## Sandbox#checkPathChanges

* (string) `path` - URL path.

*Check whether the navigation path is different than the current Angular
location path. If it is, redirect Angular.*

      sandbox.checkPathChanges = (path) ->
        self = this

        new Promise (resolve) ->

          self.inject [
            '$location', '$rootScope'
            ($location, $rootScope) ->

              # } Nowhere to go?
              return resolve() if path is url.parse(self.url()).path

              # } Navigate Angular to the request path.
              unlisten = $rootScope.$on 'shrub.core.routeRendered', ->
                unlisten()
                resolve()

              $rootScope.$apply -> $location.path path

          ]

## Sandbox#navigate

* (string) `path` - URL path.
* (string) `body` - Request body.

*Navigate angular to a path, and dispatch navigation middleware.*

      sandbox.navigate = (path, body) ->
        self = this

        @checkPathChanges(path).then ->

          new Promise (resolve, reject) ->

            req =
              body: body
              path: path
              sandbox: sandbox

###### TODO: Remove unused `res` parameter.

            navigationMiddleware.dispatch req, null, (error) =>
              return reject error if error?

              self.catchAngularRedirection path
              resolve()

## Sandbox#pathRedirectsTo

* (string) `path` - URL path.

*Check where a path would be redirected by Angular's router.*

      sandbox.pathRedirectsTo = (path) ->

        routes = null

        @inject [
          '$route'
          ($route) -> routes = $route.routes
        ]

Perfect match.

        if routes[path]?

Does this path redirect? Do an HTTP redirect.

          return routes[path].redirectTo if routes[path].redirectTo?

        else

          match = false

Check for any regexs.

          for key, route of routes
            if route.regexp?.test path

###### TODO: Need to extract params to build redirectTo, it's a small enough mismatch to ignore for now.

              return

Angular's $routeProvider.otherwise() target.

          return routes[null].redirectTo if routes[null]?

## Sandbox#inject

* (mixed) `injectable` - An annotated function to inject with
  dependencies.

*Inject an
[annotated function](http://docs.angularjs.org/guide/di#dependency-annotation)
with dependencies.*

      sandbox.inject = (injectable) ->

        injector = @_window.angular.element(@_window.document).injector()
        injector.invoke injectable

Make sure the socket is dead.

      sandbox.registerCleanupFunction ->

        new Promise (resolve) ->

          sandbox.inject [
            'shrub-socket'
            (socket) ->

              socket.on 'disconnect', -> resolve()
              socket.disconnect()

          ]

      new Promise (resolve) ->

        sandbox.inject [
          '$sniffer', 'shrub-socket'
          ($sniffer, socket) ->

Don't even try HTML 5 history on the server side.

            $sniffer.history = false

Let the socket finish initialization.

            socket.on 'initialized', -> resolve sandbox

        ]


Set Access-Control headers for collection

    ObjectID    = require('mongodb').ObjectID
    stringify   = require('JSONStream').stringify
    createHash  = require('crypto').createHash

    Document    = require './model/Document'

    mongo       = require './db/mongo'

## PARAM {collection}

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

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

      next()

## OPTIONS /{collection}

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

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

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

### ?tag=`String`

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

### ?grupper=`String`

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

### ?after=`Mixed`

      if typeof req.query.after is 'string' and req.query.after isnt ''
        if not isNaN(req.query.after)
          req.query.after = new Date(parseInt(req.query.after, 10)).toISOString()
        query.endret = {$gte:req.query.after}

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

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

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

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

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

#### ToDo

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

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

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

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

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

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

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

Set up MongoDB options.

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

Retrive matching documents from MongoDB.

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

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

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

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

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

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

## POST /{collection}

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

      req.body.tilbyder = req.usr

      new Document(req.type, null).once('error', next).once 'ready', ->
        @insert req.body, (err, warn, data) ->
          if err
            return next(err) if err.name isnt 'ValidationError'
            return res.json 422,
              document: req.body
              message: 'Validation Failed'
              errors: err.details #TODO(starefossen) document this

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

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

    ObjectID    = require('mongodb').ObjectID
    stringify   = require('JSONStream').stringify
    createHash  = require('crypto').createHash

    Document    = require './model/Document'

    mongo       = require './db/mongo'

## PARAM {collection}

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

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

      next()

## OPTIONS /{collection}

    exports.options = (req, res, next) ->
      res.setHeader 'Access-Control-Allow-Methods', 'HEAD, GET, POST, PATCH, PUT'
      res.send()

## GET /{collection}

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

### ?tag=`String`

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

### ?grupper=`String`

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

### ?after=`Mixed`

      if typeof req.query.after is 'string' and req.query.after isnt ''
        if not isNaN(req.query.after)
          req.query.after = new Date(parseInt(req.query.after, 10)).toISOString()
        query.endret = {$gte:req.query.after}

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

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

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

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

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

#### ToDo

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

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

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

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

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

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

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

Set up MongoDB options.

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

Retrive matching documents from MongoDB.

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

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

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

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

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

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

## POST /{collection}

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

      req.body.tilbyder = req.usr

      new Document(req.type, null).once('error', next).once 'ready', ->
        @insert req.body, (err, warn, data) ->
          if err
            return next(err) if err.name isnt 'ValidationError'
            return res.json 422,
              document: req.body
              message: 'Validation Failed'
              errors: err.details #TODO(starefossen) document this

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

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


pretty decent initial test.

pretty decent initial test.

Literate CoffeeScript Test
--------------------------

comment comment

    test "basic literate CoffeeScript parsing", ->
      ok yes

now with a...

    test "broken up indentation", ->

... broken up ...

      do ->

... nested block.

        ok yes

Code in `backticks is not parsed` and...

    test "comments in indented blocks work", ->
      do ->
        do ->
          # Regular comment.

          ###
            Block comment.
          ###

          ok yes

Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:

  * I

  * Am

  * A

  * List


use timeKeepAlive to calculate waitTime of deads

# Super secret queue methods

    @QueueMethods =
      popAndServeNextTicket: ->

Move the current connection to the processed queue

TODO: get postID

        queueMeta = QueueMeta.findOne()

Update object, extended at various point for updating the QueueMeta in one swoop

        update =
          $set: {}

If `theOnlyConnectionAllowedIn` is set to something, check if the connection
has expired and process the connection if it has.

        if queueMeta.theOnlyConnectionAllowedIn isnt undefined

          previousConnection = queueMeta.theOnlyConnectionAllowedIn
          previousConnectionDidntPosted = not queueMeta.hasCurrentConnectionPosted

Return if timeCurrentTicketExpires is in the future (hasn't expired)

          hasntExpired = queueMeta.timeCurrentTicketExpires > moment.utc().toDate()
          return if hasntExpired

Okay then, the current connection has expired, time to get bumped.
Insert the processed connection into the ProcessedQueue

          try

Calculate how long the connection had to wait in line.

            waitTimeSeconds = moment.utc().diff (moment.utc queueMeta.theOnlyConnectionAllowedIn.timeEnqueued), 'seconds'

Make the processed object.

            processed =
              connection: queueMeta.theOnlyConnectionAllowedIn
              waitTimeSeconds: waitTimeSeconds

Insert the processed connection to `QueueProcessed`

            QueueProcessed.insert processed
          catch err
            console.log err

Remove the connection from the `Queue`

          criteria = _id: queueMeta.currentlyServingQueueId
          Queue.remove criteria

Update `QueueMeta` stats and ticket number.

          try
            totalWaitTimeSeconds = queueMeta.totalWaitTimeSeconds + waitTimeSeconds
            currentlyServingTicketNumber = queueMeta.currentlyServingTicketNumber + 1
            _.extend update.$set,
              currentlyServingTicketNumber: currentlyServingTicketNumber
              hasCurrentConnectionPosted: no
              totalWaitTimeSeconds: totalWaitTimeSeconds
          catch err
            console.log err

Lookup the lowest ticket # waiting in the `Queue`

        nextConnection = Queue.findOne {}, { sort: { 'ticketNumber': 1 } }

Set the allowed connection and expireTime if one exists

If there is a nextConnection (i.e. someone waiting in line), then serve them.
Otherwise clear out theOnlyConnectionAllowedIn.

        if nextConnection?
          timeCurrentTicketExpires = moment.utc()
          timeCurrentTicketExpires.add 1, 'minutes'
          _.extend update.$set,
            theOnlyConnectionAllowedIn: nextConnection
            timeCurrentTicketExpires: timeCurrentTicketExpires.toDate()
            currentlyServingTicketNumber: nextConnection.ticketNumber
            currentlyServingQueueId: nextConnection._id

If there is no nextConnection, unset theOnlyConnectionAllowedIn.
Note, fake data is filled in to pass schema, even though we're $unset'ing

        else
          update.$unset = theOnlyConnectionAllowedIn: ""

        criteria = _id: queueMeta._id

Delete `update.$set` if it's empty

        if _.isEmpty update.$set
          delete update.$set

If `update` isn't empty, update!

        if not _.isEmpty update
          QueueMeta.update criteria, update

TODO: keep it DRY; logic isn't exactly shared and want to keep update atomic to
avoid extraneous DDP messages.

## Disconnect dead connections, remove from queue

      cleanUpDeadConnections: ->

A connection is dead if keepAlive > 5 minutes ago

        timeFiveMinutesAgoUtc = moment.utc().subtract('5', 'minutes').toDate()
        criteria =
          timeKeepAlive:
            $lt: timeFiveMinutesAgoUtc

        deadQueue = Queue.find criteria
        deadQueue.forEach (queue) ->
          criteria =
            _id: queue._id
          Queue.remove criteria

Calculate how long the connection had to wait in line.

          waitTimeSeconds = moment.utc().diff (moment.utc queue.timeKeepAlive), 'seconds'

Make the processed object.

          processed =
            connection: queue
            waitTimeSeconds: waitTimeSeconds
            diconnect: yes
          QueueProcessed.insert processed

          queueMeta = QueueMeta.findOne()

          totalWaitTimeSeconds = queueMeta.totalWaitTimeSeconds + waitTimeSeconds

          criteria =
            _id: queueMeta._id
          action =
            $set:
              totalWaitTimeSeconds: totalWaitTimeSeconds
          QueueMeta.update criteria, action

# Super secret queue methods

    @QueueMethods =
      popAndServeNextTicket: ->

Move the current connection to the processed queue

TODO: get postID

        queueMeta = QueueMeta.findOne()

Update object, extended at various point for updating the QueueMeta in one swoop

        update =
          $set: {}

If `theOnlyConnectionAllowedIn` is set to something, check if the connection
has expired and process the connection if it has.

        if queueMeta.theOnlyConnectionAllowedIn isnt undefined

          previousConnection = queueMeta.theOnlyConnectionAllowedIn
          previousConnectionDidntPosted = not queueMeta.hasCurrentConnectionPosted

Return if timeCurrentTicketExpires is in the future (hasn't expired)

          hasntExpired = queueMeta.timeCurrentTicketExpires > moment.utc().toDate()
          return if hasntExpired

Okay then, the current connection has expired, time to get bumped.
Insert the processed connection into the ProcessedQueue

          try

Calculate how long the connection had to wait in line.

            waitTimeSeconds = moment.utc().diff (moment.utc queueMeta.theOnlyConnectionAllowedIn.timeEnqueued), 'seconds'

Make the processed object.

            processed =
              connection: queueMeta.theOnlyConnectionAllowedIn
              waitTimeSeconds: waitTimeSeconds

Insert the processed connection to `QueueProcessed`

            QueueProcessed.insert processed
          catch err
            console.log err

Remove the connection from the `Queue`

          criteria = _id: queueMeta.currentlyServingQueueId
          Queue.remove criteria

Update `QueueMeta` stats and ticket number.

          try
            totalWaitTimeSeconds = queueMeta.totalWaitTimeSeconds + waitTimeSeconds
            currentlyServingTicketNumber = queueMeta.currentlyServingTicketNumber + 1
            _.extend update.$set,
              currentlyServingTicketNumber: currentlyServingTicketNumber
              hasCurrentConnectionPosted: no
              totalWaitTimeSeconds: totalWaitTimeSeconds
          catch err
            console.log err

Lookup the lowest ticket # waiting in the `Queue`

        nextConnection = Queue.findOne {}, { sort: { 'ticketNumber': 1 } }

Set the allowed connection and expireTime if one exists

If there is a nextConnection (i.e. someone waiting in line), then serve them.
Otherwise clear out theOnlyConnectionAllowedIn.

        if nextConnection?
          timeCurrentTicketExpires = moment.utc()
          timeCurrentTicketExpires.add 1, 'minutes'
          _.extend update.$set,
            theOnlyConnectionAllowedIn: nextConnection
            timeCurrentTicketExpires: timeCurrentTicketExpires.toDate()
            currentlyServingTicketNumber: nextConnection.ticketNumber
            currentlyServingQueueId: nextConnection._id

If there is no nextConnection, unset theOnlyConnectionAllowedIn.
Note, fake data is filled in to pass schema, even though we're $unset'ing

        else
          update.$unset = theOnlyConnectionAllowedIn: ""

        criteria = _id: queueMeta._id

Delete `update.$set` if it's empty

        if _.isEmpty update.$set
          delete update.$set

If `update` isn't empty, update!

        if not _.isEmpty update
          QueueMeta.update criteria, update

TODO: keep it DRY; logic isn't exactly shared and want to keep update atomic to
avoid extraneous DDP messages.

## Disconnect dead connections, remove from queue

      cleanUpDeadConnections: ->

A connection is dead if keepAlive > 5 minutes ago

        timeFiveMinutesAgoUtc = moment.utc().subtract('5', 'minutes').toDate()
        criteria =
          timeKeepAlive:
            $lt: timeFiveMinutesAgoUtc

        deadQueue = Queue.find criteria
        deadQueue.forEach (queue) ->
          criteria =
            _id: queue._id
          Queue.remove criteria

Calculate how long the connection had to wait in line.

          waitTimeSeconds = moment.utc().diff (moment.utc queue.timeEnqueued), 'seconds'

Make the processed object.

          processed =
            connection: queue
            waitTimeSeconds: waitTimeSeconds
            diconnect: yes
          QueueProcessed.insert processed

          queueMeta = QueueMeta.findOne()

          totalWaitTimeSeconds = queueMeta.totalWaitTimeSeconds + waitTimeSeconds

          criteria =
            _id: queueMeta._id
          action =
            $set:
              totalWaitTimeSeconds: totalWaitTimeSeconds
          QueueMeta.update criteria, action


Fix the display selection logic to account for the day schedule

Display Selection
=================

The display selector -- as its name probably suggests -- selects what to display
on screen, other than overrides taking priority.

Core Logic
----------

We structure the core logic as a simple series of conditionals.

    selectedDisplay = (state) ->

If the kill mode is active, we display a no entry sign on all displays.

As an exception, judges' displays are not public-facing: on those
we display the origin of the kill.

        return 'no-entry' if state.kill? unless state.mode is 'judge'
        return 'kill-origin' if state.kill? and state.mode is 'judge'

For the judge's display, give the selection from the keyboard.

        return state.judgeSelection if state.mode is 'judge'

For info displays, alternate between the league and timetables based
on the turnover clock - unless an association 3 DS item is active
(which would indicate prizegiving, where we want to minimise
distractions).

        if state.mode is 'info'
            return 'blank' if state.dsAssociation is 3
            return 'day-sched' if state.turnover is 0
            return 'match-sched' if state.turnover is 1
            if state.turnover is 2
                return 'knockout-state' if state.dsAssociation is 2
                return 'league-state'

For zones, we display the zone layout during match time, and blank
during association 3 items, otherwise the default display.

        if state.mode is 'zone'
            return 'zone' if state.dsAssociation in [1, 2]
            return 'blank' if state.dsAssociation is 3
            return 'default'

For layout-mode displays, we display the layout layout during
matches, and blank during association 3 items -- otherwise the arena
welcome sign.

        if state.mode is 'layout'
            return 'layout' if state.dsAssociation in [1, 2]
            return 'blank' if state.dsAssociation is 3
            return 'arena-entrance'

For blank displays, always show blank.

        return 'blank' if state.mode is 'blank'

In all other cases, fall back to the default view.

        return 'default'

Public Property
---------------

First, we define the *data source*, which combines all the streams which the
display selector uses as input.

    dataSource = Bacon.combineTemplate
                     mode: Data.mode,
                     kill: Data.killState
                     judgeSelection: window.JudgeSelection
                     turnover: window.Turnover.map((x) -> x % 3)
                     dsAssociation: window.CurrentDSAssociation

We then use *selectedDisplay* to map the data source, followed by
a final pass to skip duplicates, so that we do not flap display
modes.

    window.SelectedDisplay = dataSource.map(selectedDisplay)
                                       .skipDuplicates()

Display Selection
=================

The display selector -- as its name probably suggests -- selects what to display
on screen, other than overrides taking priority.

Core Logic
----------

We structure the core logic as a simple series of conditionals.

    selectedDisplay = (state) ->

If the kill mode is active, we display a no entry sign on all displays.

As an exception, judges' displays are not public-facing: on those
we display the origin of the kill.

        return 'no-entry' if state.kill? unless state.mode is 'judge'
        return 'kill-origin' if state.kill? and state.mode is 'judge'

For the judge's display, give the selection from the keyboard.

        return state.judgeSelection if state.mode is 'judge'

For info displays, alternate between the league and timetables based
on the turnover clock.

        if state.mode is 'info'
            return 'match-sched' if state.turnover is 0
            return 'league-state' if state.turnover is 1
            return 'day-sched' if state.turnover is 2

In all other cases, fall back to the default view.

        return 'default'

Public Property
---------------

First, we define the *data source*, which combines all the streams which the
display selector uses as input.

    dataSource = Bacon.combineTemplate
                     mode: Data.mode,
                     kill: Data.killState
                     judgeSelection: window.JudgeSelection
                     turnover: window.Turnover.map((x) -> x % 3)

We then use *selectedDisplay* to map the data source, followed by
a final pass to skip duplicates, so that we do not flap display
modes.

    window.SelectedDisplay = dataSource.map(selectedDisplay)
                                       .skipDuplicates()


revert primary change detection up the tree

# Container - controller of object properties

## Class Container

    debug = require('debug')('yang:container')
    delegate = require 'delegates'
    Emitter  = require('events').EventEmitter
    Property = require('./property')
    kProp = Symbol.for('property')

    class Container extends Property

      constructor: ->
        super arguments...
        @state.children = new Map
        @state.changes = new Set
        Object.setPrototypeOf @state, Emitter.prototype

      delegate @prototype, 'state'
        .getter 'children'
        .getter 'changes'
        .method 'once'
        .method 'on'
        .method 'off'
        .method 'emit'

      @property 'props',
        get: -> Array.from(@children.values())

      @property 'changed',
        get: -> @changes.size > 0 or @state.changed

      @property 'data',
        set: (value) -> @set value, { force: true }
        get: ->
          value = switch
            when @binding?.get? then @binding.get @context
            else @value

          return value unless value instanceof Object

          new Proxy value,
            has: (obj, key) => @children.has(key) or key of obj
            get: (obj, key) => switch
              when key is kProp then this
              when key is 'toJSON' then @toJSON.bind(this)
              when @children.has(key) then @children.get(key).data
              when key of obj then obj[key]
              when key is 'inspect' then @toJSON.bind(this)
              when key of this and typeof @[key] is 'function' then @[key].bind(this)
            set: (obj, key, value) => switch
              when @children.has(key) then @children.get(key).set(value)
              else obj[key] = value
            deleteProperty: (obj, key) => switch
              when @children.has(key) then @children.get(key).delete()
              when key of obj then delete obj[key]

      @property 'change',
        get: -> switch
          when @changed and not @active then null
          when @changed and @changes.size
            obj = {}
            obj[prop.name] = prop.change for prop in Array.from(@changes)
            obj
          when @changed then @data

      clone: ->
        copy = super children: new Map, changes: new Set
        copy.add prop.clone(parent: copy) for prop in @props
        return copy

      debug: -> debug @uri, arguments...

### add (child)

This call is used to add a child property to map of children.

      add: (child) ->
        @children.set child.name, child
        if @value?
          Object.defineProperty @value, child.name,
            configurable: true
            enumerable: child.active

### remove (child)

This call is used to remove a child property from map of children.

      remove: (child) ->
        @children.delete child.name
        if @value?
          delete @value[child.name]

### get (key)

      get: (key) -> switch
        when key? and @children.has(key) then @children.get(key).data
        else super arguments...

### set (obj, opts)

      set: (obj, opts={}) ->
        @children.clear()
        @changes.clear()
        # TODO: should we also clear Object.defineProperties?
        try obj = Object.assign {}, obj if kProp of obj
        super obj, opts

### merge (obj, opts)

Enumerate key/value of the passed in `obj` and merge into known child
properties.

      merge: (obj, opts={}) ->
        opts.origin ?= this
        return @delete opts if obj is null
        return @set obj, opts if opts.replace or not @value?

        # TODO: protect this as a transaction?
        { deep = true } = opts

        subopts = Object.assign {}, opts, inner: true, replace: not deep
        for own k, v of obj
          @debug "[merge] looking for #{k} inside #{@children.size} children"
          prop = @children.get(k) ? @in(k)
          continue unless prop? and not Array.isArray(prop)
          @debug "[merge] applying value to child prop #{prop.name}"
          prop.merge(v, subopts)
        @update @value, opts

### update

Updates the value to the data model. Called *once* for each node that
is part of the change branch.

      update: (value, opts={}) ->
        opts.origin ?= this

        if value instanceof Property
          @debug "[update] changes.add #{value.name}"
          @changes.add value if value.parent is this
          if opts.inner or opts.origin is this
            return this
          # higher up from change origin
          value = @value

        @debug "[update] handle #{@changes.size} changed props"

        for prop from @changes
          @add prop, opts
          @debug "[update] child #{prop.uri} changed? #{prop.changed}"
          @changes.delete prop unless prop.changed

        # we must clear children here if being deleted before calling super (which calls parent.update)
        @children.clear() if value is null
        super value, opts

        @emit 'update', this, opts
        return this

### commit (opts)

Commits the changes to the data model. Async transaction.
Events: commit, change

      commit: (opts={}) ->
        return true unless @changed

        if @locked
          return new Promise (resolve, reject) => @once 'commit', (res) => resolve res

        @debug "[commit] #{@changes.size} changes"
        try
          @state.locked = true
          @state.setMaxListeners(30 + (@changes.size * 2))

          subopts = Object.assign {}, opts, inner: true

          # 1. traverse down the children
          await prop.commit subopts for prop from @changes when not prop.locked

          @debug "[commit] execute commit binding (if any)..." unless opts.sync
          await @binding?.commit? @context.with(opts) unless opts.sync

          # TODO: enable this later after kos stops using a '.' dummy container between module and nodes
          # opts.origin = this if @changes.size > 1 or not @active
          opts.origin ?= this

          # 2. traverse up the parent (if has parent)
          promise = @parent?.commit? opts
            .then (ok) =>
              @debug "[commit] parent returned: #{ok}"
              await @revert opts unless ok
              if ok
                @emit 'change', opts.origin, opts.actor unless opts.suppress
                @finalize()
              @emit 'commit', ok, opts
              return ok
          unless promise?
            @emit 'change', opts.origin, opts.actor unless opts.suppress
            @finalize()
            promise = true
        catch err
          @debug "[commit] revert due to #{err.message}"
          await @revert opts
          @emit 'commit', false, opts
          throw @error err
        finally
          @state.locked = false

        return switch
          when opts.inner then true
          else Promise.resolve promise

      revert: (opts={}) ->
        return unless @changed

        @debug "[revert] #{@changes.size} changes"
        # below is hackish but works to make a copy of current value
        # to be used as ctx.prior during revert commit binding call
        @state.value = @toJSON()
        await prop.revert opts for prop from @changes
        @add prop for prop from @changes
        await super opts

      finalize: ->
        @changes.clear()
        super()

### toJSON

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

      toJSON: (key, state = true) ->
        props = @props
        value = switch
          when props.length
            obj = {}
            for prop in props when prop.enumerable and (state or prop.mutable)
              value = prop.toJSON false, state
              obj[prop.name] = value if value?
            obj
          else @value
        value = "#{@name}": value if key is true
        return value

### inspect

      inspect: ->
        output = super arguments...
        return Object.assign output, children: @children.size

    module.exports = Container

# Container - controller of object properties

## Class Container

    debug = require('debug')('yang:container')
    delegate = require 'delegates'
    Emitter  = require('events').EventEmitter
    Property = require('./property')
    kProp = Symbol.for('property')

    class Container extends Property

      constructor: ->
        super arguments...
        @state.children = new Map
        @state.changes = new Set
        Object.setPrototypeOf @state, Emitter.prototype

      delegate @prototype, 'state'
        .getter 'children'
        .getter 'changes'
        .method 'once'
        .method 'on'
        .method 'off'
        .method 'emit'

      @property 'props',
        get: -> Array.from(@children.values())

      @property 'changed',
        get: -> @changes.size > 0 or @state.changed

      @property 'data',
        set: (value) -> @set value, { force: true }
        get: ->
          value = switch
            when @binding?.get? then @binding.get @context
            else @value

          return value unless value instanceof Object

          new Proxy value,
            has: (obj, key) => @children.has(key) or key of obj
            get: (obj, key) => switch
              when key is kProp then this
              when key is 'toJSON' then @toJSON.bind(this)
              when @children.has(key) then @children.get(key).data
              when key of obj then obj[key]
              when key is 'inspect' then @toJSON.bind(this)
              when key of this and typeof @[key] is 'function' then @[key].bind(this)
            set: (obj, key, value) => switch
              when @children.has(key) then @children.get(key).set(value)
              else obj[key] = value
            deleteProperty: (obj, key) => switch
              when @children.has(key) then @children.get(key).delete()
              when key of obj then delete obj[key]

      @property 'change',
        get: -> switch
          when @changed and not @active then null
          when @changed and @changes.size
            obj = {}
            obj[prop.name] = prop.change for prop in Array.from(@changes)
            obj
          when @changed then @data

      clone: ->
        copy = super children: new Map, changes: new Set
        copy.add prop.clone(parent: copy) for prop in @props
        return copy

      debug: -> debug @uri, arguments...

### add (child)

This call is used to add a child property to map of children.

      add: (child) ->
        @children.set child.name, child
        if @value?
          Object.defineProperty @value, child.name,
            configurable: true
            enumerable: child.active

### remove (child)

This call is used to remove a child property from map of children.

      remove: (child) ->
        @children.delete child.name
        if @value?
          delete @value[child.name]

### get (key)

      get: (key) -> switch
        when key? and @children.has(key) then @children.get(key).data
        else super arguments...

### set (obj, opts)

      set: (obj, opts={}) ->
        @children.clear()
        @changes.clear()
        # TODO: should we also clear Object.defineProperties?
        try obj = Object.assign {}, obj if kProp of obj
        super obj, opts

### merge (obj, opts)

Enumerate key/value of the passed in `obj` and merge into known child
properties.

      merge: (obj, opts={}) ->
        opts.origin ?= this
        return @delete opts if obj is null
        return @set obj, opts if opts.replace or not @value?

        # TODO: protect this as a transaction?
        { deep = true } = opts

        subopts = Object.assign {}, opts, inner: true, replace: not deep
        for own k, v of obj
          @debug "[merge] looking for #{k} inside #{@children.size} children"
          prop = @children.get(k) ? @in(k)
          continue unless prop? and not Array.isArray(prop)
          @debug "[merge] applying value to child prop #{prop.name}"
          prop.merge(v, subopts)
        @update @value, opts

### update

Updates the value to the data model. Called *once* for each node that
is part of the change branch.

      update: (value, opts={}) ->
        opts.origin ?= this

        if value instanceof Property
          @debug "[update] changes.add #{value.name}"
          @changes.add value if value.parent is this
          if opts.inner or opts.origin is this
            return this
          # higher up from change origin
          value = @value

        @debug "[update] handle #{@changes.size} changed props"

        for prop from @changes
          @add prop, opts
          @debug "[update] child #{prop.uri} changed? #{prop.changed}"
          @changes.delete prop unless prop.changed

        # we must clear children here if being deleted before calling super (which calls parent.update)
        @children.clear() if value is null
        super value, opts

        @emit 'update', this, opts
        return this

### commit (opts)

Commits the changes to the data model. Async transaction.
Events: commit, change

      commit: (opts={}) ->
        return true unless @changed

        if @locked
          return new Promise (resolve, reject) => @once 'commit', (res) => resolve res

        @debug "[commit] #{@changes.size} changes"
        try
          @state.locked = true
          @state.setMaxListeners(30 + (@changes.size * 2))

          subopts = Object.assign {}, opts, inner: true

          # 1. traverse down the children
          await prop.commit subopts for prop from @changes when not prop.locked

          @debug "[commit] execute commit binding (if any)..." unless opts.sync
          await @binding?.commit? @context.with(opts) unless opts.sync

          opts.origin = this if @changes.size > 1 or not @active
          opts.origin ?= this

          # 2. traverse up the parent (if has parent)
          promise = @parent?.commit? opts
            .then (ok) =>
              @debug "[commit] parent returned: #{ok}"
              await @revert opts unless ok
              if ok
                @emit 'change', opts.origin, opts.actor unless opts.suppress
                @finalize()
              @emit 'commit', ok, opts
              return ok
          unless promise?
            @emit 'change', opts.origin, opts.actor unless opts.suppress
            @finalize()
            promise = true
        catch err
          @debug "[commit] revert due to #{err.message}"
          await @revert opts
          @emit 'commit', false, opts
          throw @error err
        finally
          @state.locked = false

        return switch
          when opts.inner then true
          else Promise.resolve promise

      revert: (opts={}) ->
        return unless @changed

        @debug "[revert] #{@changes.size} changes"
        # below is hackish but works to make a copy of current value
        # to be used as ctx.prior during revert commit binding call
        @state.value = @toJSON()
        await prop.revert opts for prop from @changes
        @add prop for prop from @changes
        await super opts

      finalize: ->
        @changes.clear()
        super()

### toJSON

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

      toJSON: (key, state = true) ->
        props = @props
        value = switch
          when props.length
            obj = {}
            for prop in props when prop.enumerable and (state or prop.mutable)
              value = prop.toJSON false, state
              obj[prop.name] = value if value?
            obj
          else @value
        value = "#{@name}": value if key is true
        return value

### inspect

      inspect: ->
        output = super arguments...
        return Object.assign output, children: @children.size

    module.exports = Container