/*global window: false, $: false, pageTracker: false, kanso: true */

/**
 * Code required to bootstrap the browser CommonJS environment.
 */

(function (exports) {

    exports.moduleCache = {};

    exports.normalizePath = function (p) {
        var path = [];
        var parts = p.split('/');
        for (var i = 0; i < parts.length; i += 1) {
            if (parts[i] === '..') {
                path.pop();
            }
            else if (parts[i] !== '.') {
                path.push(parts[i]);
            }
        }
        return path.join('/');
    };

    exports.dirname = function (p) {
        if (p === '/') {
            return p;
        }
        var parts = p.split('/');
        parts.pop();
        if (parts.length === 1 && parts[0] === '') {
            return '/';
        }
        return parts.join('/');
    };

    exports.createRequire = function (current) {
        return function (target) {
            var path;
            if (target.charAt(0) === '.') {
                var dir = exports.dirname(current);
                path = exports.normalizePath(dir + '/' + target);
            }
            else {
                path = exports.normalizePath(target);
            }
            var m = kanso.moduleCache[path];
            if (!m) {
                throw new Error('No such module: ' + path);
            }
            if (!m.loaded) {
                m.exports = {};
                m.id = path;
                // TODO: property not provided by couchdb, but is by node:
                //m.require = exports.createRequire(path);
                // TODO: property not provided by couchdb, but is by node:
                //m.filename = '';
                // TODO: module properties provided by couchdb, but not by kanso
                // * current
                // * parent
                // set this to true *before* calling m.load so circular
                // requires don't blow the call stack
                m.loaded = true;
                //m.load(m, m.exports, m.require);
                m.load(m, m.exports, exports.createRequire(path));
            }
            return m.exports;
        };
    };

    if (typeof require === 'undefined') {
        // make require available globally, unless already in a commonjs
        // environment
        this.require = exports.createRequire('');
    }

}((typeof exports === 'undefined') ? this.kanso = {}: module.exports));


/**
 * CommonJS modules are wrapped and appended to this file.
 */
/********** underscore **********/

kanso.moduleCache["underscore"] = {load: (function (module, exports, require) {

//     Underscore.js 1.2.0
//     (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
//     Underscore is freely distributable under the MIT license.
//     Portions of Underscore are inspired or borrowed from Prototype,
//     Oliver Steele's Functional, and John Resig's Micro-Templating.
//     For all details and documentation:
//     http://documentcloud.github.com/underscore

(function() {

  // Baseline setup
  // --------------

  // Establish the root object, `window` in the browser, or `global` on the server.
  var root = this;

  // Save the previous value of the `_` variable.
  var previousUnderscore = root._;

  // Establish the object that gets returned to break out of a loop iteration.
  var breaker = {};

  // Save bytes in the minified (but not gzipped) version:
  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;

  // Create quick reference variables for speed access to core prototypes.
  var slice            = ArrayProto.slice,
      unshift          = ArrayProto.unshift,
      toString         = ObjProto.toString,
      hasOwnProperty   = ObjProto.hasOwnProperty;

  // All **ECMAScript 5** native function implementations that we hope to use
  // are declared here.
  var
    nativeForEach      = ArrayProto.forEach,
    nativeMap          = ArrayProto.map,
    nativeReduce       = ArrayProto.reduce,
    nativeReduceRight  = ArrayProto.reduceRight,
    nativeFilter       = ArrayProto.filter,
    nativeEvery        = ArrayProto.every,
    nativeSome         = ArrayProto.some,
    nativeIndexOf      = ArrayProto.indexOf,
    nativeLastIndexOf  = ArrayProto.lastIndexOf,
    nativeIsArray      = Array.isArray,
    nativeKeys         = Object.keys,
    nativeBind         = FuncProto.bind;

  // Create a safe reference to the Underscore object for use below.
  var _ = function(obj) { return new wrapper(obj); };

  // Export the Underscore object for **CommonJS**, with backwards-compatibility
  // for the old `require()` API. If we're not in CommonJS, add `_` to the
  // global object.
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = _;
    _._ = _;
  } else {
    // Exported as a string, for Closure Compiler "advanced" mode.
    root['_'] = _;
  }

  // Current version.
  _.VERSION = '1.2.0';

  // Collection Functions
  // --------------------

  // The cornerstone, an `each` implementation, aka `forEach`.
  // Handles objects with the built-in `forEach`, arrays, and raw objects.
  // Delegates to **ECMAScript 5**'s native `forEach` if available.
  var each = _.each = _.forEach = function(obj, iterator, context) {
    if (obj == null) return;
    if (nativeForEach && obj.forEach === nativeForEach) {
      obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
      for (var i = 0, l = obj.length; i < l; i++) {
        if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
      }
    } else {
      for (var key in obj) {
        if (hasOwnProperty.call(obj, key)) {
          if (iterator.call(context, obj[key], key, obj) === breaker) return;
        }
      }
    }
  };

  // Return the results of applying the iterator to each element.
  // Delegates to **ECMAScript 5**'s native `map` if available.
  _.map = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
    each(obj, function(value, index, list) {
      results[results.length] = iterator.call(context, value, index, list);
    });
    return results;
  };

  // **Reduce** builds up a single result from a list of values, aka `inject`,
  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
    var initial = memo !== void 0;
    if (obj == null) obj = [];
    if (nativeReduce && obj.reduce === nativeReduce) {
      if (context) iterator = _.bind(iterator, context);
      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
    }
    each(obj, function(value, index, list) {
      if (!initial) {
        memo = value;
        initial = true;
      } else {
        memo = iterator.call(context, memo, value, index, list);
      }
    });
    if (!initial) throw new TypeError("Reduce of empty array with no initial value");
    return memo;
  };

  // The right-associative version of reduce, also known as `foldr`.
  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
    if (obj == null) obj = [];
    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
      if (context) iterator = _.bind(iterator, context);
      return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
    }
    var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
    return _.reduce(reversed, iterator, memo, context);
  };

  // Return the first value which passes a truth test. Aliased as `detect`.
  _.find = _.detect = function(obj, iterator, context) {
    var result;
    any(obj, function(value, index, list) {
      if (iterator.call(context, value, index, list)) {
        result = value;
        return true;
      }
    });
    return result;
  };

  // Return all the elements that pass a truth test.
  // Delegates to **ECMAScript 5**'s native `filter` if available.
  // Aliased as `select`.
  _.filter = _.select = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
    each(obj, function(value, index, list) {
      if (iterator.call(context, value, index, list)) results[results.length] = value;
    });
    return results;
  };

  // Return all the elements for which a truth test fails.
  _.reject = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    each(obj, function(value, index, list) {
      if (!iterator.call(context, value, index, list)) results[results.length] = value;
    });
    return results;
  };

  // Determine whether all of the elements match a truth test.
  // Delegates to **ECMAScript 5**'s native `every` if available.
  // Aliased as `all`.
  _.every = _.all = function(obj, iterator, context) {
    var result = true;
    if (obj == null) return result;
    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
    each(obj, function(value, index, list) {
      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
    });
    return result;
  };

  // Determine if at least one element in the object matches a truth test.
  // Delegates to **ECMAScript 5**'s native `some` if available.
  // Aliased as `any`.
  var any = _.some = _.any = function(obj, iterator, context) {
    iterator = iterator || _.identity;
    var result = false;
    if (obj == null) return result;
    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
    each(obj, function(value, index, list) {
      if (result |= iterator.call(context, value, index, list)) return breaker;
    });
    return !!result;
  };

  // Determine if a given value is included in the array or object using `===`.
  // Aliased as `contains`.
  _.include = _.contains = function(obj, target) {
    var found = false;
    if (obj == null) return found;
    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
    any(obj, function(value) {
      if (found = value === target) return true;
    });
    return found;
  };

  // Invoke a method (with arguments) on every item in a collection.
  _.invoke = function(obj, method) {
    var args = slice.call(arguments, 2);
    return _.map(obj, function(value) {
      return (method.call ? method || value : value[method]).apply(value, args);
    });
  };

  // Convenience version of a common use case of `map`: fetching a property.
  _.pluck = function(obj, key) {
    return _.map(obj, function(value){ return value[key]; });
  };

  // Return the maximum element or (element-based computation).
  _.max = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
    if (!iterator && _.isEmpty(obj)) return -Infinity;
    var result = {computed : -Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      computed >= result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  };

  // Return the minimum element (or element-based computation).
  _.min = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
    if (!iterator && _.isEmpty(obj)) return Infinity;
    var result = {computed : Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      computed < result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  };

  // Shuffle an array.
  _.shuffle = function(obj) {
    var shuffled = [], rand;
    each(obj, function(value, index, list) {
      if (index == 0) {
        shuffled[0] = value;
      } else {
        rand = Math.floor(Math.random() * (index + 1));
        shuffled[index] = shuffled[rand];
        shuffled[rand] = value;
      }
    });
    return shuffled;
  };

  // Sort the object's values by a criterion produced by an iterator.
  _.sortBy = function(obj, iterator, context) {
    return _.pluck(_.map(obj, function(value, index, list) {
      return {
        value : value,
        criteria : iterator.call(context, value, index, list)
      };
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }), 'value');
  };

  // Groups the object's values by a criterion produced by an iterator
  _.groupBy = function(obj, iterator) {
    var result = {};
    each(obj, function(value, index) {
      var key = iterator(value, index);
      (result[key] || (result[key] = [])).push(value);
    });
    return result;
  };

  // Use a comparator function to figure out at what index an object should
  // be inserted so as to maintain order. Uses binary search.
  _.sortedIndex = function(array, obj, iterator) {
    iterator || (iterator = _.identity);
    var low = 0, high = array.length;
    while (low < high) {
      var mid = (low + high) >> 1;
      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
    }
    return low;
  };

  // Safely convert anything iterable into a real, live array.
  _.toArray = function(iterable) {
    if (!iterable)                return [];
    if (iterable.toArray)         return iterable.toArray();
    if (_.isArray(iterable))      return slice.call(iterable);
    if (_.isArguments(iterable))  return slice.call(iterable);
    return _.values(iterable);
  };

  // Return the number of elements in an object.
  _.size = function(obj) {
    return _.toArray(obj).length;
  };

  // Array Functions
  // ---------------

  // Get the first element of an array. Passing **n** will return the first N
  // values in the array. Aliased as `head`. The **guard** check allows it to work
  // with `_.map`.
  _.first = _.head = function(array, n, guard) {
    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
  };

  // Returns everything but the last entry of the array. Especcialy useful on
  // the arguments object. Passing **n** will return all the values in
  // the array, excluding the last N. The **guard** check allows it to work with
  // `_.map`.
  _.initial = function(array, n, guard) {
    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
  };

  // Get the last element of an array. Passing **n** will return the last N
  // values in the array. The **guard** check allows it to work with `_.map`.
  _.last = function(array, n, guard) {
    return (n != null) && !guard ? slice.call(array, array.length - n) : array[array.length - 1];
  };

  // Returns everything but the first entry of the array. Aliased as `tail`.
  // Especially useful on the arguments object. Passing an **index** will return
  // the rest of the values in the array from that index onward. The **guard**
  // check allows it to work with `_.map`.
  _.rest = _.tail = function(array, index, guard) {
    return slice.call(array, (index == null) || guard ? 1 : index);
  };

  // Trim out all falsy values from an array.
  _.compact = function(array) {
    return _.filter(array, function(value){ return !!value; });
  };

  // Return a completely flattened version of an array.
  _.flatten = function(array) {
    return _.reduce(array, function(memo, value) {
      if (_.isArray(value)) return memo.concat(_.flatten(value));
      memo[memo.length] = value;
      return memo;
    }, []);
  };

  // Return a version of the array that does not contain the specified value(s).
  _.without = function(array) {
    return _.difference(array, slice.call(arguments, 1));
  };

  // Produce a duplicate-free version of the array. If the array has already
  // been sorted, you have the option of using a faster algorithm.
  // Aliased as `unique`.
  _.uniq = _.unique = function(array, isSorted, iterator) {
    var initial = iterator ? _.map(array, iterator) : array;
    var result = [];
    _.reduce(initial, function(memo, el, i) {
      if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
        memo[memo.length] = el;
        result[result.length] = array[i];
      }
      return memo;
    }, []);
    return result;
  };

  // Produce an array that contains the union: each distinct element from all of
  // the passed-in arrays.
  _.union = function() {
    return _.uniq(_.flatten(arguments));
  };

  // Produce an array that contains every item shared between all the
  // passed-in arrays. (Aliased as "intersect" for back-compat.)
  _.intersection = _.intersect = function(array) {
    var rest = slice.call(arguments, 1);
    return _.filter(_.uniq(array), function(item) {
      return _.every(rest, function(other) {
        return _.indexOf(other, item) >= 0;
      });
    });
  };

  // Take the difference between one array and another.
  // Only the elements present in just the first array will remain.
  _.difference = function(array, other) {
    return _.filter(array, function(value){ return !_.include(other, value); });
  };

  // Zip together multiple lists into a single array -- elements that share
  // an index go together.
  _.zip = function() {
    var args = slice.call(arguments);
    var length = _.max(_.pluck(args, 'length'));
    var results = new Array(length);
    for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
    return results;
  };

  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
  // we need this function. Return the position of the first occurrence of an
  // item in an array, or -1 if the item is not included in the array.
  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
  // If the array is large and already in sort order, pass `true`
  // for **isSorted** to use binary search.
  _.indexOf = function(array, item, isSorted) {
    if (array == null) return -1;
    var i, l;
    if (isSorted) {
      i = _.sortedIndex(array, item);
      return array[i] === item ? i : -1;
    }
    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
    for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
    return -1;
  };

  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
  _.lastIndexOf = function(array, item) {
    if (array == null) return -1;
    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
    var i = array.length;
    while (i--) if (array[i] === item) return i;
    return -1;
  };

  // Generate an integer Array containing an arithmetic progression. A port of
  // the native Python `range()` function. See
  // [the Python documentation](http://docs.python.org/library/functions.html#range).
  _.range = function(start, stop, step) {
    if (arguments.length <= 1) {
      stop = start || 0;
      start = 0;
    }
    step = arguments[2] || 1;

    var len = Math.max(Math.ceil((stop - start) / step), 0);
    var idx = 0;
    var range = new Array(len);

    while(idx < len) {
      range[idx++] = start;
      start += step;
    }

    return range;
  };

  // Function (ahem) Functions
  // ------------------

  // Create a function bound to a given object (assigning `this`, and arguments,
  // optionally). Binding with arguments is also known as `curry`.
  // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
  // We check for `func.bind` first, to fail fast when `func` is undefined.
  _.bind = function(func, obj) {
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    var args = slice.call(arguments, 2);
    return function() {
      return func.apply(obj, args.concat(slice.call(arguments)));
    };
  };

  // Bind all of an object's methods to that object. Useful for ensuring that
  // all callbacks defined on an object belong to it.
  _.bindAll = function(obj) {
    var funcs = slice.call(arguments, 1);
    if (funcs.length == 0) funcs = _.functions(obj);
    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
    return obj;
  };

  // Memoize an expensive function by storing its results.
  _.memoize = function(func, hasher) {
    var memo = {};
    hasher || (hasher = _.identity);
    return function() {
      var key = hasher.apply(this, arguments);
      return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
    };
  };

  // Delays a function for the given number of milliseconds, and then calls
  // it with the arguments supplied.
  _.delay = function(func, wait) {
    var args = slice.call(arguments, 2);
    return setTimeout(function(){ return func.apply(func, args); }, wait);
  };

  // Defers a function, scheduling it to run after the current call stack has
  // cleared.
  _.defer = function(func) {
    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
  };

  // Internal function used to implement `_.throttle` and `_.debounce`.
  var limit = function(func, wait, debounce) {
    var timeout;
    return function() {
      var context = this, args = arguments;
      var throttler = function() {
        timeout = null;
        func.apply(context, args);
      };
      if (debounce) clearTimeout(timeout);
      if (debounce || !timeout) timeout = setTimeout(throttler, wait);
    };
  };

  // Returns a function, that, when invoked, will only be triggered at most once
  // during a given window of time.
  _.throttle = function(func, wait) {
    return limit(func, wait, false);
  };

  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds.
  _.debounce = function(func, wait) {
    return limit(func, wait, true);
  };

  // Returns a function that will be executed at most one time, no matter how
  // often you call it. Useful for lazy initialization.
  _.once = function(func) {
    var ran = false, memo;
    return function() {
      if (ran) return memo;
      ran = true;
      return memo = func.apply(this, arguments);
    };
  };

  // Returns the first function passed as an argument to the second,
  // allowing you to adjust arguments, run code before and after, and
  // conditionally execute the original function.
  _.wrap = function(func, wrapper) {
    return function() {
      var args = [func].concat(slice.call(arguments));
      return wrapper.apply(this, args);
    };
  };

  // Returns a function that is the composition of a list of functions, each
  // consuming the return value of the function that follows.
  _.compose = function() {
    var funcs = slice.call(arguments);
    return function() {
      var args = slice.call(arguments);
      for (var i = funcs.length - 1; i >= 0; i--) {
        args = [funcs[i].apply(this, args)];
      }
      return args[0];
    };
  };

  // Returns a function that will only be executed after being called N times.
  _.after = function(times, func) {
    return function() {
      if (--times < 1) { return func.apply(this, arguments); }
    };
  };

  // Object Functions
  // ----------------

  // Retrieve the names of an object's properties.
  // Delegates to **ECMAScript 5**'s native `Object.keys`
  _.keys = nativeKeys || function(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
    return keys;
  };

  // Retrieve the values of an object's properties.
  _.values = function(obj) {
    return _.map(obj, _.identity);
  };

  // Return a sorted list of the function names available on the object.
  // Aliased as `methods`
  _.functions = _.methods = function(obj) {
    var names = [];
    for (var key in obj) {
      if (_.isFunction(obj[key])) names.push(key);
    }
    return names.sort();
  };

  // Extend a given object with all the properties in passed-in object(s).
  _.extend = function(obj) {
    each(slice.call(arguments, 1), function(source) {
      for (var prop in source) {
        if (source[prop] !== void 0) obj[prop] = source[prop];
      }
    });
    return obj;
  };

  // Fill in a given object with default properties.
  _.defaults = function(obj) {
    each(slice.call(arguments, 1), function(source) {
      for (var prop in source) {
        if (obj[prop] == null) obj[prop] = source[prop];
      }
    });
    return obj;
  };

  // Create a (shallow-cloned) duplicate of an object.
  _.clone = function(obj) {
    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  };

  // Invokes interceptor with the obj, and then returns obj.
  // The primary purpose of this method is to "tap into" a method chain, in
  // order to perform operations on intermediate results within the chain.
  _.tap = function(obj, interceptor) {
    interceptor(obj);
    return obj;
  };

  // Internal recursive comparison function.
  function eq(a, b, stack) {
    // Identical objects are equal. `0 === -0`, but they aren't identical.
    // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
    if (a === b) return a !== 0 || 1 / a == 1 / b;
    // A strict comparison is necessary because `null == undefined`.
    if (a == null) return a === b;
    // Compare object types.
    var typeA = typeof a;
    if (typeA != typeof b) return false;
    // Optimization; ensure that both values are truthy or falsy.
    if (!a != !b) return false;
    // `NaN` values are equal.
    if (_.isNaN(a)) return _.isNaN(b);
    // Compare string objects by value.
    var isStringA = _.isString(a), isStringB = _.isString(b);
    if (isStringA || isStringB) return isStringA && isStringB && String(a) == String(b);
    // Compare number objects by value.
    var isNumberA = _.isNumber(a), isNumberB = _.isNumber(b);
    if (isNumberA || isNumberB) return isNumberA && isNumberB && +a == +b;
    // Compare boolean objects by value. The value of `true` is 1; the value of `false` is 0.
    var isBooleanA = _.isBoolean(a), isBooleanB = _.isBoolean(b);
    if (isBooleanA || isBooleanB) return isBooleanA && isBooleanB && +a == +b;
    // Compare dates by their millisecond values.
    var isDateA = _.isDate(a), isDateB = _.isDate(b);
    if (isDateA || isDateB) return isDateA && isDateB && a.getTime() == b.getTime();
    // Compare RegExps by their source patterns and flags.
    var isRegExpA = _.isRegExp(a), isRegExpB = _.isRegExp(b);
    if (isRegExpA || isRegExpB) {
      // Ensure commutative equality for RegExps.
      return isRegExpA && isRegExpB &&
             a.source == b.source &&
             a.global == b.global &&
             a.multiline == b.multiline &&
             a.ignoreCase == b.ignoreCase;
    }
    // Ensure that both values are objects.
    if (typeA != 'object') return false;
    // Unwrap any wrapped objects.
    if (a._chain) a = a._wrapped;
    if (b._chain) b = b._wrapped;
    // Invoke a custom `isEqual` method if one is provided.
    if (_.isFunction(a.isEqual)) return a.isEqual(b);
    // Assume equality for cyclic structures. The algorithm for detecting cyclic structures is
    // adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
    var length = stack.length;
    while (length--) {
      // Linear search. Performance is inversely proportional to the number of unique nested
      // structures.
      if (stack[length] == a) return true;
    }
    // Add the first object to the stack of traversed objects.
    stack.push(a);
    var size = 0, result = true;
    if (a.length === +a.length || b.length === +b.length) {
      // Compare object lengths to determine if a deep comparison is necessary.
      size = a.length;
      result = size == b.length;
      if (result) {
        // Deep compare array-like object contents, ignoring non-numeric properties.
        while (size--) {
          // Ensure commutative equality for sparse arrays.
          if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
        }
      }
    } else {
      // Deep compare objects.
      for (var key in a) {
        if (hasOwnProperty.call(a, key)) {
          // Count the expected number of properties.
          size++;
          // Deep compare each member.
          if (!(result = hasOwnProperty.call(b, key) && eq(a[key], b[key], stack))) break;
        }
      }
      // Ensure that both objects contain the same number of properties.
      if (result) {
        for (key in b) {
          if (hasOwnProperty.call(b, key) && !size--) break;
        }
        result = !size;
      }
    }
    // Remove the first object from the stack of traversed objects.
    stack.pop();
    return result;
  }

  // Perform a deep comparison to check if two objects are equal.
  _.isEqual = function(a, b) {
    return eq(a, b, []);
  };

  // Is a given array or object empty?
  _.isEmpty = function(obj) {
    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
    for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
    return true;
  };

  // Is a given value a DOM element?
  _.isElement = function(obj) {
    return !!(obj && obj.nodeType == 1);
  };

  // Is a given value an array?
  // Delegates to ECMA5's native Array.isArray
  _.isArray = nativeIsArray || function(obj) {
    return toString.call(obj) === '[object Array]';
  };

  // Is a given variable an object?
  _.isObject = function(obj) {
    return obj === Object(obj);
  };

  // Is a given variable an arguments object?
  _.isArguments = function(obj) {
    return !!(obj && hasOwnProperty.call(obj, 'callee'));
  };

  // Is a given value a function?
  _.isFunction = function(obj) {
    return !!(obj && obj.constructor && obj.call && obj.apply);
  };

  // Is a given value a string?
  _.isString = function(obj) {
    return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
  };

  // Is a given value a number?
  _.isNumber = function(obj) {
    return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
  };

  // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
  // that does not equal itself.
  _.isNaN = function(obj) {
    return obj !== obj;
  };

  // Is a given value a boolean?
  _.isBoolean = function(obj) {
    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
  };

  // Is a given value a date?
  _.isDate = function(obj) {
    return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
  };

  // Is the given value a regular expression?
  _.isRegExp = function(obj) {
    return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
  };

  // Is a given value equal to null?
  _.isNull = function(obj) {
    return obj === null;
  };

  // Is a given variable undefined?
  _.isUndefined = function(obj) {
    return obj === void 0;
  };

  // Utility Functions
  // -----------------

  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  // previous owner. Returns a reference to the Underscore object.
  _.noConflict = function() {
    root._ = previousUnderscore;
    return this;
  };

  // Keep the identity function around for default iterators.
  _.identity = function(value) {
    return value;
  };

  // Run a function **n** times.
  _.times = function (n, iterator, context) {
    for (var i = 0; i < n; i++) iterator.call(context, i);
  };

  // Escape a string for HTML interpolation.
  _.escape = function(string) {
    return (''+string).replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
  };

  // Add your own custom functions to the Underscore object, ensuring that
  // they're correctly added to the OOP wrapper as well.
  _.mixin = function(obj) {
    each(_.functions(obj), function(name){
      addToWrapper(name, _[name] = obj[name]);
    });
  };

  // Generate a unique integer id (unique within the entire client session).
  // Useful for temporary DOM ids.
  var idCounter = 0;
  _.uniqueId = function(prefix) {
    var id = idCounter++;
    return prefix ? prefix + id : id;
  };

  // By default, Underscore uses ERB-style template delimiters, change the
  // following template settings to use alternative delimiters.
  _.templateSettings = {
    evaluate    : /<%([\s\S]+?)%>/g,
    interpolate : /<%=([\s\S]+?)%>/g,
    escape      : /<%-([\s\S]+?)%>/g
  };

  // JavaScript micro-templating, similar to John Resig's implementation.
  // Underscore templating handles arbitrary delimiters, preserves whitespace,
  // and correctly escapes quotes within interpolated code.
  _.template = function(str, data) {
    var c  = _.templateSettings;
    var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
      'with(obj||{}){__p.push(\'' +
      str.replace(/\\/g, '\\\\')
         .replace(/'/g, "\\'")
         .replace(c.escape, function(match, code) {
           return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
         })
         .replace(c.interpolate, function(match, code) {
           return "'," + code.replace(/\\'/g, "'") + ",'";
         })
         .replace(c.evaluate || null, function(match, code) {
           return "');" + code.replace(/\\'/g, "'")
                              .replace(/[\r\n\t]/g, ' ') + "__p.push('";
         })
         .replace(/\r/g, '\\r')
         .replace(/\n/g, '\\n')
         .replace(/\t/g, '\\t')
         + "');}return __p.join('');";
    var func = new Function('obj', tmpl);
    return data ? func(data) : func;
  };

  // The OOP Wrapper
  // ---------------

  // If Underscore is called as a function, it returns a wrapped object that
  // can be used OO-style. This wrapper holds altered versions of all the
  // underscore functions. Wrapped objects may be chained.
  var wrapper = function(obj) { this._wrapped = obj; };

  // Expose `wrapper.prototype` as `_.prototype`
  _.prototype = wrapper.prototype;

  // Helper function to continue chaining intermediate results.
  var result = function(obj, chain) {
    return chain ? _(obj).chain() : obj;
  };

  // A method to easily add functions to the OOP wrapper.
  var addToWrapper = function(name, func) {
    wrapper.prototype[name] = function() {
      var args = slice.call(arguments);
      unshift.call(args, this._wrapped);
      return result(func.apply(_, args), this._chain);
    };
  };

  // Add all of the Underscore functions to the wrapper object.
  _.mixin(_);

  // Add all mutator Array functions to the wrapper.
  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      method.apply(this._wrapped, arguments);
      return result(this._wrapped, this._chain);
    };
  });

  // Add all accessor Array functions to the wrapper.
  each(['concat', 'join', 'slice'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      return result(method.apply(this._wrapped, arguments), this._chain);
    };
  });

  // Start chaining a wrapped Underscore object.
  wrapper.prototype.chain = function() {
    this._chain = true;
    return this;
  };

  // Extracts the result from a wrapped and chained object.
  wrapper.prototype.value = function() {
    return this._wrapped;
  };

})();

})};

/********** path **********/

kanso.moduleCache["path"] = {load: (function (module, exports, require) {

/**
 * Path functions ported from node.js to work in CouchDB and the browser.
 * This module is used internally by Kanso, although you can use it in your
 * apps too if you find the functions useful.
 *
 * @module
 */

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

/**
 * From node.js v0.2.6
 */

/**
 * Joins multiple paths together using '/'. Accepts a arbitrary number of
 * strings as arguments, returning the joined result as a single string.
 *
 * @name join(path, [...])
 * @returns {String}
 * @api public
 */

exports.join = function () {
    return exports.normalize(Array.prototype.join.call(arguments, "/"));
};

/**
 * Normalizes a path split into an array, taking care of '..' and '.' parts.
 *
 * @name normalizeArray(parts, [keepBlanks])
 * @param {Array} parts
 * @param {Boolean} keepBlanks
 * @returns {Array}
 * @api public
 */

exports.normalizeArray = function (parts, keepBlanks) {
    var directories = [], prev;
    for (var i = 0, l = parts.length - 1; i <= l; i++) {
        var directory = parts[i];

        // if it's blank, but it's not the first thing, and not the last
        // thing, skip it.
        if (directory === "" && i !== 0 && i !== l && !keepBlanks) {
            continue;
        }

        // if it's a dot, and there was some previous dir already, then
        // skip it.
        if (directory === "." && prev !== undefined) {
            continue;
        }

        // if it starts with "", and is a . or .., then skip it.
        if (directories.length === 1 && directories[0] === "" && (directory === "." || directory === "..")) {
            continue;
        }

        if (directory === ".." && directories.length && prev !== ".." && prev !== "." && prev !== undefined && (prev !== "" || keepBlanks)) {
            directories.pop();
            prev = directories.slice(-1)[0];
        }
        else {
            if (prev === ".") {
                directories.pop();
            }
            directories.push(directory);
            prev = directory;
        }
    }
    return directories;
};

/**
 * Normalize a string path, taking care of '..' and '.' parts.
 *
 * @name normalize(path, [keepBlanks])
 * @param {String} path
 * @param {Boolean} keepBlanks
 * @returns string
 * @api public
 */

exports.normalize = function (path, keepBlanks) {
    return exports.normalizeArray(path.split("/"), keepBlanks).join("/");
};

/**
 * Return the directory name of a path. Similar to the Unix dirname command.
 *
 * @name dirname(path)
 * @param {String} path
 * @returns {String}
 * @api public
 */

exports.dirname = function (path) {
    if (path.length > 1 && '/' === path[path.length - 1]) {
        path = path.replace(/\/+$/, '');
    }
    var lastSlash = path.lastIndexOf('/');
    switch (lastSlash) {
    case -1:
        return '.';
    case 0:
        return '/';
    default:
        return path.substring(0, lastSlash);
    }
};

/**
 * Return the last portion of a path. Similar to the Unix basename command.
 *
 * **Example**
 * <pre><code class="javascript">path.basename('/foo/bar/baz/asdf/quux.html')
 * // returns
 * 'quux.html'
 *
 * path.basename('/foo/bar/baz/asdf/quux.html', '.html')
 * // returns
 * 'quux'
 * </code></pre>
 *
 * @name basename(path, ext)
 * @param {String} path
 * @param {String} ext
 * @returns {String}
 * @api public
 */

exports.basename = function (path, ext) {
    var f = path.substr(path.lastIndexOf("/") + 1);
    if (ext && f.substr(-1 * ext.length) === ext) {
        f = f.substr(0, f.length - ext.length);
    }
    return f;
};

/**
 * Return the extension of the path. Everything after the last '.' in the last
 * portion of the path. If there is no '.' in the last portion of the path or
 * the only '.' is the first character, then it returns an empty string.
 *
 * <pre><code class="javascript">path.extname('index.html')
 * // returns
 * '.html'
 *
 * path.extname('index')
 * // returns
 * ''
 * </code></pre>
 *
 * @name extname(path)
 * @param {String} path
 * @returns {String}
 * @api public
 */

exports.extname = function (path) {
    var dot = path.lastIndexOf('.'),
        slash = path.lastIndexOf('/');
    // The last dot must be in the last path component, and it (the last dot)
    // must not start the last path component (i.e. be a dot that signifies a
    // hidden file in UNIX).
    return dot <= slash + 1 ? '' : path.substring(dot);
};



})};

/********** querystring **********/

kanso.moduleCache["querystring"] = {load: (function (module, exports, require) {

var _ = require('underscore')._;

/**
 * Querystring functions ported from node.js to work in CouchDB and the browser.
 * This module is used internally by Kanso, although you can use it in your
 * apps too if you find the functions useful.
 *
 * @module
 */


// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// Query String Utilities

var QueryString = exports;

/**
 * Decodes a URI Component, provided so that it could be overridden if
 * necessary.
 *
 * @name unescape(str)
 * @param {String} str
 * @returns {String}
 * @api public
 */

QueryString.unescape = function (str) {
    return decodeURIComponent(str);
};

/**
 * Encodes a URI Component, provided so that it could be overridden if
 * necessary.
 *
 * @name escape(str)
 * @param {String} str
 * @returns {String}
 * @api public
 */

QueryString.escape = function (str) {
    return encodeURIComponent(str);
};

var stringifyPrimitive = function (v) {
    switch (typeof v) {
    case 'string':
        return v;

    case 'boolean':
        return v ? 'true' : 'false';

    case 'number':
        return isFinite(v) ? v : '';

    default:
        return '';
    }
};

/**
 * Serialize an object to a query string. Optionally override the default
 * separator and assignment characters.
 *
 * **Example:**
 *
 * <pre><code class="javascript">
 * querystring.stringify({foo: 'bar'})
 * // returns
 * 'foo=bar'
 *
 * querystring.stringify({foo: 'bar', baz: 'bob'}, ';', ':')
 * // returns
 * 'foo:bar;baz:bob'
 * </code></pre>
 *
 * @name stringify(obj, [sep, eq, name])
 * @param {Object} obj
 * @param {String} sep
 * @param {String} eq
 * @param {String} name
 * @returns {String}
 * @api public
 */

QueryString.stringify = QueryString.encode = function (obj, sep, eq, name) {
    sep = sep || '&';
    eq = eq || '=';
    obj = (obj === null) ? undefined : obj;

    if (typeof obj === 'object') {
        return _.map(_.keys(obj), function (k) {
            if (_.isArray(obj[k])) {
                return _.map(obj[k], function (v) {
                    return QueryString.escape(stringifyPrimitive(k)) +
                           eq +
                           QueryString.escape(stringifyPrimitive(v));
                }).join(sep);
            }
            else {
                return QueryString.escape(stringifyPrimitive(k)) +
                       eq +
                       QueryString.escape(stringifyPrimitive(obj[k]));
            }
        }).join(sep);
    }
    if (!name) {
        return '';
    }
    return QueryString.escape(stringifyPrimitive(name)) + eq +
           QueryString.escape(stringifyPrimitive(obj));
};

/**
 * Deserialize a query string to an object. Optionally override the default
 * separator and assignment characters.
 *
 * **Example:**
 *
 * <pre><code class="javascript">
 * querystring.parse('a=b&b=c')
 * // returns
 * // { a: 'b', b: 'c' }
 * </code></pre>
 *
 * @name decode(qs, [sep, eq])
 * @param {String} qs
 * @param {String} sep
 * @param {String} eq
 * @returns {Object}
 * @api public
 */

QueryString.parse = QueryString.decode = function (qs, sep, eq) {
    sep = sep || '&';
    eq = eq || '=';
    var obj = {};

    if (typeof qs !== 'string' || qs.length === 0) {
        return obj;
    }

    qs.split(sep).forEach(function (kvp) {
        var x = kvp.split(eq);
        var k = QueryString.unescape(x[0]);
        var v = QueryString.unescape(x.slice(1).join(eq));

        if (!(k in obj)) {
            obj[k] = v;
        }
        else if (!_.isArray(obj[k])) {
            obj[k] = [obj[k], v];
        }
        else {
            obj[k].push(v);
        }
    });

    return obj;
};


})};

/********** url **********/

kanso.moduleCache["url"] = {load: (function (module, exports, require) {

/**
 * URL functions ported from node.js to work in CouchDB and the browser.
 * This module is used internally by Kanso, although you can use it in your
 * apps too if you find the functions useful.
 *
 * @module
 */

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

/**
 * From node.js v0.2.6
 */

var path = require('path'),
    querystring = require('querystring'),
    _ = require('underscore')._;


// define these here so at least they only have to be compiled once on the
// first module load.
var protocolPattern = /^([a-z0-9]+:)/,
    portPattern = /:[0-9]+$/,
    nonHostChars = ["/", "?", ";", "#"],
    hostlessProtocol = {
        "file": true,
        "file:": true
    },
    slashedProtocol = {
        "http": true,
        "https": true,
        "ftp": true,
        "gopher": true,
        "file": true,
        "http:": true,
        "https:": true,
        "ftp:": true,
        "gopher:": true,
        "file:": true
    };


function parseHost(host) {
    var out = {};
    var at = host.indexOf("@");
    if (at !== -1) {
        out.auth = host.substr(0, at);
        host = host.substr(at + 1); // drop the @
    }
    var port = portPattern.exec(host);
    if (port) {
        port = port[0];
        out.port = port.substr(1);
        host = host.substr(0, host.length - port.length);
    }
    if (host) {
        out.hostname = host;
    }
    return out;
}

/**
 * Take a URL string, and return an object. Pass true as the second argument
 * to also parse the query string using the querystring module.
 *
 * @name parse(url, [parseQueryString, slashesDenoteHost])
 * @param {String} url
 * @param {Boolean} parseQueryString
 * @param {Boolean} slashesDenoteHost
 * @returns Object
 * @api public
 */

exports.parse = function (url, parseQueryString, slashesDenoteHost) {
    if (url && typeof(url) === "object" && url.href) {
        return url;
    }

    var out = { href : url },
        rest = url;

    var proto = protocolPattern.exec(rest);
    if (proto) {
        proto = proto[0];
        out.protocol = proto;
        rest = rest.substr(proto.length);
    }

    // figure out if it's got a host
    // user@server is *always* interpreted as a hostname, and url
    // resolution will treat //foo/bar as host=foo,path=bar because that's
    // how the browser resolves relative URLs.
    var slashes;
    var re = new RegExp('^\\/\\/[^@\\/]+@[^@\\/]+');
    if (slashesDenoteHost || proto || rest.match(re)) {
        slashes = rest.substr(0, 2) === "//";
        if (slashes && !(proto && hostlessProtocol[proto])) {
            rest = rest.substr(2);
            out.slashes = true;
        }
    }
    if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) {
        // there's a hostname.
        // the first instance of /, ?, ;, or # ends the host.
        // don't enforce full RFC correctness, just be unstupid about it.
        var firstNonHost = -1;
        for (var i = 0, l = nonHostChars.length; i < l; i++) {
            var index = rest.indexOf(nonHostChars[i]);
            if (index !== -1 && (firstNonHost < 0 || index < firstNonHost)) {
                firstNonHost = index;
            }
        }
        if (firstNonHost !== -1) {
            out.host = rest.substr(0, firstNonHost);
            rest = rest.substr(firstNonHost);
        } else {
            out.host = rest;
            rest = "";
        }

        // pull out the auth and port.
        var p = parseHost(out.host);
        var keys = _.keys(p);
        for (var j = 0, l2 = keys.length; j < l2; j++) {
            var key = keys[j];
            out[key] = p[key];
        }
        // we've indicated that there is a hostname, so even if it's empty, it has to be present.
        out.hostname = out.hostname || "";
    }

    // now rest is set to the post-host stuff.
    // chop off from the tail first.
    var hash = rest.indexOf("#");
    if (hash !== -1) {
        // got a fragment string.
        out.hash = rest.substr(hash);
        rest = rest.slice(0, hash);
    }
    var qm = rest.indexOf("?");
    if (qm !== -1) {
        out.search = rest.substr(qm);
        out.query = rest.substr(qm + 1);
        if (parseQueryString) {
            out.query = querystring.parse(out.query);
        }
        rest = rest.slice(0, qm);
    }
    if (rest) {
        out.pathname = rest;
    }

    return out;
};

/**
 * Take a parsed URL object, and return a formatted URL string.
 *
 * @name format(obj)
 * @param {Object} obj
 * @api public
 */

// format a parsed object into a url string
exports.format = function (obj) {
    // ensure it's an object, and not a string url. If it's an obj, this is a no-op.
    // this way, you can call url_format() on strings to clean up potentially wonky urls.
    if (typeof(obj) === "string") {
        obj = exports.parse(obj);
    }

    var protocol = obj.protocol || "",
        host = (obj.host !== undefined) ? obj.host
            : obj.hostname !== undefined ? ((obj.auth ? obj.auth + "@" : "") + obj.hostname + (obj.port ? ":" + obj.port : ""))
            : false,
        pathname = obj.pathname || "",
        search = obj.search || (
                obj.query && ("?" + (typeof(obj.query) === "object" ? querystring.stringify(obj.query) : String(obj.query)))
                ) || "",
        hash = obj.hash || "";

    if (protocol && protocol.substr(-1) !== ":") {
        protocol += ":";
    }

    // only the slashedProtocols get the //.  Not mailto:, xmpp:, etc.
    // unless they had them to begin with.
    if (obj.slashes || (!protocol || slashedProtocol[protocol]) && host !== false) {
        host = "//" + (host || "");
        if (pathname && pathname.charAt(0) !== "/") {
            pathname = "/" + pathname;
        }
    }
    else if (!host) {
        host = "";
    }

    if (hash && hash.charAt(0) !== "#") {
        hash = "#" + hash;
    }
    if (search && search.charAt(0) !== "?") {
        search = "?" + search;
    }

    return protocol + host + pathname + search + hash;
};

/**
 * Take a base URL, and a href URL, and resolve them as a browser would for
 * an anchor tag.
 *
 * @name resolve(source, relative)
 * @param {String} source
 * @param {String} relative
 * @returns {String}
 * @api public
 */

exports.resolve = function (source, relative) {
    return exports.format(exports.resolveObject(source, relative));
};

exports.resolveObject = function (source, relative) {
    if (!source) {
        return relative;
    }
    var relPath;

    source = exports.parse(exports.format(source), false, true);
    relative = exports.parse(exports.format(relative), false, true);

    // hash is always overridden, no matter what.
    source.hash = relative.hash;

    if (relative.href === "") {
        return source;
    }

    // hrefs like //foo/bar always cut to the protocol.
    if (relative.slashes && !relative.protocol) {
        relative.protocol = source.protocol;
        return relative;
    }

    if (relative.protocol && relative.protocol !== source.protocol) {
        // if it's a known url protocol, then changing the protocol does weird things
        // first, if it's not file:, then we MUST have a host, and if there was a path
        // to begin with, then we MUST have a path.
        // if it is file:, then the host is dropped, because that's known to be hostless.
        // anything else is assumed to be absolute.

        if (!slashedProtocol[relative.protocol]) {
            return relative;
        }

        source.protocol = relative.protocol;
        if (!relative.host && !hostlessProtocol[relative.protocol]) {
            relPath = (relative.pathname || "").split("/");
            while (relPath.length && !(relative.host = relPath.shift())) {}
            if (!relative.host) {
                relative.host = "";
            }
            if (relPath[0] !== "") {
                relPath.unshift("");
            }
            if (relPath.length < 2) {
                relPath.unshift("");
            }
            relative.pathname = relPath.join("/");
        }
        source.pathname = relative.pathname;
        source.search = relative.search;
        source.query = relative.query;
        source.host = relative.host || "";
        delete source.auth;
        delete source.hostname;
        source.port = relative.port;
        return source;
    }

    var isSourceAbs = (source.pathname && source.pathname.charAt(0) === "/"),
        isRelAbs = (relative.host !== undefined || relative.pathname && relative.pathname.charAt(0) === "/"),
        mustEndAbs = (isRelAbs || isSourceAbs || (source.host && relative.pathname)),
        removeAllDots = mustEndAbs,
        srcPath = source.pathname && source.pathname.split("/") || [],
        psychotic = source.protocol && !slashedProtocol[source.protocol] && source.host !== undefined;
    relPath = relative.pathname && relative.pathname.split("/") || [];

    // if the url is a non-slashed url, then relative links like ../.. should be able
    // to crawl up to the hostname, as well.  This is strange.
    // source.protocol has already been set by now.
    // Later on, put the first path part into the host field.
    if (psychotic) {

        delete source.hostname;
        delete source.auth;
        delete source.port;
        if (source.host) {
            if (srcPath[0] === "") {
                srcPath[0] = source.host;
            }
            else {
                srcPath.unshift(source.host);
            }
        }
        delete source.host;

        if (relative.protocol) {
            delete relative.hostname;
            delete relative.auth;
            delete relative.port;
            if (relative.host) {
                if (relPath[0] === "") {
                    relPath[0] = relative.host;
                }
                else {
                    relPath.unshift(relative.host);
                }
            }
            delete relative.host;
        }
        mustEndAbs = mustEndAbs && (relPath[0] === "" || srcPath[0] === "");
    }

    if (isRelAbs) {
        // it's absolute.
        source.host = (relative.host || relative.host === "") ? relative.host : source.host;
        source.search = relative.search;
        source.query = relative.query;
        srcPath = relPath;
        // fall through to the dot-handling below.
    }
    else if (relPath.length) {
        // it's relative
        // throw away the existing file, and take the new path instead.
        if (!srcPath) {
            srcPath = [];
        }
        srcPath.pop();
        srcPath = srcPath.concat(relPath);
        source.search = relative.search;
        source.query = relative.query;
    }
    else if ("search" in relative) {
        // just pull out the search.
        // like href="?foo".
        // Put this after the other two cases because it simplifies the booleans
        if (psychotic) {
            source.host = srcPath.shift();
        }
        source.search = relative.search;
        source.query = relative.query;
        return source;
    }
    if (!srcPath.length) {
        // no path at all.  easy.
        // we've already handled the other stuff above.
        delete source.pathname;
        return source;
    }

    // resolve dots.
    // if a url ENDs in . or .., then it must get a trailing slash.
    // however, if it ends in anything else non-slashy, then it must NOT get a trailing slash.
    var last = srcPath.slice(-1)[0];
    var hasTrailingSlash = ((source.host || relative.host) && (last === "." || last === "..") || last === "");

    // Figure out if this has to end up as an absolute url, or should continue to be relative.
    srcPath = path.normalizeArray(srcPath, true);
    if (srcPath.length === 1 && srcPath[0] === ".") {
        srcPath = [];
    }
    if (mustEndAbs || removeAllDots) {
        // all dots must go.
        var dirs = [];
        srcPath.forEach(function (dir, i) {
            if (dir === "..") {
                dirs.pop();
            }
            else if (dir !== ".") {
                dirs.push(dir);
            }
        });

        if (mustEndAbs && dirs[0] !== "") {
            dirs.unshift("");
        }
        srcPath = dirs;
    }
    if (hasTrailingSlash && (srcPath.length < 2 || srcPath.slice(-1)[0] !== "")) {
        srcPath.push("");
    }

    // put the host back
    if (psychotic) {
        source.host = srcPath[0] === "" ? "" : srcPath.shift();
    }

    mustEndAbs = mustEndAbs || (source.host && srcPath.length);

    if (mustEndAbs && srcPath[0] !== "") {
        srcPath.unshift("");
    }

    source.pathname = srcPath.join("/");

    return source;
};


})};

/********** events **********/

kanso.moduleCache["events"] = {load: (function (module, exports, require) {

/**
 * ## Events module
 *
 * This is a browser port of the node.js events module. Many objects and
 * modules emit events and these are instances of events.EventEmitter.
 *
 * You can access this module by doing: `require("events")`
 *
 * Functions can then be attached to objects, to be executed when an event is
 * emitted. These functions are called listeners.
 *
 * @module
 */


/**
 * To access the EventEmitter class, require('events').EventEmitter.
 *
 * When an EventEmitter instance experiences an error, the typical action is to
 * emit an 'error' event. Error events are treated as a special case. If there
 * is no listener for it, then the default action is for the error to throw.
 *
 * All EventEmitters emit the event 'newListener' when new listeners are added.
 *
 * @name events.EventEmitter
 * @api public
 *
 * ```javascript
 * var EventEmitter = require('events').EventEmitter;
 *
 * // create an event emitter
 * var emitter = new EventEmitter();
 * ```
 */

var EventEmitter = exports.EventEmitter = function () {};

var isArray = Array.isArray || function (obj) {
    return toString.call(obj) === '[object Array]';
};


/**
 * By default EventEmitters will print a warning if more than 10 listeners are
 * added for a particular event. This is a useful default which helps finding
 * memory leaks. Obviously not all Emitters should be limited to 10. This
 * function allows that to be increased. Set to zero for unlimited.
 *
 * @name emitter.setMaxListeners(n)
 * @param {Number} n - The maximum number of listeners
 * @api public
 */

// By default EventEmitters will print a warning if more than
// 10 listeners are added to it. This is a useful default which
// helps finding memory leaks.
//
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
var defaultMaxListeners = 10;
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!this._events) this._events = {};
  this._events.maxListeners = n;
};


/**
 * Execute each of the listeners in order with the supplied arguments.
 *
 * @name emitter.emit(event, [arg1], [arg2], [...])
 * @param {String} event - The event name/id to fire
 * @api public
 */

EventEmitter.prototype.emit = function(type) {
  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events || !this._events.error ||
        (isArray(this._events.error) && !this._events.error.length))
    {
      if (arguments[1] instanceof Error) {
        throw arguments[1]; // Unhandled 'error' event
      } else {
        throw new Error("Uncaught, unspecified 'error' event.");
      }
      return false;
    }
  }

  if (!this._events) return false;
  var handler = this._events[type];
  if (!handler) return false;

  if (typeof handler == 'function') {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        var args = Array.prototype.slice.call(arguments, 1);
        handler.apply(this, args);
    }
    return true;

  } else if (isArray(handler)) {
    var args = Array.prototype.slice.call(arguments, 1);

    var listeners = handler.slice();
    for (var i = 0, l = listeners.length; i < l; i++) {
      listeners[i].apply(this, args);
    }
    return true;

  } else {
    return false;
  }
};


/**
 * Adds a listener to the end of the listeners array for the specified event.
 *
 * @name emitter.on(event, listener) | emitter.addListener(event, listener)
 * @param {String} event - The event name/id to listen for
 * @param {Function} listener - The function to bind to the event
 * @api public
 *
 * ```javascript
 * session.on('change', function (userCtx) {
 *     console.log('session changed!');
 * });
 * ```
 */

// EventEmitter is defined in src/node_events.cc
// EventEmitter.prototype.emit() is also defined there.
EventEmitter.prototype.addListener = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('addListener only takes instances of Function');
  }

  if (!this._events) this._events = {};

  // To avoid recursion in the case that type == "newListeners"! Before
  // adding it to the listeners, first emit "newListeners".
  this.emit('newListener', type, listener);

  if (!this._events[type]) {
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  } else if (isArray(this._events[type])) {

    // Check for listener leak
    if (!this._events[type].warned) {
      var m;
      if (this._events.maxListeners !== undefined) {
        m = this._events.maxListeners;
      } else {
        m = defaultMaxListeners;
      }

      if (m && m > 0 && this._events[type].length > m) {
        this._events[type].warned = true;
        console.error('(node) warning: possible EventEmitter memory ' +
                      'leak detected. %d listeners added. ' +
                      'Use emitter.setMaxListeners() to increase limit.',
                      this._events[type].length);
        console.trace();
      }
    }

    // If we've already got an array, just append.
    this._events[type].push(listener);
  } else {
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

/**
 * Adds a one time listener for the event. This listener is invoked only the
 * next time the event is fired, after which it is removed.
 *
 * @name emitter.once(event, listener)
 * @param {String} event- The event name/id to listen for
 * @param {Function} listener - The function to bind to the event
 * @api public
 *
 * ```javascript
 * db.once('unauthorized', function (req) {
 *     // this event listener will fire once, then be unbound
 * });
 * ```
 */

EventEmitter.prototype.once = function(type, listener) {
  var self = this;
  self.on(type, function g() {
    self.removeListener(type, g);
    listener.apply(this, arguments);
  });

  return this;
};

/**
 * Remove a listener from the listener array for the specified event. Caution:
 * changes array indices in the listener array behind the listener.
 *
 * @name emitter.removeListener(event, listener)
 * @param {String} event - The event name/id to remove the listener from
 * @param {Function} listener - The listener function to remove
 * @api public
 *
 * ```javascript
 * var callback = function (init) {
 *     console.log('duality app loaded');
 * };
 * devents.on('init', callback);
 * // ...
 * devents.removeListener('init', callback);
 * ```
 */

EventEmitter.prototype.removeListener = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('removeListener only takes instances of Function');
  }

  // does not use listeners(), so no side effect of creating _events[type]
  if (!this._events || !this._events[type]) return this;

  var list = this._events[type];

  if (isArray(list)) {
    var i = list.indexOf(listener);
    if (i < 0) return this;
    list.splice(i, 1);
    if (list.length == 0)
      delete this._events[type];
  } else if (this._events[type] === listener) {
    delete this._events[type];
  }

  return this;
};

/**
 * Removes all listeners, or those of the specified event.
 *
 * @name emitter.removeAllListeners([event])
 * @param {String} event - Event name/id to remove all listeners for (optional)
 * @api public
 */

EventEmitter.prototype.removeAllListeners = function(type) {
  // does not use listeners(), so no side effect of creating _events[type]
  if (type && this._events && this._events[type]) this._events[type] = null;
  return this;
};

/**
 * Returns an array of listeners for the specified event. This array can be
 * manipulated, e.g. to remove listeners.
 *
 * @name emitter.listeners(event)
 * @param {String} events - The event name/id to return listeners for
 * @api public
 *
 * ```javascript
 * session.on('change', function (stream) {
 *     console.log('session changed');
 * });
 * console.log(util.inspect(session.listeners('change'))); // [ [Function] ]
 * ```
 */

EventEmitter.prototype.listeners = function(type) {
  if (!this._events) this._events = {};
  if (!this._events[type]) this._events[type] = [];
  if (!isArray(this._events[type])) {
    this._events[type] = [this._events[type]];
  }
  return this._events[type];
};


/**
 * @name emitter Event: 'newListener'
 *
 * This event is emitted any time someone adds a new listener.
 *
 * ```javascript
 * emitter.on('newListener', function (event, listener) {
 *     // new listener added
 * });
 * ```
 */


})};

/********** db **********/

kanso.moduleCache["db"] = {load: (function (module, exports, require) {

/*global $: false */

/**
 * ## DB Module
 *
 * This contains the core functions for dealing with CouchDB. That includes
 * document CRUD operations, querying views and creating/deleting databases.
 *
 *
 * ### Events
 *
 * The db module is an EventEmitter. See the
 * [events package](http://kan.so/packages/details/events) for more information.
 *
 * #### unauthorized
 *
 * Emitted by the db module when a request results in a 401 Unauthorized
 * response. This is listened to used by the session module to help detect
 * session timeouts etc.
 *
 * ```javascript
 * var db = require("db");
 *
 * db.on('unauthorized', function (req) {
 *     // req is the ajax request object which returned 401
 * });
 * ```
 *
 * @module
 */


var events = require('events'),
    _ = require('underscore')._;


/**
 * Tests if running in the browser
 *
 * @returns {Boolean}
 */

function isBrowser() {
    return (typeof(window) !== 'undefined');
}


/**
 * This module is an EventEmitter, used for emitting 'unauthorized' events
 */

var exports = module.exports = new events.EventEmitter();


/**
 * Taken from jQuery 1.4.4 so we can support more recent versions of jQuery.
 */

var httpData = function (xhr, type, s) {
    var ct = xhr.getResponseHeader("content-type") || "",
        xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
        data = xml ? xhr.responseXML : xhr.responseText;

    if (xml && data.documentElement.nodeName === "parsererror") {
        $.error("parsererror");
    }
    if (s && s.dataFilter) {
        data = s.dataFilter(data, type);
    }
    if (typeof data === "string") {
        if (type === "json" || !type && ct.indexOf("json") >= 0) {
            data = $.parseJSON(data);
        }
        else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
            $.globalEval(data);
        }
    }
    return data;
};


/**
 * Returns a function for handling ajax responses from jquery and calls
 * the callback with the data or appropriate error.
 *
 * @param {Function} callback(err,response)
 * @api private
 */

function onComplete(options, callback) {
    return function (req) {
        var resp;
        var ctype = req.getResponseHeader('Content-Type');
        if (ctype === 'application/json' || ctype === 'text/json') {
            try {
                resp = httpData(req, "json");
            }
            catch (e) {
                return callback(e);
            }
        }
        else {
            if (options.expect_json) {
                try {
                    resp = httpData(req, "json");
                }
                catch (ex) {
                    return callback(
                        new Error('Expected JSON response, got ' + ctype)
                    );
                }
            }
            else {
                resp = req.responseText;
            }
        }
        if (req.status === 401) {
            exports.emit('unauthorized', req);
        }
        if (req.status === 200 || req.status === 201 || req.status === 202) {
            callback(null, resp);
        }
        else if (resp.error || resp.reason) {
            var err = new Error(resp.reason || resp.error);
            err.error = resp.error;
            err.reason = resp.reason;
            err.code = resp.code;
            err.status = req.status;
            callback(err);
        }
        else {
            // TODO: map status code to meaningful error message
            var err2 = new Error('Returned status code: ' + req.status);
            err2.status = req.status;
            callback(err2);
        }
    };
}


/**
 * Attempts to guess the database name and design doc id from the current URL,
 * or the loc paramter. Returns an object with 'db', 'design_doc' and 'root'
 * properties, or null for a URL not matching the expected format (perhaps
 * behing a vhost).
 *
 * You wouldn't normally use this function directly, but use `db.current()` to
 * return a DB object bound to the current database instead.
 *
 * @name guessCurrent([loc])
 * @param {String} loc - An alternative URL to use instead of window.location
 *     (optional)
 * @returns {Object|null} - An object with 'db', 'design_doc' and 'root'
 *     properties, or null for a URL not matching the
 *     expected format (perhaps behing a vhost)
 * @api public
 */

exports.guessCurrent = function (loc) {
    var loc = loc || window.location;

    /**
     * A database must be named with all lowercase letters (a-z), digits (0-9),
     * or any of the _$()+-/ characters and must end with a slash in the URL.
     * The name has to start with a lowercase letter (a-z).
     *
     * http://wiki.apache.org/couchdb/HTTP_database_API
     */

    var re = /\/([a-z][a-z0-9_\$\(\)\+-\/]*)\/_design\/([^\/]+)\//;
    var match = re.exec(loc.pathname);

    if (match) {
        return {
            db: match[1],
            design_doc: match[2],
            root: '/'
        }
    }
    return null;
};

/**
 * Converts an object to a string of properly escaped URL parameters.
 *
 * @name escapeUrlParams([obj])
 * @param {Object} obj - An object containing url parameters, with
 *       parameter names stored as property names (or keys).
 * @returns {String}
 * @api public
 */

exports.escapeUrlParams = function (obj) {
    var rv = [ ];
    for (var key in obj) {
        rv.push(
            encodeURIComponent(key) +
                '=' + encodeURIComponent(obj[key])
        );
    }
    return (rv.length > 0 ? ('?' + rv.join('&')) : '');
};

/**
 * Encodes a document id or view, list or show name. This also will make sure
 * the forward-slash is not escaped for documents with id's beginning with
 * "\_design/".
 *
 * @name encode(str)
 * @param {String} str - the name or id to escape
 * @returns {String}
 * @api public
 */

exports.encode = function (str) {
    return encodeURIComponent(str).replace(/^_design%2F/, '_design/');
};


/**
 * Properly encodes query parameters to CouchDB views etc. Handle complex
 * keys and other non-string parameters by passing through JSON.stringify.
 * Returns a shallow-copied clone of the original query after complex values
 * have been stringified.
 *
 * @name stringifyQuery(query)
 * @param {Object} query
 * @returns {Object}
 * @api public
 */

exports.stringifyQuery = function (query) {
    var q = {};
    for (var k in query) {
        if (typeof query[k] !== 'string') {
            q[k] = JSON.stringify(query[k]);
        }
        else {
            q[k] = query[k];
        }
    }
    return q;
};


/**
 * Make a request, with some default settings, proper callback
 * handling, and optional caching. Used behind-the-scenes by
 * most other DB module functions.
 *
 * @name request(options, callback)
 * @param {Object} options
 * @param {Function} callback(err,response)
 * @api public
 */

exports.request = function (options, callback) {
    options.complete = onComplete(options, callback);
    options.dataType = 'json';
    $.ajax(options);
};


/**
 * Creates a CouchDB database.
 *
 * If you're running behind a virtual host you'll need to set up
 * appropriate rewrites for a DELETE request to '/' either turning off safe
 * rewrites or setting up a new vhost entry.
 *
 * @name createDatabase(name, callback)
 * @param {String} name
 * @param {Function} callback(err,response)
 * @api public
 */

exports.createDatabase = function (name, callback) {
    var req = {
        type: 'PUT',
        url: '/' + exports.encode(name.replace(/^\/+/, ''))
    };
    exports.request(req, callback);
};

/**
 * Deletes a CouchDB database.
 *
 * If you're running behind a virtual host you'll need to set up
 * appropriate rewrites for a DELETE request to '/' either turning off safe
 * rewrites or setting up a new vhost entry.
 *
 * @name deleteDatabase(name, callback)
 * @param {String} name
 * @param {Function} callback(err,response)
 * @api public
 */

// TODO: detect when 'name' argument is a url and don't construct a url then
exports.deleteDatabase = function (name, callback) {
    var req = {
        type: 'DELETE',
        url: '/' + exports.encode(name.replace(/^\/+/, ''))
    };
    exports.request(req, callback);
};


/**
 * Lists all databses
 *
 * If you're running behind a virtual host you'll need to set up
 * appropriate rewrites for a DELETE request to '/' either turning off safe
 * rewrites or setting up a new vhost entry.
 *
 * @name allDbs(callback)
 * @param {Function} callback(err,response)
 * @api public
 */

exports.allDbs = function (callback) {
    var req = {
        type: 'GET',
        url: '/_all_dbs'
    };
    exports.request(req, callback);
};


/**
 * Returns a new UUID generated by CouchDB. Its possible to cache
 * multiple UUIDs for later use, to avoid making too many requests.
 *
 * @name newUUID(cacheNum, callback)
 * @param {Number} cacheNum (optional, default: 1)
 * @param {Function} callback(err,response)
 * @api public
 */

var uuidCache = [];

exports.newUUID = function (cacheNum, callback) {
    if (!callback) {
        callback = cacheNum;
        cacheNum = 1;
    }
    if (uuidCache.length) {
        return callback(null, uuidCache.shift());
    }
    var req = {
        url: '/_uuids',
        data: {count: cacheNum},
        expect_json: true
    };
    exports.request(req, function (err, resp) {
        if (err) {
            return callback(err);
        }
        uuidCache = resp.uuids;
        callback(null, uuidCache.shift());
    });
};


/**
 * DB object created by use(dbname) function
 */

function DB(url) {
    this.url = url;
    // add the module functions to the DB object
    for (var k in exports) {
        this[k] = exports[k];
    }
};


/**
 * Creates a new DB object with methods operating on the database at 'url'
 *
 * The DB object also exposes the same module-level methods (eg, createDatabase)
 * so it can be used in-place of the db exports object, for example:
 *
 * ```javascript
 * var db = require('db').use('mydb');
 *
 * db.createDatabase('example', function (err, resp) {
 *     // do something
 * });
 * ```
 *
 * @name use(url)
 * @param {String} url - The url to bind the new DB object to
 * @returns {DB}
 * @api public
 */

// TODO: handle full urls, not just db names
exports.use = function (url) {
    /* Force leading slash; make absolute path */
    return new DB((url.substr(0, 1) !== '/' ? '/' : '') + url);
};

/**
 * Attempts to guess the current DB name and return a DB object using that.
 * Should work reliably unless running behind a virtual host.
 *
 * Throws an error if the current database url cannot be detected.
 *
 * The DB object also exposes the same module-level methods (eg, createDatabase)
 * so it can be used in-place of the db exports object, for example:
 *
 * ```javascript
 * var db = require('db').current();
 *
 * db.createDatabase('example', function (err, resp) {
 *     // do something
 * });
 * ```
 *
 * @name current()
 * @returns {DB}
 * @api public
 */

exports.current = function () {
    // guess current db url etc
    var curr = exports.guessCurrent();
    if (!curr) {
        throw new Error(
            'Cannot guess current database URL, if running behind a virtual ' +
            'host you need to explicitly set the database URL using ' +
            'db.use(database_url) instead of db.current()'
        );
    }
    return exports.use(curr.db);
};


/**
 * Fetches a rewrite from the database the app is running on. Results
 * are passed to the callback, with the first argument of the callback
 * reserved for any exceptions that occurred (node.js style).
 *
 * @name DB.getRewrite(name, path, [q], callback)
 * @param {String} name - the name of the design doc
 * @param {String} path
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.getRewrite = function (name, path, /*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    // prepend forward-slash if missing
    path = (path[0] === '/') ? path: '/' + path;

    var req = {
        url: this.url + '/_design/' + exports.encode(name) + '/_rewrite' + path,
        data: exports.stringifyQuery(q)
    };
    exports.request(req, callback);
};


/**
 * Queries all design documents in the database.
 *
 * @name DB.allDesignDocs([q], callback)
 * @param {Object} q - query parameters to pass to /_all_docs (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.allDesignDocs = function (/*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    q.startkey = '"_design"';
    q.endkey = '"_design0"';
    this.allDocs(q, callback);
};


/**
 * Queries all documents in the database (include design docs).
 *
 * @name DB.allDocs([q], callback)
 * @param {Object} q - query parameters to pass to /_all_docs (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.allDocs = function (/*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    var req = {
        url: this.url + '/_all_docs',
        data: exports.stringifyQuery(q),
        expect_json: true
    };
    exports.request(req, callback);
};


/**
 * Fetches a document from the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * @name DB.getDoc(id, [q], callback)
 * @param {String} id
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.getDoc = function (id, /*optional*/q, callback) {
    if (!id) {
        throw new Error('getDoc requires an id parameter to work properly');
    }
    if (!callback) {
        callback = q;
        q = {};
    }
    var req = {
        url: this.url + '/' + exports.encode(id),
        expect_json: true,
        data: exports.stringifyQuery(q)
    };
    exports.request(req, callback);
};


/**
 * Saves a document to the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * @name DB.saveDoc(doc, callback)
 * @param {Object} doc
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.saveDoc = function (doc, callback) {
    var method, url = this.url;
    if (doc._id === undefined) {
        method = "POST";
    }
    else {
        method = "PUT";
        url += '/' + doc._id;
    }
    var req = {
        type: method,
        url: url,
        data: JSON.stringify(doc),
        processData: false,
        contentType: 'application/json',
        expect_json: true
    };
    exports.request(req, callback);
};

/**
 * Deletes a document from the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * @name DB.removeDoc(doc, callback)
 * @param {Object} doc
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.removeDoc = function (doc, callback) {
    if (!doc._id) {
        throw new Error('removeDoc requires an _id field in your document');
    }
    if (!doc._rev) {
        throw new Error('removeDoc requires a _rev field in your document');
    }
    var req = {
        type: 'DELETE',
        url: this.url + '/' + exports.encode(doc._id) +
             '?rev=' + exports.encode(doc._rev)
    };
    exports.request(req, callback);
};


/**
 * Fetches a view from the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * @name DB.getView(name, view, [q], callback)
 * @param {String} name - name of the design doc to use
 * @param {String} view - name of the view
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.getView = function (name, view, /*opt*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    var viewname = exports.encode(view);
    var req = {
        url: (this.url +
            '/_design/' + exports.encode(name) +
            '/_view/' + viewname
        ),
        expect_json: true,
        data: exports.stringifyQuery(q)
    };
    exports.request(req, callback);
};


/**
 * Transforms and fetches a view through a list from the database the app
 * is running on. Results are passed to the callback, with the first
 * argument of the callback reserved for any exceptions that occurred
 * (node.js style).
 *
 * @name DB.getList(name, list, view, [q], callback)
 * @param {String} name - name of the design doc to use
 * @param {String} list - name of the list function
 * @param {String} view - name of the view to apply the list function to
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

// TODO: run list function client-side?
DB.prototype.getList = function (name, list, view, /*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    var listname = exports.encode(list);
    var viewname = exports.encode(view);
    var req = {
        url: this.url + '/_design/' + exports.encode(name) +
            '/_list/' + listname + '/' + viewname,
        data: exports.stringifyQuery(q)
    };
    exports.request(req, callback);
};

/**
 * Transforms and fetches a document through a show from the database the app
 * is running on. Results are passed to the callback, with the first
 * argument of the callback reserved for any exceptions that occurred
 * (node.js style).
 *
 * @name DB.getShow(name, show, docid, [q], callback)
 * @param {String} name - name of the design doc to use
 * @param {String} show - name of the show function
 * @param {String} docid - id of the document to apply the show function to
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

// TODO: run show function client-side?
DB.prototype.getShow = function (name, show, docid, /*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    var showname = exports.encode(show);
    var show_url = this.url + '/_design/' +
        exports.encode(name) + '/_show/' + exports.encode(showname);
    var req = {
        url: show_url + (docid ? '/' + exports.encode(docid): ''),
        data: exports.stringifyQuery(q)
    };
    exports.request(req, callback);
};


/**
 * Fetch a design document from CouchDB.
 *
 * @name DB.getDesignDoc(name, callback)
 * @param name The name of (i.e. path to) the design document without the
 *     preceeding "\_design/".
 * @param callback The callback to invoke when the request completes.
 * @api public
 */

DB.prototype.getDesignDoc = function (name, callback) {
    this.getDoc('_design/' + name, function (err, ddoc) {
        if (err) {
            return callback(err);
        }
        return callback(null, ddoc);
    });
};

/**
 * Gets information about the database.
 *
 * @name DB.info(callback)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.info = function (callback) {
    var req = {
        url: this.url,
        expect_json: true,
    };
    exports.request(req, callback);
};


/**
 * Listen to the changes feed for a database.
 *
 * __Options:__
 * * _filter_ - the filter function to use
 * * _since_ - the update_seq to start listening from
 * * _heartbeat_ - the heartbeat time (defaults to 10 seconds)
 * * _include_docs_ - whether to include docs in the results
 *
 * Returning false from the callback will cancel the changes listener
 *
 * @name DB.changes([q], callback)
 * @param {Object} q (optional) query parameters (see options above)
 * @param {Function} callback(err,response)
 * @api public
 */

// TODO: change this to use an EventEmitter
DB.prototype.changes = function (/*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }

    var that = this;

    q = q || {};
    q.feed = 'longpoll';
    q.heartbeat = q.heartbeat || 10000;

    function getChanges(since) {
        q.since = since;
        var req = {
            type: 'GET',
            expect_json: true,
            url: that.url + '/_changes',
            data: exports.stringifyQuery(q)
        };
        var cb = function (err, data) {
            var result = callback.apply(this, arguments);
            if (result !== false) {
                getChanges(data.last_seq);
            }
        }
        exports.request(req, cb);
    }

    // use setTimeout to pass control back to the browser briefly to
    // allow the loading spinner to stop on page load
    setTimeout(function () {
        if (q.hasOwnProperty('since')) {
            getChanges(q.since);
        }
        else {
            that.info(function (err, info) {
                if (err) {
                    return callback(err);
                }
                getChanges(info.update_seq);
            });
        }
    }, 0);
};


/**
 * Saves a list of documents, without using separate requests.
 * This function uses CouchDB's HTTP bulk document API (_bulk_docs).
 * The return value is an array of objects, each containing an 'id'
 * and a 'rev' field. The return value is passed to the callback you
 * provide via its second argument; the first argument of the callback
 * is reserved for any exceptions that occurred (node.js style).
 *
 * **Options:**
 * * *all_or\_nothing* - Require that all documents be saved
 *   successfully (or saved with a conflict); otherwise roll
 *   back the operation.
 *
 * @name DB.bulkSave(docs, [options], callback)
 * @param {Array} docs An array of documents; each document is an object
 * @param {Object} options (optional) Options for the bulk-save operation.
 * @param {Function} callback(err,response) - A function to accept results
 *          and/or errors. Document update conflicts are reported in the
 *          results array.
 * @api public
 */

DB.prototype.bulkSave = function (docs, /*optional*/ options, callback) {
    if (!_.isArray(docs)) {
        throw new Error(
            'bulkSave requires an array of documents to work properly'
        );
    }
    if (!callback) {
        callback = options;
        options = {};
    }
    options.docs = doc;
    var req = {
        type: 'POST',
        url: this.url + '/_bulk_docs',
        data: JSON.stringify(options),
        processData: false,
        contentType: 'application/json',
        expect_json: true
    };
    exports.request(req, callback);
};


/**
 * Requests a list of documents, using only a single HTTP request.
 * This function uses CouchDB's HTTP bulk document API (_all_docs).
 * The return value is an array of objects, each of which is a document.
 * The return value is passed to the callback you provide via its second
 * argument; the first argument of the callback is reserved for any
 * exceptions that occurred (node.js style).
 *
 * @name DB.bulkGet(keys, [q], callback)
 * @param {Array} keys An array of documents identifiers (i.e. strings).
 * @param {Object} q (optional) Query parameters for the bulk-read operation.
 * @param {Function} callback(err,response) - A function to accept results
 *          and/or errors. Document update conflicts are reported in the
 *          results array.
 * @api public
 */

DB.prototype.bulkGet = function (keys, /*optional*/ q, callback) {
    if (keys && !_.isArray(keys)) {
        throw new Error(
            'bulkGet requires that _id values be supplied as a list'
        );
    }
    if (!callback) {
        callback = q;
        q = {};
    }

    /* Encode every query-string option:
        CouchDB requires that these be JSON, even though they
        will be URL-encoded as part of the request process. */

    for (var k in q) {
        q[k] = JSON.stringify(q[k]);
    }

    /* Make request:
        If we have a list of keys, use a post request containing
        a JSON-encoded list of keys. Otherwise, use a get request. */

    var req = {
        expect_json: true,
        url: this.url + '/_all_docs' + exports.escapeUrlParams(q)
    };
    if (keys) {
        req = _.extend(req, {
            type: 'POST',
            processData: false,
            contentType: 'application/json',
            data: JSON.stringify({ keys: keys })
        });
    } else {
        req = _.extend(req, {
            type: 'GET'
        });
    }

    exports.request(req, callback);
};


/**
 * DB methods can only be called client-side
 */

_.each(_.keys(DB.prototype), function (k) {
    var _fn = DB.prototype[k];
    DB.prototype[k] = function () {
        if (!isBrowser()) {
            throw new Error(k + ' cannot be called server-side');
        }
        return _fn.apply(this, arguments);
    };
});


})};

/********** session **********/

kanso.moduleCache["session"] = {load: (function (module, exports, require) {

/**
 * ## Session module
 *
 * This module contains functions related to session management. Logging in,
 * logging out and checking the current state of a user's session.
 *
 * Functions in this module follow the node.js callback style. The first
 * argument is an error object (if one occurred), the following arguments are
 * the results of the operation. The callback is always the last argument to a
 * function.
 *
 *
 * ### Events
 *
 * The session module is an EventEmitter. See the
 * [events package](http://kan.so/packages/details/events) for more information.
 *
 * #### change
 *
 * Emitted whenever a change to the user's session is detected, this
 * can occur as the result of a login/logout call or by getting the user's
 * session info (and it's changed).
 *
 * ```javascript
 * var session = require("session");
 *
 * session.on('change', function (userCtx) {
 *     // update session information, eg "Logged in as ..."
 * });
 * ```
 *
 * @module
 */


var events = require('events'),
    db = require('db');


/**
 * Quick utility function for testing if running in the browser, since
 * these functions won't run on CouchDB server-side
 */

function isBrowser() {
    return (typeof(window) !== 'undefined');
}

/**
 * When a db call results in an unauthorized response, the user's session is
 * checked to see if their session has timed out or they've logged out in
 * another screen.
 *
 * This check is throttled to once per second, to avoid flooding the server if
 * multiple requests are made with incorrect permissions.
 */

var last_session_check = 0;

db.on('unauthorized', function (req) {
    // db call returned 'Unauthorized', check the user's session if it's not
    // been checked on an 'Unauthorized' repsonse in the last second
    if (last_session_check < new Date().getTime() - 1000) {
        exports.info();
    }
});


/**
 * This module is an EventEmitter, used for handling 'change' events
 */

var exports = module.exports = new events.EventEmitter();


/**
 * Attempt to login using the username and password provided.
 *
 * @name login(username, password, callback)
 * @param {String} username - the username to login with
 * @param {String} password - the user's password (unhashed)
 * @param {Function} callback - function called with the result of the login
 *     attempt
 * @api public
 *
 * ```javascript
 * session.login('testuser', 'password', function (err, response) {
 *     if (err) // an error occurred logging in
 *     else     // success
 * });
 * ```
 */

exports.login = function (username, password, callback) {
    if (!isBrowser()) {
        throw new Error('login cannot be called server-side');
    }
    db.request({
        type: "POST",
        url: "/_session",
        data: {name: username, password: password}
    },
    function (err, resp) {
        if (resp && resp.ok) {
            // TODO: for some reason resp.name is set to null in the response
            // even though the roles are correct for the user! Look into this
            // and see if its a bug in couchdb, for now, just using the username
            // given to the login function instead, since we know the login
            // request was accepted.
            exports.userCtx = {name: username, roles: resp.roles};
            exports.session = {userCtx: exports.userCtx};
            exports.emit('change', exports.userCtx);
        }
        if (callback) {
            callback(err, resp);
        }
    });
};


/**
 * Logs out the current user.
 *
 * @name logout(callback)
 * @param {Function} callback - function called with the result of the logout
 *     attempt
 * @api public
 *
 * ```javascript
 * session.logout(function (err, response) {
 *     if (err) // an error occurred logging out
 *     else     // success
 * });
 * ```
 */

exports.logout = function (callback) {
    if (!isBrowser()) {
        throw new Error('logout cannot be called server-side');
    }
    db.request({
        type: "DELETE",
        url: "/_session", // don't need baseURL, /_session always available
        username: "_",
        password : "_"
    },
    function (err, resp) {
        if (resp && resp.ok) {
            exports.userCtx = {name: null, roles: []};
            exports.session = {userCtx: exports.userCtx};
            exports.emit('change', exports.userCtx);
        }
        if (callback) {
            callback(err, resp);
        }
    });
};


/**
 * Returns the current user's session information. The info object contains a
 * `userCtx` property and an `info` property. The first contains the name and
 * roles of the current user, the second contains information about the user
 * database and authentication handlers.
 *
 * @name info(callback)
 * @param {Function} callback - function called with the session information
 * @api public
 *
 * ```javascript
 * session.info(function (err, info) {
 *     if (err) // an error occurred getting session info
 *     else     // success
 * });
 * ```
 */

exports.info = function (callback) {
    if (!isBrowser()) {
        throw new Error('info cannot be called server-side');
    }
    db.request({
        type: "GET",
        url: "/_session"
    },
    function (err, resp) {
        var oldUserCtx = exports.userCtx;
        exports.session = resp;
        exports.userCtx = (resp && resp.userCtx) || {name: null, roles: []};
        // TODO: should this check for differences in more than just name?
        if (!oldUserCtx || oldUserCtx.name !== exports.userCtx.name) {
            exports.emit('change', exports.userCtx);
        }
        if (callback) {
            callback(err, resp);
        }
    });
};


})};

/********** cookies **********/

kanso.moduleCache["cookies"] = {load: (function (module, exports, require) {

/*global escape: false */

/**
 * Functions related to the manipulation and reading of cookies.
 *
 * @module
 */

function isBrowser() {
    return (typeof(window) !== 'undefined');
}


/**
 * Read cookies currently stored in the browser, returning an object
 * keyed by cookie name.
 *
 * @name readBrowserCookies()
 * @returns Object
 * @api public
 */

exports.readBrowserCookies = function () {
    if (!isBrowser()) {
        throw new Error('readBrowserCookies cannot be called server-side');
    }
    var cookies = {};
    var parts = document.cookie.split(';');
    for (var i = 0, len = parts.length; i < len; i++) {
        var name = parts[i].split('=')[0];
        var value = parts[i].split('=').slice(1).join('=');
        cookies[name] = value;
    }
    return cookies;
};

/**
 * Reads browser cookies and returned the value of the named cookie.
 *
 * @name readBrowserCookie(name)
 * @returns {String}
 * @api public
 */

exports.readBrowserCookie = function (name) {
    return exports.readBrowserCookies()[name];
};

/**
 * Creates a string for storing a cookie on the browser.
 *
 * @name cookieString(req, opt)
 * @param {Request Object} req
 * @param {Object} opt
 * @returns {String}
 * @api public
 */

exports.cookieString = function (req, opt) {
    var str = escape(opt.name) + '=' + escape(opt.value) + '; path=' + opt.path;
    if (opt.days) {
        var expires = new Date().setTime(
            new Date().getTime() + 1000 * 60 * 60 * 24 * opt.days
        );
        str += '; expires=' + expires.toGMTString();
    }
    return str;
};

/**
 * Sets a cookie on the browser, for use client-side only.
 *
 * @name setBrowserCookie(req, opt)
 * @param {Request Object} req
 * @param {Object} opt
 * @api public
 */

exports.setBrowserCookie = function (req, opt) {
    if (!isBrowser()) {
        throw new Error('setBrowserCookie cannot be called server-side');
    }
    var str = (typeof opt === 'string') ? opt: exports.cookieString(req, opt);
    //console.log('document.cookie = ' + str);
    document.cookie = str;
};

/**
 * Creates a Set-Cookie header on a response object.
 *
 * @name setResponseCookie(req, res, opt)
 * @param {Request Object} req
 * @param {Response Object} res
 * @param {Object} opt
 * @api public
 */

exports.setResponseCookie = function (req, res, opt) {
    var str = (typeof opt === 'string') ? opt: exports.cookieString(req, opt);
    if (typeof res !== 'object') {
        res = {code: 200, body: res};
    }
    if (!res.headers) {
        res.headers = {};
    }
    // TODO: is it possible to send multiple set-cookie headers by turning
    // headers into an array like in node?
    // XXX: just replacing all cookies for now - not ideal!
    res.headers['Set-Cookie'] = str;
};


})};

/********** settings/packages/duality **********/

kanso.moduleCache["settings/packages/duality"] = {load: (function (module, exports, require) {

module.exports = {"name":"duality","version":"0.0.15","categories":["frameworks","duality"],"description":"The central part of the Duality framework. Implements the CouchDB design doc API in the browser and handles the dispatching of URLs","modules":"duality","attachments":"duality.js","dependencies":{"settings":null,"modules":null,"properties":null,"attachments":null,"underscore":null,"url":null,"db":">=0.0.8","session":null,"cookies":null,"events":null},"postprocessors":{"queue":"build/queue","app":"build/app"},"minify":false,"open":false};

})};

/********** duality/events **********/

kanso.moduleCache["duality/events"] = {load: (function (module, exports, require) {

/**
 * EventEmitter for Duality core
 *
 * This has to be a separate module to duality/core because dualty/core will
 * directly require the app it's running. The app will often want to bind to
 * duality events when evaluated. This causes a circular dependency, causing
 * duality/core to be an empty object without the EventEmitter methods
 * available.
 */

var events = require('events');
var exports = module.exports = new events.EventEmitter();


})};

/********** duality/core **********/

kanso.moduleCache["duality/core"] = {load: (function (module, exports, require) {

/*global window: false, getRow: true, start: true, $: false, pageTracker: true,
  duality: true, log: true, console: true, send: true */

/**
 * The core module contains functions used by duality to facilitate the running
 * of your app. You shouldn't need to use any of the functions here directly
 * unless you're messing with the internals of the framework.
 *
 * @module
 */


/**
 * Module dependencies
 */

var settings = require('settings/root'), // module auto-generated
    url = require('url'),
    db = require('db'),
    utils = require('./utils'),
    session = require('session'),
    cookies = require('cookies'),
    events = require('./events'),
    urlParse = url.parse,
    urlFormat = url.format,
    _ = require('underscore')._,
    flashmessages,
    templates;


try {
    flashmessages = require('./flashmessages');
}
catch (e) {
    // may not be available
}

try {
    templates = require('duality/templates');
}
catch (e) {
    // may not be available
}


var BASE_TEMPLATE = 'base.html';
if (settings.duality && settings.duality.base_template) {
    BASE_TEMPLATE = settings.duality.base_template;
}

/**
 * This is because the first page hit also triggers duality to handle the url
 * client-side. Knowing it is the first page being loaded means we can stop
 * the pageTracker code from submitting the URL twice. Exported because this
 * might be useful information to other modules, it should not be modified
 * by them.
 */

//exports.initial_hit = utils.initial_hit;


/**
 * This variable keeps track of whether or not the browser supports
 * pushstate for manipulating browser history.
 */

exports.history_support = false;

/**
 * Stores the current html5 history state to detect duplicate popstate events
 */

exports.current_state = null;

/**
 * Set to true when setURL is called so the onpopstate which fires afterwards
 * knows it is the result of an explicit call to setURL (as opposed to clicking
 * the back, forward or reload buttons). This means we can avoid showing a
 * confirmation dialog for POST requests in these circumstances.
 */

exports.set_called = false;


/**
 * This is set to true when the initial page request is to an unknown URL
 * rewrite target (such as a static .html page). This tells duality whether to
 * handle urls internally or let the browser refresh the page when clicking
 * links etc
 */

exports.unknown_target = false;


if (typeof window !== 'undefined') {
    if (!window.console) {
        // console.log is going to cause errors, just stub the functions
        // for now. TODO: add logging utility for IE?
        window.console = {
            log: function () {},
            error: function () {},
            info: function () {},
            warn: function () {}
        };
    }
    var console = window.console;
}


/**
 * Global functions required to match the CouchDB JavaScript environment.
 */

if (typeof getRow === 'undefined' && typeof window !== 'undefined') {
    window.getRow = function () {
        return null;
    };
}
if (typeof start === 'undefined' && typeof window !== 'undefined') {
    window.start = function (options) {};
}
if (typeof send === 'undefined' && typeof window !== 'undefined') {
    window.send = function (options) {};
}
if (typeof log === 'undefined' && typeof window !== 'undefined') {
    window.log = function () {
        return console.log.apply(console, arguments);
    };
}


/**
 * Used to store userCtx, periodically updated like on session.login and
 * session.logout. TODO: Or if a permissions error is returned from a db method?
 */

// TODO: added to utils to avoid circular dependency bug in couchdb
//exports.userCtx = utils.userCtx;


exports._rewrites = [];
exports._shows = {};
exports._lists = {};
exports._updates = {};


function loadDeps(deps) {
    // TODO: ignore deps already merged in
    if (!deps) {
        return;
    }
    for (var k in deps) {
        var s = null;
        try {
            s = require('settings/packages/' + k);
        }
        catch (e) {
            // no settings, skip
        }
        if (s) {
            if (s.load) {
                var a = require(s.load);
                // should these always be concatenated?
                // TODO: this behaves differently to the build steps which only
                // use the rewrites from the root package
                exports._rewrites = exports._rewrites.concat(a.rewrites || []);

                // TODO: detect conflicting properties as the merge build step
                // would report them
                _.extend(exports._shows, a.shows);
                _.extend(exports._lists, a.lists);
                _.extend(exports._updates, a.updates);
            }
            loadDeps(s.dependencies);
        }
    }
}


// load root app
var tmp = {};
tmp[settings.name] = null;
loadDeps(tmp);

// load dependencies of root app
loadDeps(settings.dependencies);

exports._rewrites = _.flatten(exports._rewrites);


/**
 * Called by duality.js once the design doc has been loaded.
 */

exports.init = function () {

    if (window.history && history.pushState) {
        exports.history_support = true;

        $('form').live('submit', function (ev) {
            var action = $(this).attr('action');
            var method = $(this).attr('method').toUpperCase();

            // use current path if action path is mising
            action = urlParse(action);
            if (!action.pathname) {
                action.pathname = urlParse(exports.getURL()).pathname;
            }
            action = urlFormat(action);

            // _session is a special case always available at the root url
            if (action !== '/_session' && exports.isAppURL(action)) {
                var url = exports.appPath(action);
                ev.preventDefault();
                var fields = $(this).serializeArray();
                var data = {};
                for (var i = 0; i < fields.length; i++) {
                    data[fields[i].name] = fields[i].value;
                }
                if (method === 'GET' || method === 'HEAD') {
                    var parsed = urlParse(url);
                    parsed.query = data;
                    data = {};
                    url = urlFormat(parsed);
                }
                // TODO: should this post form data to a new window using
                // target="_blank" if the action is to an unrecognized rewrite
                // target?
                exports.setURL(method, url, data);
            }
            ev.preventDefault();
            return false;
        });

        $('a').live('click', function (ev) {
            var href = $(this).attr('href');
            var rel = $(this).attr('rel');

            if (href && exports.isAppURL(href) && rel !== 'external') {
                var url = exports.appPath(href);
                ev.preventDefault();
                var match = exports.matchURL('GET', url);
                if (/^_show\//.test(match.to) ||
                    /^_list\//.test(match.to) ||
                    /^_update\//.test(match.to)) {
                    exports.setURL('GET', url, {});
                }
                else {
                    // unknown rewrite target, don't create history entry
                    // but open in a new window since the new page probably
                    // doesn't have pushstate support and will break the back
                    // button
                    window.open(exports.getBaseURL() + url);
                }
            }
        });

        window.onpopstate = function (ev) {
            var url = exports.getAppURL();
            var state = ev.state || {};
            var method = state.method || 'GET';
            var data = state.data;
            var count = state.history_count;

            if (method !== 'GET' && method !== 'HEAD') {
                // unsafe method, unless caused by an explicit call to setURL
                // show a confirmation dialog
                if (!exports.set_called) {
                    // TODO: at this point is it too late to undo the popstate?
                    var resend = confirm(
                        'In order to complete this request the browser will ' +
                        'have to re-send information, repeating any ' +
                        'previous action (such as creating a document).\n\n' +
                        'Re-send information?'
                    );
                    if (!resend) {
                        var curr_count = exports.current_state.history_count;
                        window.history.go(curr_count - count);
                        return;
                    }
                }
            }
            // reset set_called
            exports.set_called = false;

            var curr = exports.current_state;
            if (curr &&
                curr.url === url &&
                curr.timestamp === state.timestamp &&
                (curr.method || 'GET') === (state.method || 'GET')) {
                // duplicate popstate event
                return;
            }
            exports.current_state = {
                method: method,
                url: url,
                data: data,
                timestamp: state.timestamp,
                history_count: count
            };
            exports.handle(method, url, data);
        };
        window.onpopstate({});
    }
    else {
        // This browser has no html5  history support, attempt to
        // enhance the page anyway
        // TODO: figure out the data from the initial request as this
        // re-rendering might wipe relevant data from the response
        // TODO: figure out the method from the initial request
        // because the initial request may have been a POST (pointing to
        // an update function instead of the show this GET might render)
        //
        // - perhaps use cookies to pass the method and data back to the client?
        //
        exports.handle('GET', exports.getAppURL(), {});
    }

    // TODO: should this be after userCtx is available??
    // call init on app too
    events.emit('init');
};


/**
 * Extracts groups from a url, eg:
 * '/some/path' with pattern '/some/:name' -> {name: 'path'}
 *
 * @name rewriteGroups(pattern, url)
 * @param {String} pattern
 * @param {String} url
 * @returns {Object}
 * @api public
 */

exports.rewriteGroups = function (pattern, url) {
    var pathname = urlParse(url).pathname;
    var re = new RegExp(
        '^' + pattern.replace(/:\w+/g, '([^/]+)').replace(/\*/g, '.*') + '$'
    );
    var m = re.exec(pathname);
    if (!m) {
        return [];
    }
    var values = m.slice(1);
    var keys = [];
    var matches = pattern.match(/:\w+/g) || [];
    for (var i = 0; i < matches.length; i++) {
        keys.push(matches[i].substr(1));
    }
    var groups = {};
    for (var j = 0; j < keys.length; j++) {
        groups[keys[j]] = values[j];
    }
    return groups;
};

/**
 * Extracts a splat value from a rewrite pattern and matching URL.
 *
 * @name rewriteSplat(pattern, url)
 * @param {String} pattern
 * @param {String} url
 * @returns {String}
 * @api public
 */

exports.rewriteSplat = function (pattern, url) {
    // splats are only supported at the end of a rewrite pattern
    if (pattern.charAt(pattern.length - 1) === '*') {
        var re = new RegExp(pattern.substr(0, pattern.length - 1) + '(.*)');
        var match = re.exec(url);
        if (match) {
            return match[1];
        }
    }
};


/**
 * Attempts to match rewrite from patterns to a URL, returning the
 * matching rewrite object if successful.
 *
 * @name matchURL(method, url)
 * @param {String} method
 * @param {String} url
 * @returns {Object}
 * @api public
 */

exports.matchURL = function (method, url) {
    var pathname = urlParse(url).pathname;
    var rewrites = exports._rewrites;
    for (var i = 0; i < rewrites.length; i++) {
        var r = rewrites[i];
        if (!r.method || method === r.method) {
            var from = r.from;
            from = from.replace(/\*$/, '(.*)');
            from = from.replace(/:\w+/g, '([^/]+)');
            var re = new RegExp('^' + from + '$');
            if (re.test(pathname)) {
                return r;
            }
        }
    }
};

/**
 * Replace group names in a string with the value of that group
 * eg: "/:name" with groups {name: 'test'} -> "/test"
 *
 * @name replaceGroups(val, groups, splat)
 * @param {String} val
 * @param {Object} groups
 * @param {String} splat
 * @returns {String}
 * @api public
 */

exports.replaceGroups = function (val, groups, splat) {
    var k, match, result = val;

    if (typeof val === 'string') {
        result = val.split('/');
        for (var i = 0; i < result.length; i++) {
            match = false;
            for (k in groups) {
                if (result[i] === ':' + k) {
                    result[i] = decodeURIComponent(groups[k]);
                    match = true;
                }
            }
            if (!match && result[i] === '*') {
                result[i] = splat;
            }
        }
        result = result.join('/');
    }
    else if (val.length) {
        result = val.slice();
        for (var j = 0; j < val.length; j++) {
            match = false;
            for (k in groups) {
                if (val[j] === ':' + k) {
                    result[j] = decodeURIComponent(groups[k]);
                    match = true;
                }
            }
            if (!match && val[j] === '*') {
                result[j] = splat;
            }
        }
    }
    return result;
};


/**
 * Creates a new request object from a url and matching rewrite object.
 * Query parameters are automatically populated from rewrite pattern.
 *
 * @name createRequest(method, url, data, match, callback)
 * @param {String} method
 * @param {String} url
 * @param {Object} data
 * @param {Object} match
 * @param {Function} callback
 * @api public
 */

exports.createRequest = function (method, url, data, match, callback) {
    var groups = exports.rewriteGroups(match.from, url);
    var query = urlParse(url, true).query || {};
    var k;
    if (match.query) {
        for (k in match.query) {
            if (match.query.hasOwnProperty(k)) {
                query[k] = exports.replaceGroups(match.query[k], groups);
            }
        }
    }
    if (groups) {
        for (k in groups) {
            if (groups.hasOwnProperty(k)) {
                query[k] = decodeURIComponent(groups[k]);
            }
        }
    }
    // splats are available for rewriting match.to, but not accessible on
    // the request object (couchdb 1.1.x), storing in a separate variable
    // for now
    var splat = exports.rewriteSplat(match.from, url);
    var to = exports.replaceGroups(match.to, query, splat);
    var req = {
        method: method,
        query: query,
        headers: {},
        path: to.split('/'),
        client: true,
        initial_hit: utils.initial_hit,
        cookie: cookies.readBrowserCookies()
    };
    if (data) {
        req.form = data;
    }

    db.newUUID(100, function (err, uuid) {
        if (err) {
            return callback(err);
        }
        req.uuid = uuid;

        if (utils.userCtx) {
            req.userCtx = utils.userCtx;
            return callback(null, req);
        }
        else {
            session.info(function (err, session) {
                if (err) {
                    return callback(err);
                }
                req.userCtx = session.userCtx;
                callback(null, req);
            });
        }
    });
};


/**
 * Handles return values from show / list / update functions
 */

exports.handleResponse = function (req, res) {
    if (req && typeof res === 'object') {
        if (res.headers) {
            if (res.headers['Set-Cookie']) {
                document.cookie = res.headers['Set-Cookie'];
            }
            var loc = res.headers['Location'];
            if (loc && _.indexOf([301, 302, 303, 307], res.code) !== -1) {
                if (exports.isAppURL(loc)) {
                    // reset method to GET unless response is a 307
                    var method = res.code === 307 ? req.method || 'GET': 'GET';
                    exports.setURL(method, exports.appPath(loc));
                }
                else {
                    document.location = loc;
                }
            }
        }
    }
};


/**
 * Fetches the relevant document and calls the named show function.
 *
 * @name runShowBrowser(req, name, docid, callback)
 * @param {Object} req
 * @param {String} name
 * @param {String} docid
 * @param {Function} callback
 * @api public
 */

exports.runShowBrowser = function (req, name, docid, callback) {
    var result;
    var fn = exports._shows[name];
    if (!fn) {
        throw new Error('Unknown show function: ' + name);
    }

    var info = {
        type: 'show',
        name: name,
        target: docid,
        query: req.query,
        fn: fn
    };
    events.emit('beforeResource', info);

    if (docid) {
        var appdb = db.use(exports.getDBURL(req));
        appdb.getDoc(docid, req.query, function (err, doc) {
            var current_req = (utils.currentRequest() || {});
            if (current_req.uuid === req.uuid) {
                if (err) {
                    return callback(err);
                }
                var res = exports.runShow(fn, doc, req);
                events.emit('afterResponse', info, req, res);
                if (res) {
                    exports.handleResponse(req, res);
                }
                else {
                    // returned without response, meaning cookies won't be set
                    // by handleResponseHeaders
                    if (flashmessages && req.outgoing_flash_messages) {
                        flashmessages.setCookieBrowser(
                            req, req.outgoing_flash_messages
                        );
                    }
                }
                callback();
            }
        });
    }
    else {
        var res = exports.runShow(fn, null, req);
        events.emit('afterResponse', info, req, res);
        if (res) {
            exports.handleResponse(req, res);
        }
        else {
            // returned without response, meaning cookies won't be set by
            // handleResponseHeaders
            if (flashmessages && req.outgoing_flash_messages) {
                flashmessages.setCookieBrowser(
                    req, req.outgoing_flash_messages
                );
            }
        }
        callback();
    }
};

/**
 * Helper for runShow/runList.
 *
 * @name parseResponse(req, res)
 * @param {Object} req
 * @param {Object} res
 * @api public
 */

exports.parseResponse = function (req, res) {
    var ids = _.without(_.keys(res), 'title', 'code', 'headers', 'body');
    if (req.client) {
        if (res.title) {
            document.title = res.title;
        }
        _.each(ids, function (id) {
            $('#' + id).html(res[id]);
        });
    }
    else if (!res.body) {
        var context = {title: res.title || ''};
        _.each(ids, function (id) {
            context[id] = res[id];
        });
        if (!templates) {
            throw new Error(
                'Short-hand response style requires template module'
            );
        }
        var body = templates.render(BASE_TEMPLATE, req, context);
        res = {
            body: body,
            code: res.code || 200,
            headers: res.headers
        };
    }
    return {
        body: res.body,
        code: res.code,
        headers: res.headers
    };
};

/**
 * Runs a show function with the given document and request object,
 * emitting relevant events. This function runs both server and client-side.
 *
 * @name runShow(fn, doc, req)
 * @param {Function} fn
 * @param {Object} doc
 * @param {Object} req
 * @api public
 */

exports.parseResponse = function (req, res) {
    var ids = _.without(_.keys(res), 'title', 'code', 'headers', 'body');
    if (req.client) {
        if (res.title) {
            document.title = res.title;
        }
        _.each(ids, function (id) {
            $('#' + id).html(res[id]);
        });
    }
    else if (!res.body) {
        var context = {title: res.title || ''};
        _.each(ids, function (id) {
            context[id] = res[id];
        });
        if (!templates) {
            throw new Error(
                'Short-hand response style requires templates module'
            );
        }
        var body = templates.render(BASE_TEMPLATE, req, context);
        res = {
            body: body,
            code: res.code || 200,
            headers: res.headers
        };
    }
    return {
        body: res.body,
        code: res.code,
        headers: res.headers
    };
};

exports.runShow = function (fn, doc, req) {
    if (flashmessages) {
        req = flashmessages.updateRequest(req);
    }
    utils.currentRequest(req);
    var info = {
        type: 'show',
        name: req.path[1],
        target: req.path[2],
        query: req.query,
        fn: fn
    };
    events.emit('beforeRequest', info, req);
    var res = fn(doc, req);

    if (!(res instanceof Object)) {
        res = {code: 200, body: res};
    }
    else {
        res = exports.parseResponse(req, res);
    }
    events.emit('beforeResponseStart', info, req, res);
    events.emit('beforeResponseData', info, req, res, res.body || '');

    if (flashmessages) {
        res = flashmessages.updateResponse(req, res);
    }
    req.response_received = true;
    return res;
};

/**
 * Fetches the relevant document and calls the named update function.
 *
 * @name runUpdateBrowser(req, name, docid, callback)
 * @param {Object} req
 * @param {String} name
 * @param {String} docid
 * @param {Function} callback
 * @api public
 */

exports.runUpdateBrowser = function (req, name, docid, callback) {
    var result;
    var fn = exports._updates[name];
    if (!fn) {
        throw new Error('Unknown update function: ' + name);
    }

    var info = {
        type: 'update',
        name: name,
        target: docid,
        query: req.query,
        fn: fn
    };
    events.emit('beforeResource', info);

    if (docid) {
        var appdb = db.use(exports.getDBURL(req));
        appdb.getDoc(docid, req.query, function (err, doc) {
            var current_req = (utils.currentRequest() || {});
            if (current_req.uuid === req.uuid) {
                if (err) {
                    return callback(err);
                }
                exports.runUpdate(fn, doc, req, function (err, res) {
                    if (err) {
                        events.emit('updateFailure', err, info, req, res, doc);
                        return callback(err);
                    }
                    events.emit('afterResponse', info, req, res);
                    if (res) {
                        exports.handleResponse(req, res[1]);
                    }
                    else {
                        // returned without response, meaning cookies won't be
                        // set by handleResponseHeaders
                        if (flashmessages && req.outgoing_flash_messages) {
                            flashmessages.setCookieBrowser(
                                req, req.outgoing_flash_messages
                            );
                        }
                    }
                    callback();
                });
            }
        });
    }
    else {
        exports.runUpdate(fn, null, req, function (err, res) {
            if (err) {
                events.emit('updateFailure', err, info, req, res, null);
                return callback(err);
            }
            events.emit('afterResponse', info, req, res);
            if (res) {
                exports.handleResponse(req, res[1]);
            }
            else {
                // returned without response, meaning cookies won't be set by
                // handleResponseHeaders
                if (flashmessages && req.outgoing_flash_messages) {
                    flashmessages.setCookieBrowser(
                        req, req.outgoing_flash_messages
                    );
                }
            }
            callback();
        });
    }
};

/**
 * Runs a update function with the given document and request object,
 * emitting relevant events. This function runs both server and client-side.
 *
 * @name runUpdate(fn, doc, req)
 * @param {Function} fn
 * @param {Object} doc
 * @param {Object} req
 * @api public
 */

exports.runUpdate = function (fn, doc, req, cb) {
    if (flashmessages) {
        req = flashmessages.updateRequest(req);
    }
    utils.currentRequest(req);
    var info = {
        type: 'update',
        name: req.path[1],
        target: req.path[2],
        query: req.query,
        fn: fn
    };
    events.emit('beforeRequest', info, req);
    var val = fn(doc, req);

    var res = val ? val[1]: null;
    if (!(res instanceof Object)) {
        res = {code: 200, body: res};
    }
    else {
        res = exports.parseResponse(req, res);
    }
    events.emit('beforeResponseStart', info, req, res);
    events.emit('beforeResponseData', info, req, res, res.body || '');

    if (flashmessages) {
        res = flashmessages.updateResponse(req, res);
    }
    var r = [val ? val[0]: null, res];
    if (req.client && r[0]) {
        var appdb = db.use(exports.getDBURL(req));
        appdb.saveDoc(r[0], function (err, res) {
            if (err) {
                return cb(err);
            }
            req.response_received = true;
            cb(null, r);
        });
    }
    else {
        req.response_received = true;
        cb(null, r);
    }
};


/**
 * Creates a fake head object from view results for passing to a list function
 * being run client-side.
 *
 * @name createHead(data)
 * @param {Object} data
 * @returns {Object}
 * @api public
 */

exports.createHead = function (data) {
    var head = {};
    for (var k in data) {
        if (k !== 'rows') {
            head[k] = data[k];
        }
    }
    return head;
};


/**
 * Fetches the relevant view and calls the named list function with the results.
 *
 * @name runListBrowser(req, name, view, callback)
 * @param {Object} req
 * @param {String} name
 * @param {String} view
 * @param {Function} callback
 * @api public
 */

exports.runListBrowser = function (req, name, view, callback) {
    var fn = exports._lists[name];
    if (!fn) {
        throw new Error('Unknown list function: ' + name);
    }

    var info = {
        type: 'list',
        name: name,
        target: view,
        query: req.query,
        fn: fn
    };
    events.emit('beforeResource', info);

    if (view) {
        // update_seq used in head parameter passed to list function
        req.query.update_seq = true;
        var appdb = db.use(exports.getDBURL(req));
        appdb.getView(settings.name, view, req.query, function (err, data) {
            var current_req = (utils.currentRequest() || {});
            if (current_req.uuid === req.uuid) {
                if (err) {
                    return callback(err);
                }
                getRow = function () {
                    return data.rows.shift();
                };
                start = function (res) {
                    exports.handleResponse(req, res);
                };
                var head = exports.createHead(data);
                var res = exports.runList(fn, head, req);
                events.emit('afterResponse', info, req, res);
                if (res) {
                    exports.handleResponse(req, res);
                }
                else {
                    // returned without response, meaning cookies won't be set
                    // by handleResponseHeaders
                    if (flashmessages && req.outgoing_flash_messages) {
                        flashmessages.setCookieBrowser(
                            req, req.outgoing_flash_messages
                        );
                    }
                }
                getRow = function () {
                    return null;
                };
                callback();
            }
        });
    }
    // TODO: check if it should throw here
    else {
        var e = new Error('no view specified');
        if (callback) {
            callback(e);
        }
        else {
            throw e;
        }
    }
};

/**
 * Runs a list function with the given document and request object,
 * emitting relevant events. This function runs both server and client-side.
 *
 * @name runList(fn, head, req)
 * @param {Function} fn
 * @param {Object} head
 * @param {Object} req
 * @api public
 */

exports.runList = function (fn, head, req) {
    if (flashmessages) {
        req = flashmessages.updateRequest(req);
    }
    utils.currentRequest(req);
    var info = {
        type: 'list',
        name: req.path[1],
        target: req.path[2],
        query: req.query,
        fn: fn
    };
    // cache response from start call
    var start_res;
    var _start = start;
    start = function (res) {
        start_res = res;
        events.emit('beforeResponseStart', info, req, res);
        if (res.body) {
            events.emit('beforeResponseData', info, req, res, res.body);
        }
        if (flashmessages) {
            res = flashmessages.updateResponse(req, res);
        }
        _start(res);
    };
    var _send = send;
    send = function (data) {
        if (!start_res.body) {
            start_res.body = '';
        }
        // TODO: does it make sense to store data here and use up memory
        // on the server?
        start_res.body += data;
        events.emit('beforeResponseData', info, req, start_res, data);
        _send(data);
    };
    events.emit('beforeRequest', info, req);
    var val = fn(head, req);

    if (val instanceof Object) {
        val = exports.parseResponse(req, val).body;
    }
    if (!start_res) {
        start_res = {code: 200, body: val};
        events.emit('beforeResponseStart', info, req, start_res);
        events.emit('beforeResponseData', info, req, start_res, val);
        start = _start;
        send = _send;
    }
    else {
        start_res.body = start_res.body ? start_res.body + val: val;
        events.emit('beforeResponseData', info, req, start_res, val);
    }
    start = _start;
    send = _send;
    req.response_received = true;
    return val;
};


/**
 * Creates a request object for the url and runs appropriate show, list or
 * update functions.
 *
 * @name handle(method, url, data)
 * @param {String} method
 * @param {String} url
 * @param {Object} data
 * @api public
 */

exports.handle = function (method, url, data) {
    if (exports.unknown_target) {
        // if we're currently on an unknown rewrite target page (such as a
        // static .html file), don't attempt to intercept the request
        window.location = exports.getBaseURL() + url;
        return;
    }
    var match = exports.matchURL(method, url);
    if (match) {
        var parsed = urlParse(url);
        exports.createRequest(method, url, data, match, function (err, req) {
            if (err) {
                throw err;
            }
            var msg = method + ' ' + url + ' -> ' +
                JSON.stringify(req.path.join('/')) + ' ' +
                JSON.stringify(req.query);

            if (data) {
                msg += ' data: ' + JSON.stringify(data);
            }

            console.log(msg);
            utils.currentRequest(req);

            var after = function () {
                if (parsed.hash) {
                    // we have to handle in-page anchors manually because we've
                    // hijacked the hash part of the url
                    // TODO: don't re-handle the page if only the hash has
                    // changed

                    // test if a valid element name or id
                    if (/#[A-Za-z_\-:\.]+/.test(parsed.hash)) {
                        var el = $(parsed.hash);
                        if (el.length) {
                            window.scrollTo(0, el.offset().top);
                        }
                    }
                    else if (parsed.hash === '#') {
                        // scroll to top of page
                        window.scrollTo(0, 0);
                    }
                    // TODO: handle invalid values?
                }
            };

            var src, fn, name;

            if (req.path[0] === '_show') {
                exports.runShowBrowser(
                    req, req.path[1], req.path.slice(2).join('/'), after
                );
                if (!utils.initial_hit) {
                    window.scrollTo(0, 0);
                }
            }
            else if (req.path[0] === '_list') {
                exports.runListBrowser(
                    req, req.path[1], req.path.slice(2).join('/'), after
                );
                if (!utils.initial_hit) {
                    window.scrollTo(0, 0);
                }
            }
            else if (req.path[0] === '_update') {
                exports.runUpdateBrowser(
                    req, req.path[1], req.path.slice(2).join('/'), after
                );
                if (!utils.initial_hit) {
                    window.scrollTo(0, 0);
                }
            }
            else {
                console.log('Unknown rewrite target: ' + req.path.join('/'));
                if (!utils.initial_hit) {
                    var newurl = exports.getBaseURL() + url;
                    console.log('Opening new window for: ' + newurl);
                    // reset url
                    window.history.go(-1);
                    // open in new window, since this page is unlikely to have
                    // pushstate support and would break the back button
                    window.open(newurl);
                }
                else {
                    exports.unknown_target = true;
                    console.log(
                        'Initial hit is an uknown rewrite target, duality ' +
                        'will not fetch from server in order to avoid ' +
                        'redirect loop'
                    );
                }
            }
            utils.initial_hit = false;
        });
    }
    else {
        console.log(method + ' ' + url + ' -> [404]');
        window.location = exports.getBaseURL() + url;
        return;
    }

    /**
     * if google analytics is included on the page, and this url
     * has not been tracked (not the initial hit) then manually
     * track a page view. This is done consistently for hash-based
     * and pushState urls
     */
    if (window.pageTracker && !utils.initial_hit) {
        pageTracker._trackPageview(url);
    }
};


/**
 * Add a history entry for the given url, prefixed with the baseURL for the app.
 *
 * @name setURL(method, url, data)
 * @param {String} method
 * @param {String} url
 * @param {Object} data (optional)
 * @api public
 */

exports.setURL = function (method, url, data) {
    var fullurl = exports.getBaseURL() + url;
    var state = {
        method: method,
        data: data,
        timestamp: new Date().getTime(),
        history_count: window.history.length + 1
    };
    // this is the result of a direct call to setURL
    // (don't show confirmation dialog for unsafe states needing to re-submit)
    exports.set_called = true;

    /**
     * if the current request is unsafe, we replace it so clicking the back
     * button 'skips' it without showing a dialog.
     *
     * This means GET1, POST2, GET3 would result in a history of GET1, GET3.
     * Clicking back after GET3 wouldn't re-submit a form.
     *
     * Doing GET1, POST2, then clicking back and forward again would result
     * in a confirmation dialog asking if you want to re-submit.
     */

    var curr_state = exports.current_state;
    var curr_method = curr_state ? (curr_state.method || 'GET'): 'GET';

    if (curr_method !== 'GET' && curr_method !== 'HEAD') {
        // unsafe method on current request, replace it
        window.history.replaceState(state, document.title, fullurl);
    }
    else {
        // last request was safe, add a new entry in the history
        window.history.pushState(state, document.title, fullurl);
    }
    // manually fire popstate event
    window.onpopstate({state: state});
};


/**
 * This was moved to utils to avoid a circular dependency between
 * core.js and db.js... however, it should be accessed via the core.js module
 * as it may get moved back once circular dependencies are fixed in couchdb's
 * commonjs implementation.
 */

exports.getBaseURL = utils.getBaseURL;

exports.getDBURL = function (req) {
    return exports.getBaseURL(req) + '/_db';
};


/**
 * Gets the current app-level URL (without baseURL prefix).
 *
 * @name getAppURL()
 * @returns {String}
 * @api public
 */

exports.getAppURL = function () {
    return exports.appPath(exports.getURL());
};

/**
 * Gets the window location coerced to a string (for IE)
 *
 * @name getURL()
 * @returns {String}
 * @api public
 */

exports.getURL = function () {
    return '' + window.location;
};


/**
 * Tests if two urls are of the same origin. Accepts parsed url objects
 * or strings as arguments.
 *
 * @name sameOrigin(a, b)
 * @param a
 * @param b
 * @returns Boolean
 * @api public
 */

exports.sameOrigin = function (a, b) {
    var ap = (typeof a === 'string') ? urlParse(a): a;
    var bp = (typeof b === 'string') ? urlParse(b): b;
    // if one url is relative to current origin, return true
    if (ap.protocol === undefined || bp.protocol === undefined) {
        return true;
    }
    return (
        ap.protocol === bp.protocol &&
        ap.hostname === bp.hostname &&
        ap.port === bp.port
    );
};

/**
 * Converts a full url to an app-level url (without baseURL prefix).
 * example: {baseURL}/some/path -> /some/path
 *
 * @name appPath(p)
 * @param {String} p
 * @returns {String}
 * @api public
 */

exports.appPath = function (p) {
    // hash links need current URL prepending
    if (p.charAt(0) === '#') {
        var newurl = urlParse(exports.getURL());
        newurl.hash = p;
        return exports.appPath(urlFormat(newurl));
    }
    else if (p.charAt(0) === '?') {
        // if the request is just a query, then prepend the current app path
        // as a browser would
        var newurl2 = urlParse(exports.getURL());
        delete newurl2.query;
        delete newurl2.search;
        delete newurl2.href;
        newurl2.search = p;
        return exports.appPath(urlFormat(newurl2));
    }
    else if (/\w+:/.test(p)) {
        // include protocol
        var origin = p.split('/').slice(0, 3).join('/');
        // coerce window.location to a real string so we can use split in IE
        var loc = '' + window.location;
        if (origin === loc.split('/').slice(0, 3).join('/')) {
            // remove origin, set p to pathname only
            // IE often adds this to a tags, hence why we strip it out now
            p = p.substr(origin.length);
        }
        else {
            // not same origin, return original full path
            return p || '/';
        }
    }
    var base = exports.getBaseURL();
    if (p.substr(0, base.length) === base) {
        return p.substr(base.length) || '/';
    }
    return p || '/';
};


/**
 * Used to decide whether to handle a link or not. Should detect app vs.
 * external urls.
 *
 * @name isAppURL(url)
 * @param {String} url
 * @returns {Boolean}
 * @api public
 */

exports.isAppURL = function (url) {
    // coerce window.location to a real string in IE
    var loc = '' + window.location;
    var base = exports.getBaseURL();
    if (!exports.sameOrigin(url, loc)) {
        return false;
    }
    var p = urlParse(url).pathname;
    if (!p) {
        return false;
    }
    if (p.length < base.length) {
        return false;
    }
    if (p.length === base.length) {
        return p === base;
    }
    return p.substr(0, base.length + 1) === base + '/';
};


// make sure currentRequest() always provided the latest session information
events.on('sessionChange', function (userCtx, req) {
    var curr_req = utils.currentRequest();
    if (curr_req) {
        curr_req.userCtx = userCtx;
        utils.currentRequest(curr_req);
    }
});


})};

/********** duality/utils **********/

kanso.moduleCache["duality/utils"] = {load: (function (module, exports, require) {

/*global window: false, __duality_current_request: true*/


/**
 * General utility functions used by duality. Some functions were moved here
 * from other modules (such as core), to avoid a circular dependency bug in
 * CouchDB.
 *
 * This module also stores some useful properties such as 'isBrowser', which is
 * true if the code is running in a browser environment, and 'initial_hit' which
 * is set to true when a page is first requested from CouchDB (and set to false
 * for subsequent requests).
 *
 * @module
 */

/**
 * Module dependencies
 */

var settings = require('settings/root'), // settings module is auto-generated
    _ = require('underscore')._;


/**
 * Some functions calculate results differently depending on the execution
 * environment. The isBrowser value is used to set the correct environment
 * for these functions, and is only exported to make unit testing easier.
 */

exports.isBrowser = function () {
    return (typeof(window) !== 'undefined');
};

/**
 * Keeps track of the last *triggered* request. This is to avoid a race
 * condition where two link clicks in quick succession can cause the rendered
 * page to not match the current URL. If the first link's document or view takes
 * longer to return than the second, the URL was updated for the second link
 * click but the page for the first link will render last, overwriting the
 * correct page. Now, callbacks for fetching documents and views check against
 * this value to see if they should continue rendering the result or not.
 */

exports.currentRequest = function (v) {
    if (v) {
        __duality_current_request = v;
    } else if (typeof(__duality_current_request) === 'undefined') {
        __duality_current_request = null;
    }
    return __duality_current_request;
};


/**
 * This is because the first page hit also triggers duality to handle the url
 * client-side. Knowing it is the first page being loaded means we can stop
 * the pageTracker code from submitting the URL twice. Exported because this
 * might be useful information to other modules, it should not be modified
 * by them.
 */

// TODO: this was moved to this module from core.js to avoid a circular
// dependency between core.js and session.js

exports.initial_hit = true;

/**
 * Used to store userCtx, periodically updated like on session.login and
 * session.logout.
 */

// TODO: added to utils to avoid circular dependency bug in couchdb

exports.userCtx = null;

/**
 * Caches extended session info (like the current authentication db) after
 * a call to session.info
 */
exports.session = null;

/**
 * This is used to make unit testing in the browser easier.
 * Because it can be overridden without actually changing the window's location.
 * (and navigating away from the test suite)
 */

exports.getWindowLocation = function () {
    return window.location;
};

/**
 * Returns the path to prefix to any URLs. When running behind a
 * virtual host, there is nothing to prefix URLs with. When accessing the
 * app directly, URLs need to be prefixed with /db/_design/appname/_rewrite.
 *
 * The request object argument is only required when run server-side, but its
 * a good idea to include it whenever you call getBaseURL.
 *
 * @name getBaseURL(req)
 * @param {Object} req
 * @returns {String}
 * @api public
 */

// TODO: this was moved to this module from core.js to avoid a circular
// dependency between core.js and db.js ...once circular dependencies in
// couchdb's commonjs implementation are fixed it can be moved back into
// core.js. For now, this is also exported from core.js and should
// be accessed from there.

exports.getBaseURL = function (/*optional*/req) {
    if (!req) {
        req = exports.currentRequest();
    }
    if ('baseURL' in settings) {
        return settings.baseURL;
    }
    if (exports.isBrowser()) {
        var re = new RegExp('(.*\\/_rewrite).*$');
        var match = re.exec(exports.getWindowLocation().pathname);
        if (match) {
            return match[1];
        }
        return '';
    }
    if (req.headers['x-couchdb-vhost-path']) {
        return '';
    }
    return '/' + req.path.slice(0, 3).join('/') + '/_rewrite';
};

/**
 * Creates CouchDB response object for returning from a show, list or update
 * function, which redirects to the given app url (automatically prepending the
 * baseURL)
 *
 * @name redirect(req, url)
 * @param {Object} req
 * @param {String} url
 * @returns {Object}
 * @api public
 */

exports.redirect = function (/*optional*/req, url) {
    url = url || '/';
    var baseURL = exports.getBaseURL(req);
    return {code: 302, headers: {'Location': baseURL + url}};
};


})};

/********** handlebars **********/

kanso.moduleCache["handlebars"] = {load: (function (module, exports, require) {

// lib/handlebars/base.js
var Handlebars = {};

Handlebars.VERSION = "1.0.beta.2";

Handlebars.helpers  = {};
Handlebars.partials = {};

Handlebars.registerHelper = function(name, fn, inverse) {
  if(inverse) { fn.not = inverse; }
  this.helpers[name] = fn;
};

Handlebars.registerPartial = function(name, str) {
  this.partials[name] = str;
};

Handlebars.registerHelper('helperMissing', function(arg) {
  if(arguments.length === 2) {
    return undefined;
  } else {
    throw new Error("Could not find property '" + arg + "'");
  }
});

Handlebars.registerHelper('blockHelperMissing', function(context, options) {
  var inverse = options.inverse || function() {}, fn = options.fn;


  var ret = "";
  var type = Object.prototype.toString.call(context);

  if(type === "[object Function]") {
    context = context();
  }

  if(context === true) {
    return fn(this);
  } else if(context === false || context == null) {
    return inverse(this);
  } else if(type === "[object Array]") {
    if(context.length > 0) {
      for(var i=0, j=context.length; i<j; i++) {
        ret = ret + fn(context[i]);
      }
    } else {
      ret = inverse(this);
    }
    return ret;
  } else {
    return fn(context);
  }
});

Handlebars.registerHelper('each', function(context, options) {
  var fn = options.fn, inverse = options.inverse;
  var ret = "";

  if(context && context.length > 0) {
    for(var i=0, j=context.length; i<j; i++) {
      ret = ret + fn(context[i]);
    }
  } else {
    ret = inverse(this);
  }
  return ret;
});

Handlebars.registerHelper('if', function(context, options) {
  if(!context || Handlebars.Utils.isEmpty(context)) {
    return options.inverse(this);
  } else {
    return options.fn(this);
  }
});

Handlebars.registerHelper('unless', function(context, options) {
  var fn = options.fn, inverse = options.inverse;
  options.fn = inverse;
  options.inverse = fn;

  return Handlebars.helpers['if'].call(this, context, options);
});

Handlebars.registerHelper('with', function(context, options) {
  return options.fn(context);
});
;
// lib/handlebars/compiler/parser.js
/* Jison generated parser */
var handlebars = (function(){

var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"root":3,"program":4,"EOF":5,"statements":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"OPEN_PARTIAL":24,"params":25,"hash":26,"param":27,"STRING":28,"INTEGER":29,"BOOLEAN":30,"hashSegments":31,"hashSegment":32,"ID":33,"EQUALS":34,"pathSegments":35,"SEP":36,"$accept":0,"$end":1},
terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"OPEN_PARTIAL",28:"STRING",29:"INTEGER",30:"BOOLEAN",33:"ID",34:"EQUALS",36:"SEP"},
productions_: [0,[3,2],[4,3],[4,1],[4,0],[6,1],[6,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[7,2],[17,3],[17,2],[17,2],[17,1],[25,2],[25,1],[27,1],[27,1],[27,1],[27,1],[26,1],[31,2],[31,1],[32,3],[32,3],[32,3],[32,3],[21,1],[35,3],[35,1]],
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {

var $0 = $$.length - 1;
switch (yystate) {
case 1: return $$[$0-1] 
break;
case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]) 
break;
case 3: this.$ = new yy.ProgramNode($$[$0]) 
break;
case 4: this.$ = new yy.ProgramNode([]) 
break;
case 5: this.$ = [$$[$0]] 
break;
case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1] 
break;
case 7: this.$ = new yy.InverseNode($$[$0-2], $$[$0-1], $$[$0]) 
break;
case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0]) 
break;
case 9: this.$ = $$[$0] 
break;
case 10: this.$ = $$[$0] 
break;
case 11: this.$ = new yy.ContentNode($$[$0]) 
break;
case 12: this.$ = new yy.CommentNode($$[$0]) 
break;
case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 
break;
case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 
break;
case 15: this.$ = $$[$0-1] 
break;
case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 
break;
case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true) 
break;
case 18: this.$ = new yy.PartialNode($$[$0-1]) 
break;
case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]) 
break;
case 20: 
break;
case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]] 
break;
case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null] 
break;
case 23: this.$ = [[$$[$0-1]], $$[$0]] 
break;
case 24: this.$ = [[$$[$0]], null] 
break;
case 25: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; 
break;
case 26: this.$ = [$$[$0]] 
break;
case 27: this.$ = $$[$0] 
break;
case 28: this.$ = new yy.StringNode($$[$0]) 
break;
case 29: this.$ = new yy.IntegerNode($$[$0]) 
break;
case 30: this.$ = new yy.BooleanNode($$[$0]) 
break;
case 31: this.$ = new yy.HashNode($$[$0]) 
break;
case 32: $$[$0-1].push($$[$0]); this.$ = $$[$0-1] 
break;
case 33: this.$ = [$$[$0]] 
break;
case 34: this.$ = [$$[$0-2], $$[$0]] 
break;
case 35: this.$ = [$$[$0-2], new yy.StringNode($$[$0])] 
break;
case 36: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])] 
break;
case 37: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])] 
break;
case 38: this.$ = new yy.IdNode($$[$0]) 
break;
case 39: $$[$0-2].push($$[$0]); this.$ = $$[$0-2]; 
break;
case 40: this.$ = [$$[$0]] 
break;
}
},
table: [{3:1,4:2,5:[2,4],6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],24:[1,15]},{1:[3]},{5:[1,16]},{5:[2,3],7:17,8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,19],20:[2,3],22:[1,13],23:[1,14],24:[1,15]},{5:[2,5],14:[2,5],15:[2,5],16:[2,5],19:[2,5],20:[2,5],22:[2,5],23:[2,5],24:[2,5]},{4:20,6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],24:[1,15]},{4:21,6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],24:[1,15]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],24:[2,9]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],24:[2,10]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],24:[2,11]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],24:[2,12]},{17:22,21:23,33:[1,25],35:24},{17:26,21:23,33:[1,25],35:24},{17:27,21:23,33:[1,25],35:24},{17:28,21:23,33:[1,25],35:24},{21:29,33:[1,25],35:24},{1:[2,1]},{6:30,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],24:[1,15]},{5:[2,6],14:[2,6],15:[2,6],16:[2,6],19:[2,6],20:[2,6],22:[2,6],23:[2,6],24:[2,6]},{17:22,18:[1,31],21:23,33:[1,25],35:24},{10:32,20:[1,33]},{10:34,20:[1,33]},{18:[1,35]},{18:[2,24],21:40,25:36,26:37,27:38,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,38],28:[2,38],29:[2,38],30:[2,38],33:[2,38],36:[1,46]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],36:[2,40]},{18:[1,47]},{18:[1,48]},{18:[1,49]},{18:[1,50],21:51,33:[1,25],35:24},{5:[2,2],8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,2],22:[1,13],23:[1,14],24:[1,15]},{14:[2,20],15:[2,20],16:[2,20],19:[2,20],22:[2,20],23:[2,20],24:[2,20]},{5:[2,7],14:[2,7],15:[2,7],16:[2,7],19:[2,7],20:[2,7],22:[2,7],23:[2,7],24:[2,7]},{21:52,33:[1,25],35:24},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],24:[2,8]},{14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],24:[2,14]},{18:[2,22],21:40,26:53,27:54,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,23]},{18:[2,26],28:[2,26],29:[2,26],30:[2,26],33:[2,26]},{18:[2,31],32:55,33:[1,56]},{18:[2,27],28:[2,27],29:[2,27],30:[2,27],33:[2,27]},{18:[2,28],28:[2,28],29:[2,28],30:[2,28],33:[2,28]},{18:[2,29],28:[2,29],29:[2,29],30:[2,29],33:[2,29]},{18:[2,30],28:[2,30],29:[2,30],30:[2,30],33:[2,30]},{18:[2,33],33:[2,33]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],34:[1,57],36:[2,40]},{33:[1,58]},{14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],24:[2,13]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],24:[2,16]},{5:[2,17],14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],24:[2,17]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],24:[2,18]},{18:[1,59]},{18:[1,60]},{18:[2,21]},{18:[2,25],28:[2,25],29:[2,25],30:[2,25],33:[2,25]},{18:[2,32],33:[2,32]},{34:[1,57]},{21:61,28:[1,62],29:[1,63],30:[1,64],33:[1,25],35:24},{18:[2,39],28:[2,39],29:[2,39],30:[2,39],33:[2,39],36:[2,39]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],24:[2,19]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],24:[2,15]},{18:[2,34],33:[2,34]},{18:[2,35],33:[2,35]},{18:[2,36],33:[2,36]},{18:[2,37],33:[2,37]}],
defaultActions: {16:[2,1],37:[2,23],53:[2,21]},
parseError: function parseError(str, hash) {
    throw new Error(str);
},
parse: function parse(input) {
    var self = this,
        stack = [0],
        vstack = [null], // semantic value stack
        lstack = [], // location stack
        table = this.table,
        yytext = '',
        yylineno = 0,
        yyleng = 0,
        recovering = 0,
        TERROR = 2,
        EOF = 1;

    //this.reductionCount = this.shiftCount = 0;

    this.lexer.setInput(input);
    this.lexer.yy = this.yy;
    this.yy.lexer = this.lexer;
    if (typeof this.lexer.yylloc == 'undefined')
        this.lexer.yylloc = {};
    var yyloc = this.lexer.yylloc;
    lstack.push(yyloc);

    if (typeof this.yy.parseError === 'function')
        this.parseError = this.yy.parseError;

    function popStack (n) {
        stack.length = stack.length - 2*n;
        vstack.length = vstack.length - n;
        lstack.length = lstack.length - n;
    }

    function lex() {
        var token;
        token = self.lexer.lex() || 1; // $end = 1
        // if token isn't its numeric value, convert
        if (typeof token !== 'number') {
            token = self.symbols_[token] || token;
        }
        return token;
    };

    var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
    while (true) {
        // retreive state number from top of stack
        state = stack[stack.length-1];

        // use default actions if available
        if (this.defaultActions[state]) {
            action = this.defaultActions[state];
        } else {
            if (symbol == null)
                symbol = lex();
            // read action for current state and first input
            action = table[state] && table[state][symbol];
        }

        // handle parse error
        if (typeof action === 'undefined' || !action.length || !action[0]) {

            if (!recovering) {
                // Report error
                expected = [];
                for (p in table[state]) if (this.terminals_[p] && p > 2) {
                    expected.push("'"+this.terminals_[p]+"'");
                }
                var errStr = '';
                if (this.lexer.showPosition) {
                    errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+'\nExpecting '+expected.join(', ');
                } else {
                    errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
                                  (symbol == 1 /*EOF*/ ? "end of input" :
                                              ("'"+(this.terminals_[symbol] || symbol)+"'"));
                }
                this.parseError(errStr,
                    {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
            }

            // just recovered from another error
            if (recovering == 3) {
                if (symbol == EOF) {
                    throw new Error(errStr || 'Parsing halted.');
                }

                // discard current lookahead and grab another
                yyleng = this.lexer.yyleng;
                yytext = this.lexer.yytext;
                yylineno = this.lexer.yylineno;
                yyloc = this.lexer.yylloc;
                symbol = lex();
            }

            // try to recover from error
            while (1) {
                // check for error recovery rule in this state
                if ((TERROR.toString()) in table[state]) {
                    break;
                }
                if (state == 0) {
                    throw new Error(errStr || 'Parsing halted.');
                }
                popStack(1);
                state = stack[stack.length-1];
            }

            preErrorSymbol = symbol; // save the lookahead token
            symbol = TERROR;         // insert generic error symbol as new lookahead
            state = stack[stack.length-1];
            action = table[state] && table[state][TERROR];
            recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
        }

        // this shouldn't happen, unless resolve defaults are off
        if (action[0] instanceof Array && action.length > 1) {
            throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
        }

        switch (action[0]) {

            case 1: // shift
                //this.shiftCount++;

                stack.push(symbol);
                vstack.push(this.lexer.yytext);
                lstack.push(this.lexer.yylloc);
                stack.push(action[1]); // push state
                symbol = null;
                if (!preErrorSymbol) { // normal execution/no error
                    yyleng = this.lexer.yyleng;
                    yytext = this.lexer.yytext;
                    yylineno = this.lexer.yylineno;
                    yyloc = this.lexer.yylloc;
                    if (recovering > 0)
                        recovering--;
                } else { // error just occurred, resume old lookahead f/ before error
                    symbol = preErrorSymbol;
                    preErrorSymbol = null;
                }
                break;

            case 2: // reduce
                //this.reductionCount++;

                len = this.productions_[action[1]][1];

                // perform semantic action
                yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
                // default location, uses first token for firsts, last for lasts
                yyval._$ = {
                    first_line: lstack[lstack.length-(len||1)].first_line,
                    last_line: lstack[lstack.length-1].last_line,
                    first_column: lstack[lstack.length-(len||1)].first_column,
                    last_column: lstack[lstack.length-1].last_column
                };
                r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);

                if (typeof r !== 'undefined') {
                    return r;
                }

                // pop off stack
                if (len) {
                    stack = stack.slice(0,-1*len*2);
                    vstack = vstack.slice(0, -1*len);
                    lstack = lstack.slice(0, -1*len);
                }

                stack.push(this.productions_[action[1]][0]);    // push nonterminal (reduce)
                vstack.push(yyval.$);
                lstack.push(yyval._$);
                // goto new state = table[STATE][NONTERMINAL]
                newState = table[stack[stack.length-2]][stack[stack.length-1]];
                stack.push(newState);
                break;

            case 3: // accept
                return true;
        }

    }

    return true;
}};/* Jison generated lexer */
var lexer = (function(){

var lexer = ({EOF:1,
parseError:function parseError(str, hash) {
        if (this.yy.parseError) {
            this.yy.parseError(str, hash);
        } else {
            throw new Error(str);
        }
    },
setInput:function (input) {
        this._input = input;
        this._more = this._less = this.done = false;
        this.yylineno = this.yyleng = 0;
        this.yytext = this.matched = this.match = '';
        this.conditionStack = ['INITIAL'];
        this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
        return this;
    },
input:function () {
        var ch = this._input[0];
        this.yytext+=ch;
        this.yyleng++;
        this.match+=ch;
        this.matched+=ch;
        var lines = ch.match(/\n/);
        if (lines) this.yylineno++;
        this._input = this._input.slice(1);
        return ch;
    },
unput:function (ch) {
        this._input = ch + this._input;
        return this;
    },
more:function () {
        this._more = true;
        return this;
    },
pastInput:function () {
        var past = this.matched.substr(0, this.matched.length - this.match.length);
        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
    },
upcomingInput:function () {
        var next = this.match;
        if (next.length < 20) {
            next += this._input.substr(0, 20-next.length);
        }
        return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
    },
showPosition:function () {
        var pre = this.pastInput();
        var c = new Array(pre.length + 1).join("-");
        return pre + this.upcomingInput() + "\n" + c+"^";
    },
next:function () {
        if (this.done) {
            return this.EOF;
        }
        if (!this._input) this.done = true;

        var token,
            match,
            col,
            lines;
        if (!this._more) {
            this.yytext = '';
            this.match = '';
        }
        var rules = this._currentRules();
        for (var i=0;i < rules.length; i++) {
            match = this._input.match(this.rules[rules[i]]);
            if (match) {
                lines = match[0].match(/\n.*/g);
                if (lines) this.yylineno += lines.length;
                this.yylloc = {first_line: this.yylloc.last_line,
                               last_line: this.yylineno+1,
                               first_column: this.yylloc.last_column,
                               last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
                this.yytext += match[0];
                this.match += match[0];
                this.matches = match;
                this.yyleng = this.yytext.length;
                this._more = false;
                this._input = this._input.slice(match[0].length);
                this.matched += match[0];
                token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
                if (token) return token;
                else return;
            }
        }
        if (this._input === "") {
            return this.EOF;
        } else {
            this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 
                    {text: "", token: null, line: this.yylineno});
        }
    },
lex:function lex() {
        var r = this.next();
        if (typeof r !== 'undefined') {
            return r;
        } else {
            return this.lex();
        }
    },
begin:function begin(condition) {
        this.conditionStack.push(condition);
    },
popState:function popState() {
        return this.conditionStack.pop();
    },
_currentRules:function _currentRules() {
        return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
    }});
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {

var YYSTATE=YY_START
switch($avoiding_name_collisions) {
case 0: this.begin("mu"); if (yy_.yytext) return 14; 
break;
case 1: return 14; 
break;
case 2: return 24; 
break;
case 3: return 16; 
break;
case 4: return 20; 
break;
case 5: return 19; 
break;
case 6: return 19; 
break;
case 7: return 23; 
break;
case 8: return 23; 
break;
case 9: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.begin("INITIAL"); return 15; 
break;
case 10: return 22; 
break;
case 11: return 34; 
break;
case 12: return 33; 
break;
case 13: return 33; 
break;
case 14: return 36; 
break;
case 15: /*ignore whitespace*/ 
break;
case 16: this.begin("INITIAL"); return 18; 
break;
case 17: this.begin("INITIAL"); return 18; 
break;
case 18: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28; 
break;
case 19: return 30; 
break;
case 20: return 30; 
break;
case 21: return 29; 
break;
case 22: return 33; 
break;
case 23: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33; 
break;
case 24: return 'INVALID'; 
break;
case 25: return 5; 
break;
}
};
lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s/.])/,/^\[.*\]/,/^./,/^$/];
lexer.conditions = {"mu":{"rules":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],"inclusive":false},"INITIAL":{"rules":[0,1,25],"inclusive":true}};return lexer;})()
parser.lexer = lexer;
return parser;
})();
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.parser = handlebars;
exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
exports.main = function commonjsMain(args) {
    if (!args[1])
        throw new Error('Usage: '+args[0]+' FILE');
    if (typeof process !== 'undefined') {
        var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
    } else {
        var cwd = require("file").path(require("file").cwd());
        var source = cwd.join(args[1]).read({charset: "utf-8"});
    }
    return exports.parser.parse(source);
}
if (typeof module !== 'undefined' && require.main === module) {
  exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
}
};
;
// lib/handlebars/compiler/base.js
Handlebars.Parser = handlebars;

Handlebars.parse = function(string) {
  Handlebars.Parser.yy = Handlebars.AST;
  return Handlebars.Parser.parse(string);
};

Handlebars.print = function(ast) {
  return new Handlebars.PrintVisitor().accept(ast);
};

Handlebars.logger = {
  DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,

  // override in the host environment
  log: function(level, str) {}
};

Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
;
// lib/handlebars/compiler/ast.js
(function() {

  Handlebars.AST = {};

  Handlebars.AST.ProgramNode = function(statements, inverse) {
    this.type = "program";
    this.statements = statements;
    if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
  };

  Handlebars.AST.MustacheNode = function(params, hash, unescaped) {
    this.type = "mustache";
    this.id = params[0];
    this.params = params.slice(1);
    this.hash = hash;
    this.escaped = !unescaped;
  };

  Handlebars.AST.PartialNode = function(id, context) {
    this.type    = "partial";

    // TODO: disallow complex IDs

    this.id      = id;
    this.context = context;
  };

  var verifyMatch = function(open, close) {
    if(open.original !== close.original) {
      throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
    }
  };

  Handlebars.AST.BlockNode = function(mustache, program, close) {
    verifyMatch(mustache.id, close);
    this.type = "block";
    this.mustache = mustache;
    this.program  = program;
  };

  Handlebars.AST.InverseNode = function(mustache, program, close) {
    verifyMatch(mustache.id, close);
    this.type = "inverse";
    this.mustache = mustache;
    this.program  = program;
  };

  Handlebars.AST.ContentNode = function(string) {
    this.type = "content";
    this.string = string;
  };

  Handlebars.AST.HashNode = function(pairs) {
    this.type = "hash";
    this.pairs = pairs;
  };

  Handlebars.AST.IdNode = function(parts) {
    this.type = "ID";
    this.original = parts.join(".");

    var dig = [], depth = 0;

    for(var i=0,l=parts.length; i<l; i++) {
      var part = parts[i];

      if(part === "..") { depth++; }
      else if(part === "." || part === "this") { this.isScoped = true; }
      else { dig.push(part); }
    }

    this.parts    = dig;
    this.string   = dig.join('.');
    this.depth    = depth;
    this.isSimple = (dig.length === 1) && (depth === 0);
  };

  Handlebars.AST.StringNode = function(string) {
    this.type = "STRING";
    this.string = string;
  };

  Handlebars.AST.IntegerNode = function(integer) {
    this.type = "INTEGER";
    this.integer = integer;
  };

  Handlebars.AST.BooleanNode = function(bool) {
    this.type = "BOOLEAN";
    this.bool = bool;
  };

  Handlebars.AST.CommentNode = function(comment) {
    this.type = "comment";
    this.comment = comment;
  };

})();;
// lib/handlebars/utils.js
Handlebars.Exception = function(message) {
  var tmp = Error.prototype.constructor.apply(this, arguments);

  for (var p in tmp) {
    if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
  }
};
Handlebars.Exception.prototype = new Error;

// Build out our basic SafeString type
Handlebars.SafeString = function(string) {
  this.string = string;
};
Handlebars.SafeString.prototype.toString = function() {
  return this.string.toString();
};

(function() {
  var escape = {
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#x27;",
    "`": "&#x60;"
  };

  var badChars = /&(?!\w+;)|[<>"'`]/g;
  var possible = /[&<>"'`]/;

  var escapeChar = function(chr) {
    return escape[chr] || "&amp;";
  };

  Handlebars.Utils = {
    escapeExpression: function(string) {
      // don't escape SafeStrings, since they're already safe
      if (string instanceof Handlebars.SafeString) {
        return string.toString();
      } else if (string == null || string === false) {
        return "";
      }

      if(!possible.test(string)) { return string; }
      return string.replace(badChars, escapeChar);
    },

    isEmpty: function(value) {
      if (typeof value === "undefined") {
        return true;
      } else if (value === null) {
        return true;
      } else if (value === false) {
        return true;
      } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
        return true;
      } else {
        return false;
      }
    }
  };
})();;
// lib/handlebars/compiler/compiler.js
Handlebars.Compiler = function() {};
Handlebars.JavaScriptCompiler = function() {};

(function(Compiler, JavaScriptCompiler) {
  Compiler.OPCODE_MAP = {
    appendContent: 1,
    getContext: 2,
    lookupWithHelpers: 3,
    lookup: 4,
    append: 5,
    invokeMustache: 6,
    appendEscaped: 7,
    pushString: 8,
    truthyOrFallback: 9,
    functionOrFallback: 10,
    invokeProgram: 11,
    invokePartial: 12,
    push: 13,
    assignToHash: 15,
    pushStringParam: 16
  };

  Compiler.MULTI_PARAM_OPCODES = {
    appendContent: 1,
    getContext: 1,
    lookupWithHelpers: 2,
    lookup: 1,
    invokeMustache: 3,
    pushString: 1,
    truthyOrFallback: 1,
    functionOrFallback: 1,
    invokeProgram: 3,
    invokePartial: 1,
    push: 1,
    assignToHash: 1,
    pushStringParam: 1
  };

  Compiler.DISASSEMBLE_MAP = {};

  for(var prop in Compiler.OPCODE_MAP) {
    var value = Compiler.OPCODE_MAP[prop];
    Compiler.DISASSEMBLE_MAP[value] = prop;
  }

  Compiler.multiParamSize = function(code) {
    return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]];
  };

  Compiler.prototype = {
    compiler: Compiler,

    disassemble: function() {
      var opcodes = this.opcodes, opcode, nextCode;
      var out = [], str, name, value;

      for(var i=0, l=opcodes.length; i<l; i++) {
        opcode = opcodes[i];

        if(opcode === 'DECLARE') {
          name = opcodes[++i];
          value = opcodes[++i];
          out.push("DECLARE " + name + " = " + value);
        } else {
          str = Compiler.DISASSEMBLE_MAP[opcode];

          var extraParams = Compiler.multiParamSize(opcode);
          var codes = [];

          for(var j=0; j<extraParams; j++) {
            nextCode = opcodes[++i];

            if(typeof nextCode === "string") {
              nextCode = "\"" + nextCode.replace("\n", "\\n") + "\"";
            }

            codes.push(nextCode);
          }

          str = str + " " + codes.join(" ");

          out.push(str);
        }
      }

      return out.join("\n");
    },

    guid: 0,

    compile: function(program, options) {
      this.children = [];
      this.depths = {list: []};
      this.options = options;

      // These changes will propagate to the other compiler components
      var knownHelpers = this.options.knownHelpers;
      this.options.knownHelpers = {
        'helperMissing': true,
        'blockHelperMissing': true,
        'each': true,
        'if': true,
        'unless': true,
        'with': true
      };
      if (knownHelpers) {
        for (var name in knownHelpers) {
          this.options.knownHelpers[name] = knownHelpers[name];
        }
      }

      return this.program(program);
    },

    accept: function(node) {
      return this[node.type](node);
    },

    program: function(program) {
      var statements = program.statements, statement;
      this.opcodes = [];

      for(var i=0, l=statements.length; i<l; i++) {
        statement = statements[i];
        this[statement.type](statement);
      }
      this.isSimple = l === 1;

      this.depths.list = this.depths.list.sort(function(a, b) {
        return a - b;
      });

      return this;
    },

    compileProgram: function(program) {
      var result = new this.compiler().compile(program, this.options);
      var guid = this.guid++;

      this.usePartial = this.usePartial || result.usePartial;

      this.children[guid] = result;

      for(var i=0, l=result.depths.list.length; i<l; i++) {
        depth = result.depths.list[i];

        if(depth < 2) { continue; }
        else { this.addDepth(depth - 1); }
      }

      return guid;
    },

    block: function(block) {
      var mustache = block.mustache;
      var depth, child, inverse, inverseGuid;

      var params = this.setupStackForMustache(mustache);

      var programGuid = this.compileProgram(block.program);

      if(block.program.inverse) {
        inverseGuid = this.compileProgram(block.program.inverse);
        this.declare('inverse', inverseGuid);
      }

      this.opcode('invokeProgram', programGuid, params.length, !!mustache.hash);
      this.declare('inverse', null);
      this.opcode('append');
    },

    inverse: function(block) {
      var params = this.setupStackForMustache(block.mustache);

      var programGuid = this.compileProgram(block.program);

      this.declare('inverse', programGuid);

      this.opcode('invokeProgram', null, params.length, !!block.mustache.hash);
      this.opcode('append');
    },

    hash: function(hash) {
      var pairs = hash.pairs, pair, val;

      this.opcode('push', '{}');

      for(var i=0, l=pairs.length; i<l; i++) {
        pair = pairs[i];
        val  = pair[1];

        this.accept(val);
        this.opcode('assignToHash', pair[0]);
      }
    },

    partial: function(partial) {
      var id = partial.id;
      this.usePartial = true;

      if(partial.context) {
        this.ID(partial.context);
      } else {
        this.opcode('push', 'depth0');
      }

      this.opcode('invokePartial', id.original);
      this.opcode('append');
    },

    content: function(content) {
      this.opcode('appendContent', content.string);
    },

    mustache: function(mustache) {
      var params = this.setupStackForMustache(mustache);

      this.opcode('invokeMustache', params.length, mustache.id.original, !!mustache.hash);

      if(mustache.escaped) {
        this.opcode('appendEscaped');
      } else {
        this.opcode('append');
      }
    },

    ID: function(id) {
      this.addDepth(id.depth);

      this.opcode('getContext', id.depth);

      this.opcode('lookupWithHelpers', id.parts[0] || null, id.isScoped || false);

      for(var i=1, l=id.parts.length; i<l; i++) {
        this.opcode('lookup', id.parts[i]);
      }
    },

    STRING: function(string) {
      this.opcode('pushString', string.string);
    },

    INTEGER: function(integer) {
      this.opcode('push', integer.integer);
    },

    BOOLEAN: function(bool) {
      this.opcode('push', bool.bool);
    },

    comment: function() {},

    // HELPERS
    pushParams: function(params) {
      var i = params.length, param;

      while(i--) {
        param = params[i];

        if(this.options.stringParams) {
          if(param.depth) {
            this.addDepth(param.depth);
          }

          this.opcode('getContext', param.depth || 0);
          this.opcode('pushStringParam', param.string);
        } else {
          this[param.type](param);
        }
      }
    },

    opcode: function(name, val1, val2, val3) {
      this.opcodes.push(Compiler.OPCODE_MAP[name]);
      if(val1 !== undefined) { this.opcodes.push(val1); }
      if(val2 !== undefined) { this.opcodes.push(val2); }
      if(val3 !== undefined) { this.opcodes.push(val3); }
    },

    declare: function(name, value) {
      this.opcodes.push('DECLARE');
      this.opcodes.push(name);
      this.opcodes.push(value);
    },

    addDepth: function(depth) {
      if(depth === 0) { return; }

      if(!this.depths[depth]) {
        this.depths[depth] = true;
        this.depths.list.push(depth);
      }
    },

    setupStackForMustache: function(mustache) {
      var params = mustache.params;

      this.pushParams(params);

      if(mustache.hash) {
        this.hash(mustache.hash);
      }

      this.ID(mustache.id);

      return params;
    }
  };

  JavaScriptCompiler.prototype = {
    // PUBLIC API: You can override these methods in a subclass to provide
    // alternative compiled forms for name lookup and buffering semantics
    nameLookup: function(parent, name, type) {
			if (/^[0-9]+$/.test(name)) {
        return parent + "[" + name + "]";
      } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
	    	return parent + "." + name;
			}
			else {
				return parent + "['" + name + "']";
      }
    },

    appendToBuffer: function(string) {
      if (this.environment.isSimple) {
        return "return " + string + ";";
      } else {
        return "buffer += " + string + ";";
      }
    },

    initializeBuffer: function() {
      return this.quotedString("");
    },
    // END PUBLIC API

    compile: function(environment, options, context, asObject) {
      this.environment = environment;
      this.options = options || {};

      this.name = this.environment.name;
      this.isChild = !!context;
      this.context = context || {
        programs: [],
        aliases: { self: 'this' },
        registers: {list: []}
      };

      this.preamble();

      this.stackSlot = 0;
      this.stackVars = [];

      this.compileChildren(environment, options);

      var opcodes = environment.opcodes, opcode;

      this.i = 0;

      for(l=opcodes.length; this.i<l; this.i++) {
        opcode = this.nextOpcode(0);

        if(opcode[0] === 'DECLARE') {
          this.i = this.i + 2;
          this[opcode[1]] = opcode[2];
        } else {
          this.i = this.i + opcode[1].length;
          this[opcode[0]].apply(this, opcode[1]);
        }
      }

      return this.createFunctionContext(asObject);
    },

    nextOpcode: function(n) {
      var opcodes = this.environment.opcodes, opcode = opcodes[this.i + n], name, val;
      var extraParams, codes;

      if(opcode === 'DECLARE') {
        name = opcodes[this.i + 1];
        val  = opcodes[this.i + 2];
        return ['DECLARE', name, val];
      } else {
        name = Compiler.DISASSEMBLE_MAP[opcode];

        extraParams = Compiler.multiParamSize(opcode);
        codes = [];

        for(var j=0; j<extraParams; j++) {
          codes.push(opcodes[this.i + j + 1 + n]);
        }

        return [name, codes];
      }
    },

    eat: function(opcode) {
      this.i = this.i + opcode.length;
    },

    preamble: function() {
      var out = [];

      if (!this.isChild) {
        var copies = "helpers = helpers || Handlebars.helpers;";
        if(this.environment.usePartial) { copies = copies + " partials = partials || Handlebars.partials;"; }
        out.push(copies);
      } else {
        out.push('');
      }

      if (!this.environment.isSimple) {
        out.push(", buffer = " + this.initializeBuffer());
      } else {
        out.push("");
      }

      // track the last context pushed into place to allow skipping the
      // getContext opcode when it would be a noop
      this.lastContext = 0;
      this.source = out;
    },

    createFunctionContext: function(asObject) {
      var locals = this.stackVars;
      if (!this.isChild) {
        locals = locals.concat(this.context.registers.list);
      }

      if(locals.length > 0) {
        this.source[1] = this.source[1] + ", " + locals.join(", ");
      }

      // Generate minimizer alias mappings
      if (!this.isChild) {
        var aliases = []
        for (var alias in this.context.aliases) {
          this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
        }
      }

      if (this.source[1]) {
        this.source[1] = "var " + this.source[1].substring(2) + ";";
      }

      // Merge children
      if (!this.isChild) {
        this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
      }

      if (!this.environment.isSimple) {
        this.source.push("return buffer;");
      }

      var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];

      for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
        params.push("depth" + this.environment.depths.list[i]);
      }

      if(params.length === 4 && !this.environment.usePartial) { params.pop(); }

      if (asObject) {
        params.push(this.source.join("\n  "));

        return Function.apply(this, params);
      } else {
        var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n  ' + this.source.join("\n  ") + '}';
        Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
        return functionSource;
      }
    },

    appendContent: function(content) {
      this.source.push(this.appendToBuffer(this.quotedString(content)));
    },

    append: function() {
      var local = this.popStack();
      this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
      if (this.environment.isSimple) {
        this.source.push("else { " + this.appendToBuffer("''") + " }");
      }
    },

    appendEscaped: function() {
      var opcode = this.nextOpcode(1), extra = "";
      this.context.aliases.escapeExpression = 'this.escapeExpression';

      if(opcode[0] === 'appendContent') {
        extra = " + " + this.quotedString(opcode[1][0]);
        this.eat(opcode);
      }

      this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
    },

    getContext: function(depth) {
      if(this.lastContext !== depth) {
        this.lastContext = depth;
      }
    },

    lookupWithHelpers: function(name, isScoped) {
      if(name) {
        var topStack = this.nextStack();

        this.usingKnownHelper = false;

        var toPush;
        if (!isScoped && this.options.knownHelpers[name]) {
          toPush = topStack + " = " + this.nameLookup('helpers', name, 'helper');
          this.usingKnownHelper = true;
        } else if (isScoped || this.options.knownHelpersOnly) {
          toPush = topStack + " = " + this.nameLookup('depth' + this.lastContext, name, 'context');
        } else {
          toPush =  topStack + " = "
              + this.nameLookup('helpers', name, 'helper')
              + " || "
              + this.nameLookup('depth' + this.lastContext, name, 'context');
        }
        
        toPush += ';';
        this.source.push(toPush);
      } else {
        this.pushStack('depth' + this.lastContext);
      }
    },

    lookup: function(name) {
      var topStack = this.topStack();
      this.source.push(topStack + " = (" + topStack + " === null || " + topStack + " === undefined || " + topStack + " === false ? " +
 				topStack + " : " + this.nameLookup(topStack, name, 'context') + ");");
    },

    pushStringParam: function(string) {
      this.pushStack('depth' + this.lastContext);
      this.pushString(string);
    },

    pushString: function(string) {
      this.pushStack(this.quotedString(string));
    },

    push: function(name) {
      this.pushStack(name);
    },

    invokeMustache: function(paramSize, original, hasHash) {
      this.populateParams(paramSize, this.quotedString(original), "{}", null, hasHash, function(nextStack, helperMissingString, id) {
        if (!this.usingKnownHelper) {
          this.context.aliases.helperMissing = 'helpers.helperMissing';
          this.context.aliases.undef = 'void 0';
          this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
          if (nextStack !== id) {
            this.source.push("else { " + nextStack + " = " + id + "; }");
          }
        }
      });
    },

    invokeProgram: function(guid, paramSize, hasHash) {
      var inverse = this.programExpression(this.inverse);
      var mainProgram = this.programExpression(guid);

      this.populateParams(paramSize, null, mainProgram, inverse, hasHash, function(nextStack, helperMissingString, id) {
        if (!this.usingKnownHelper) {
          this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
          this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
        }
      });
    },

    populateParams: function(paramSize, helperId, program, inverse, hasHash, fn) {
      var needsRegister = hasHash || this.options.stringParams || inverse || this.options.data;
      var id = this.popStack(), nextStack;
      var params = [], param, stringParam, stringOptions;

      if (needsRegister) {
        this.register('tmp1', program);
        stringOptions = 'tmp1';
      } else {
        stringOptions = '{ hash: {} }';
      }

      if (needsRegister) {
        var hash = (hasHash ? this.popStack() : '{}');
        this.source.push('tmp1.hash = ' + hash + ';');
      }

      if(this.options.stringParams) {
        this.source.push('tmp1.contexts = [];');
      }

      for(var i=0; i<paramSize; i++) {
        param = this.popStack();
        params.push(param);

        if(this.options.stringParams) {
          this.source.push('tmp1.contexts.push(' + this.popStack() + ');');
        }
      }

      if(inverse) {
        this.source.push('tmp1.fn = tmp1;');
        this.source.push('tmp1.inverse = ' + inverse + ';');
      }

      if(this.options.data) {
        this.source.push('tmp1.data = data;');
      }

      params.push(stringOptions);

      this.populateCall(params, id, helperId || id, fn);
    },

    populateCall: function(params, id, helperId, fn) {
      var paramString = ["depth0"].concat(params).join(", ");
      var helperMissingString = ["depth0"].concat(helperId).concat(params).join(", ");

      var nextStack = this.nextStack();

      if (this.usingKnownHelper) {
        this.source.push(nextStack + " = " + id + ".call(" + paramString + ");");
      } else {
        this.context.aliases.functionType = '"function"';
        this.source.push("if(typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
      }
      fn.call(this, nextStack, helperMissingString, id);
      this.usingKnownHelper = false;
    },

    invokePartial: function(context) {
      this.pushStack("self.invokePartial(" + this.nameLookup('partials', context, 'partial') + ", '" + context + "', " + this.popStack() + ", helpers, partials);");
    },

    assignToHash: function(key) {
      var value = this.popStack();
      var hash = this.topStack();

      this.source.push(hash + "['" + key + "'] = " + value + ";");
    },

    // HELPERS

    compiler: JavaScriptCompiler,

    compileChildren: function(environment, options) {
      var children = environment.children, child, compiler;

      for(var i=0, l=children.length; i<l; i++) {
        child = children[i];
        compiler = new this.compiler();

        this.context.programs.push('');     // Placeholder to prevent name conflicts for nested children
        var index = this.context.programs.length;
        child.index = index;
        child.name = 'program' + index;
        this.context.programs[index] = compiler.compile(child, options, this.context);
      }
    },

    programExpression: function(guid) {
      if(guid == null) { return "self.noop"; }

      var child = this.environment.children[guid],
          depths = child.depths.list;
      var programParams = [child.index, child.name, "data"];

      for(var i=0, l = depths.length; i<l; i++) {
        depth = depths[i];

        if(depth === 1) { programParams.push("depth0"); }
        else { programParams.push("depth" + (depth - 1)); }
      }

      if(depths.length === 0) {
        return "self.program(" + programParams.join(", ") + ")";
      } else {
        programParams.shift();
        return "self.programWithDepth(" + programParams.join(", ") + ")";
      }
    },

    register: function(name, val) {
      this.useRegister(name);
      this.source.push(name + " = " + val + ";");
    },

    useRegister: function(name) {
      if(!this.context.registers[name]) {
        this.context.registers[name] = true;
        this.context.registers.list.push(name);
      }
    },

    pushStack: function(item) {
      this.source.push(this.nextStack() + " = " + item + ";");
      return "stack" + this.stackSlot;
    },

    nextStack: function() {
      this.stackSlot++;
      if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
      return "stack" + this.stackSlot;
    },

    popStack: function() {
      return "stack" + this.stackSlot--;
    },

    topStack: function() {
      return "stack" + this.stackSlot;
    },

    quotedString: function(str) {
      return '"' + str
        .replace(/\\/g, '\\\\')
        .replace(/"/g, '\\"')
        .replace(/\n/g, '\\n')
        .replace(/\r/g, '\\r') + '"';
    }
  };

  var reservedWords = ("break case catch continue default delete do else finally " +
                       "for function if in instanceof new return switch this throw " + 
                       "try typeof var void while with null true false").split(" ");

  var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};

  for(var i=0, l=reservedWords.length; i<l; i++) {
    compilerWords[reservedWords[i]] = true;
  }

	JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
		if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
			return true;
		}
		return false;
	}

})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);

Handlebars.precompile = function(string, options) {
  options = options || {};

  var ast = Handlebars.parse(string);
  var environment = new Handlebars.Compiler().compile(ast, options);
  return new Handlebars.JavaScriptCompiler().compile(environment, options);
};

Handlebars.compile = function(string, options) {
  options = options || {};

  var ast = Handlebars.parse(string);
  var environment = new Handlebars.Compiler().compile(ast, options);
  var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
  return Handlebars.template(templateSpec);
};
;
// lib/handlebars/vm.js
Handlebars.VM = {
  template: function(templateSpec) {
    // Just add water
    var container = {
      escapeExpression: Handlebars.Utils.escapeExpression,
      invokePartial: Handlebars.VM.invokePartial,
      programs: [],
      program: function(i, fn, data) {
        var programWrapper = this.programs[i];
        if(data) {
          return Handlebars.VM.program(fn, data);
        } else if(programWrapper) {
          return programWrapper;
        } else {
          programWrapper = this.programs[i] = Handlebars.VM.program(fn);
          return programWrapper;
        }
      },
      programWithDepth: Handlebars.VM.programWithDepth,
      noop: Handlebars.VM.noop
    };

    return function(context, options) {
      options = options || {};
      return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
    };
  },

  programWithDepth: function(fn, data, $depth) {
    var args = Array.prototype.slice.call(arguments, 2);

    return function(context, options) {
      options = options || {};

      return fn.apply(this, [context, options.data || data].concat(args));
    };
  },
  program: function(fn, data) {
    return function(context, options) {
      options = options || {};

      return fn(context, options.data || data);
    };
  },
  noop: function() { return ""; },
  invokePartial: function(partial, name, context, helpers, partials) {
    if(partial === undefined) {
      throw new Handlebars.Exception("The partial " + name + " could not be found");
    } else if(partial instanceof Function) {
      return partial(context, {helpers: helpers, partials: partials});
    } else if (!Handlebars.compile) {
      throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in vm mode");
    } else {
      partials[name] = Handlebars.compile(partial);
      return partials[name](context, {helpers: helpers, partials: partials});
    }
  }
};

Handlebars.template = Handlebars.VM.template;
;


/**
 * Custom addition for the Kanso package. Export the same browser interface
 * when used as a CommonJS module in CouchDB.
 */

if (typeof module !== 'undefined' && typeof exports !== 'undefined') {
    var exports = module.exports = Handlebars;
}

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["404.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var self=this;


  return "<h1>404 - Not Found</h1>\n";});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["base.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;


  buffer += "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\" style=\"background: #444;\"> \n  <head> \n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /> \n    <title>";
  stack1 = helpers.title || depth0.title;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "title", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</title> \n  </head> \n  <body> \n    <div id=\"content\">\n      ";
  stack1 = helpers.content || depth0.content;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "content", { hash: {} }); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n    </div>\n    <script src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/js/jquery-1.5.2.min.js\"></script>\n    <script src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/js/json2.js\"></script>\n    <script src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/modules.js\"></script>\n    <script src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/kanso.js\"></script>\n  </body>\n</html>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["welcome.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;


  buffer += "<img style=\"position: absolute; left: 50%; margin-left: -394px; top: 200px\" src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/images/tbp.png\">";
  return buffer;});
})();
Handlebars.registerHelper('uc', function (str) {
    return encodeURIComponent(str);
});

// TODO: add optional context argument?
Handlebars.registerHelper('include', function (name) {
    if (!exports.templates[name]) {
        throw new Error('Template Not Found: ' + name);
    }
    return exports.templates[name](this, {});
});

Handlebars.registerHelper('ifequal', function (val1, val2, fn, elseFn) {
    if (val1 === val2) {
        return fn();
    }
    else if (elseFn) {
        return elseFn();
    }
});


})};

/********** duality/templates **********/

kanso.moduleCache["duality/templates"] = {load: (function (module, exports, require) {

/**
 * Module dependencies
 */

var utils = require('duality/utils'),
    handlebars = require('handlebars'),
    flashmessages;

try {
    flashmessages = require('./flashmessages');
}
catch (e) {
    // flashmessages module may not be available
}




/**
 * Synchronously render dust template and return result, automatically adding
 * baseURL to the template's context. The request object is required so we
 * can determine the value of baseURL.
 *
 * @name render(name, req, context)
 * @param {String} name
 * @param {Object} req
 * @param {Object} context
 * @returns {String}
 * @api public
 */

exports.render = function (name, req, context) {
    handlebars.registerHelper('baseURL', function () {
        return utils.getBaseURL(req);
    });
    handlebars.registerHelper('isBrowser', utils.isBrowser);
    context.userCtx = req.userCtx;
    if (!context.flashMessages && flashmessages) {
        context.flashMessages = flashmessages.getMessages(req);
    }
    if (!handlebars.templates[name]) {
        throw new Error('Template Not Found: ' + name);
    }
    return handlebars.templates[name](context, {});
};


})};

/********** sanitize **********/

kanso.moduleCache["sanitize"] = {load: (function (module, exports, require) {

/**
 * Input sanitization, escaping, and construction functions
 * covering security-sensitive areas.
 *
 * @module
 */

/**
 * Module dependencies
 */

var _ = require('underscore')._;

/**
 *
 * @name escapeUrlParams(s)
 * @param {Object} obj An object containing url parameters, with
 *          parameter names stored as property names (or keys).
 * @returns {String}
 * @api public
 */

exports.escapeUrlParams = exports.url = function (obj)
{
    var rv = [ ];

    for (var key in obj) {
        rv.push(
            encodeURIComponent(key) +
                '=' + encodeURIComponent(obj[key])
        );
    }

    return (rv.length > 0 ? ('?' + rv.join('&')) : '');
};


/**
 * Encodes required characters as HTML entities so a string
 * can be included in a page. This function must be used to
 * avoid Cross-site Scripting attacks.
 *
 * @name escapeHtml(s)
 * @param {String} s
 * @returns {String}
 * @api public
 */

exports.escapeHtml = exports.h = function (s)
{
    s = ('' + s); /* Coerce to string */
    s = s.replace(/&/g, '&amp;');
    s = s.replace(/</g, '&lt;');
    s = s.replace(/>/g, '&gt;');
    s = s.replace(/"/g, '&quot;');
    s = s.replace(/'/g, '&#39;');
    return s;
};


/**
 * Encodes selected characters in a string, so that the string
 * can be safely used within a Javascript string constant. This
 * function must be used to avoid cross-site scripting attacks
 * (or, in some cases, arbitrary server-side code execution).
 *
 * @name escapeJavascriptString(s)
 * @param {String} s
 * @returns {String}
 * @api public
 */

exports.escapeJavascriptString = exports.js = function (s)
{
    s = ('' + s); /* Coerce to string */
    s = s.replace(/'/g, "\\'");
    s = s.replace(/"/g, '\\"');
    return s;
};


/**
 * Encodes selected characters in a string, so that the string
 * can be safely used within an XML character data section.
 * This function must be used to avoid cross-site scripting attacks.
 *
 * @name escapeXmlCharacterData(s)
 * @param {String} s
 * @returns {String}
 * @api public
 */

exports.escapeXmlCharacterData = exports.cdata = function (s)
{
    s = ('' + s); /* Coerce to string */
    s = s.replace(/\]\]>/g, '');
    return s;
};


/**
 * Encodes selected characters in a string, so that the string
 * can be safely used on the right side of a CSS attribute selector's
 * equal sign. This function should be used when using a user-modifiable
 * string to match an element by attribute -- either in jQuery, or as part
 * of a dynamically-generated CSS selector.
 *
 * @name escapeAttributeSelectorValue(s)
 * @param {String} s
 * @returns {String}
 * @api public
 */

exports.escapeAttributeSelectorValue = exports.css = function (s)
{
    s = ('' + s); /* Coerce to string */
    s = s.replace(/['"\\=\[\]]/, '\\$1');
    return s;
};


/**
 * Takes any number of arguments, and combines them together
 * to safely form a string that's suitable for use as an
 * identifier or name.
 *
 * @name generateAbstractIdentifier(rv, args, esc_fn, final_fn)
 * @param {Array} rv An accumulator array, used to store results.
 * @param {Array} args An array of arguments to the id-generating function.
 * @param {Array} esc_fn An input-sanitizing function, to be applied to
 *          each component of the identifier before it's emitted.
 * @param {Array} join_fn A function that accepts an array of sanitized
 *          identifier components, and produces a string.
 * @returns {String}
 */

exports.generateAbstractIdentifier = function (rv, args, esc_fn, join_fn) {
    if (args.length <= 0) {
        return null;
    }
    for (var i = 0, len = args.length; i < len; ++i) {
        var arg = args[i];
        if (arg !== undefined && arg !== null) {
            if (_.isArray(arg)) {
                /* Avoid recursion; limit to one level deep */
                for (var j = 0, lenj = arg.length; j < lenj; ++j) {
                    if (arg[j] !== undefined && arg[j] !== null) {
                        rv.push(esc_fn(arg[j]));
                    }
                }
            } else {
                rv.push(esc_fn(arg));
            }
        }
    }
    return join_fn(rv);
};


/**
 * Takes any number of arguments, and combines them together
 * to safely form a string that's suitable for use as a DOM
 * element identifier.
 *
 * @name generateDomIdentifier()
 * @returns {String}
 * @api public
 */

exports.generateDomIdentifier = exports.id = function (/* ... */) {
    return exports.generateAbstractIdentifier(
        [ 'id' ], arguments, function (x) {
            return ('' + x).replace(/[^A-Za-z0-9_]+/, '_');
        }, function (x) {
            return x.join('_');
        }
    );
};


/**
 * Takes any number of arguments, and combines them together
 * to safely form a string that's suitable for use in a DOM
 * element's name attribute.
 *
 * @name generateDomName()
 * @returns {String}
 * @api public
 */

exports.generateDomName = exports.name = function (/* ... */) {
    return exports.generateAbstractIdentifier(
        [ ], arguments, function (x) {
            return ('' + x).replace(/[\'\"]+/, '_');
        }, function (x) {
            return x.join('.');
        }
    );
};


})};

/********** couchtypes/actions.core **********/

kanso.moduleCache["couchtypes/actions.core"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Implementation of widget actions. These are procedures
 * that can be referenced by widgets to present/collect information,
 * manipulate the DOM, or otherwise affect the application state
 * when a widget is acted upon.
 *
 * @module
 */

/**
 * Module dependencies
 */

var utils = require('couchtypes/utils'),
    sanitize = require('sanitize'),
    _ = require('underscore')._;

var h = sanitize.escapeHtml;


/**
 * Create a closure that loads {module}, and invokes {method}
 * on it. The {options} parameter is prepenced to the method's
 * argument list. This separate function silences a lint warning.
 * See the parse method for details.
 */

exports.make_action_handler = function (module, method, options) {
    return function () {
        var args = [ options ].concat(
            Array.prototype.slice.apply(arguments)
        );
        return require(module)[method].apply(null, args);
    };
};


/**
 * Convert an object containing several [ module, callback ] or
 * { module: x, callback: y } items in to an object containing
 * several native javascript functions, by using require.
 *
 * @param actions An object, containing items describing a
 *          function that can be obtained via require().
 */

exports.parse = function (actions) {
    var rv = {};
    for (var k in actions) {
        var module, callback, options;
        var action = actions[k];
        if (action === false) {
            rv[k] = utils.emptyFunction;
        } else {
            if (_.isArray(action)) {
                module = action[0];
                callback = action[1];
                options = action[2];
            } else if (_.isFunction(action)) {
                rv[k] = action;
                continue;
            } else if (typeof(action) === 'object') {
                module = action.module;
                callback = action.callback;
                options = action.options;
            } else {
                throw new Error(
                    'Action `' + k + '` is `' + typeof(action) + '`, ' +
                        "which this function doesn't know how to interpret"
                );
            }
            /* Resolve function description to actual function */
            rv[k] = exports.make_action_handler(module, callback, options);
        }
    }
    return rv;
};



})};

/********** couchtypes/actions.dialog **********/

kanso.moduleCache["couchtypes/actions.dialog"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Implementation of widget actions. These are procedures
 * that can be referenced by widgets to present/collect information,
 * manipulate the DOM, or otherwise affect the application state
 * when a widget is acted upon.
 *
 * @module
 */

/**
 * Module dependencies
 */

var core = require('./actions.core'),
    utils = require('couchtypes/utils'),
    sanitize = require('sanitize'),
    _ = require('underscore')._;

var h = sanitize.escapeHtml;


/**
 * An action that produces a dialog box or popup, with buttons along
 * its bottom. The contents of the dialog can be controlled by
 * setting either options.widget or options.type. If both are
 * specified, the widget will be used. If only type is specified,
 * this function transparently instansiates an embedForm widget,
 * which does the actual form rendering and presentation. To control
 * which dialog implementation is used, set action_options.style.
 */

exports.showDialog = function (action_options,
                               names, data, options, callback) {
    options = (options || {});
    action_options = (action_options || {});

    var operation = 'update';
    var widget = action_options.widget;
    var name = sanitize.generateDomName(data.path);
    var path_extra = (options.path_extra || []).concat([ 'dialog' ]);

    if (names.action !== 'edit') {
        operation = names.action;
    }
    var widget_options = {
        path_extra: path_extra,
        operation: operation
    };

    /* Shortcut:
        If no widget is specified, assume embedForm, and
        use the options to showDialog as options to embedForm. */

    if (!widget && action_options.type) {
        var widgets = require('./widgets');
        widget = widgets.embedForm(
            _.defaults(action_options.options || {}, {
                type: action_options.type
            })
        );
    }

    if (!widget) {
        throw new Error(
            'showDialog: Unable to determine the widget to' +
            ' use for the field named `' + data.path.join('.') +
            '`; widget or field type was not correctly specified'
        );
    }

    /* Dialog setup and event handling:
        This is wrapped in a closure to allow it to easily be
        used inside both synchronous and asynchronous functions. */

    var generateAbstractDialog = function (_impl) {

        /* Generate strings for content */
        var cancel_label = 'Cancel';
        var title_label = action_options.title;
        var action_label = utils.titleize(names.action);

        if (!title_label) {
            var type_label = utils.titleize(names.type);
            title_label = [ action_label, type_label ].join(' ');
        }

        /* Generate inner elements */
        var title_elt = $(
            '<h2>' + h(title_label) + '</h2>'
        );
        var ok_elt = $(
            '<input type="submit" value="' + h(action_label) + '" />'
        );
        var cancel_elt = $(
            '<input type="button" value="' + h(cancel_label) + '" />'
        );
        var actions_elt = $(
            '<div class="actions" />'
        );

        /* Create widget's parent element */
        var div = $('<div class="dialog" />');

        /* Add dialog title */
        div.append(title_elt);

        /* Draw widget */
        div.append(
            widget.toHTML(
                name, data.value, data.raw, data.field,
                widget_options, data.errors
            )
        );

        /* Find the form element:
            This is created by the call to widget.toHTML, above. */

        var form_elt = div.closestChild('form');

        if (form_elt.length <= 0) {

            /* No form element found?
                Generate one and wrap the contents of the dialog with it.
                This provides support for widgets other than embedForm. */

            var wrapper_elt = $('<div class="dialog" />');

            /* Mark as a rendering context for CSS */
            div.addClass('render');
            div.removeClass('dialog');

            form_elt = $('<form />');
            form_elt.append(div);
            wrapper_elt.append(form_elt);
            div = wrapper_elt;
        }


        /* Insert elements:
            This is the panel of actions, including ok and cancel. */
        
        actions_elt.append(ok_elt);
        actions_elt.append(cancel_elt);
        form_elt.append(actions_elt);

        /* Insert elements:
            This is a progress indicator / spinner element. */
       
        var spinner_elt = $('<div class="spinner" />');
        spinner_elt.hide();

        form_elt.append(spinner_elt);
        form_elt.append('<div class="clear" />');

        /* Event handler:
            Handle successful outcome. */

        ok_elt.click(function (ev) {

            /* Show progress indicator:
                This is deleted automatically when the dialog is closed. */

            spinner_elt.show();

            /* Validate widget:
                This usually defers to a form type's implementation.
                Most simple widgets just return true for this method. */

            data.errors =
                widget.validate(div, data.path, widget_options);

            if (data.errors.length > 0) {

                /* Repost dialog box:
                    This will replace the current dialog box.
                    some dialog box implementations return to the event loop
                    before actually removing its elements, so we do the same. */

                _impl.close(div, options);

                exports.showDialog(
                    action_options, names, data, options, callback
                );

            } else {

                /* Invoke callback:
                    Let the widget that invoked us know that we're done. */

                callback(
                    true, widget.getValue(div, data.path, widget_options)
                );

                /* Order matters:
                    The callback may refer to elements inside of the
                    dialog, so don't destroy it until after it returns, and
                    has had a chance to register any callbacks / timeouts. */

                _impl.close(div, options);
            }

            ev.preventDefault();
        });

        /* Event handler:
            Handle negative outcome, or cancellation. */

        cancel_elt.click(function () {
            callback(
                false, widget.getValue(div, data.path, widget_options)
            );
            _impl.close(div, options);
        });

        /* Make default form action 'ok' */
        form_elt.submit(function (ev) {
            ev.preventDefault();
            ok_elt.click();
            return false;
        });

        /* Launch dialog:
            This wraps the <div> and inserts it in the DOM. */

        _impl.open(div, options);

        /* Initialize widget:
            We do this last -- this makes sure all elements are present
            and initialized prior to client-side widget initialization. */

        widget.clientInit(
            data.field, data.path, data.value,
                data.raw, data.errors, widget_options
        );
    };

    /* Pop-up style dialog:
        Javascript implementation provided by uPopup. */

    var popup;

    generateAbstractDialog({
        open: function (elt) {
            $(elt).uPopup('create', data.element, {
                center: true
            });
            var popup = $(elt).uPopup('wrapper');
        },
        close: function (elt) {
            $(elt).uPopup('destroy');
        }
    });

    return;
};




})};

/********** couchtypes/actions.embed **********/

kanso.moduleCache["couchtypes/actions.embed"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Implementation of widget actions. These are procedures
 * that can be referenced by widgets to present/collect information,
 * manipulate the DOM, or otherwise affect the application state
 * when a widget is acted upon.
 *
 * @module
 */

/**
 * Module dependencies
 */

var core = require('./actions.core'),
    db = require('db'),
    utils = require('couchtypes/utils'),
    _ = require('underscore')._,
    duality;


try {
    duality = require('duality/core');
}
catch (e) {
    // may not be available
}


/**
 * Update the action originator (i.e. a widget) with a new value.
 * If the originating widget is not widget.embedList, it must provide
 * a setListItemValue method, which accepts three arguments -- (i) a DOM
 * element that wraps the widget; (ii) the new value for the widget; and
 * (iii) a set of widget options, which sometimes contains information
 * about the widget's nesting context and/or list item offset. It's
 * important to note that this action doesn't cause any data to be
 * saved on its own, but merely updates a widget's value for use
 * in the next save operation.
 */

exports.defaultEmbedSave = function (action_options, names, 
                                     data, options, callback) {
    if (!data.element) {
        return callback(false, data.value);
    }

    var widget = utils.getPropertyPath(data, [ 'field', 'widget' ]);
    var item_elt = $(data.element).closest('.item');

    widget.setListItemValue(
        item_elt, data.value, options
    );

    return callback(true, data.value);
};


/**
 * Saves the document specified in data.value. This action is
 * intended for use with the reference and uniqueReference types,
 * but can in theory be used by any widget or action that handles
 * external (i.e. non-embedded) documents. When combined with the
 * embedForm widget's support for dereferencing these field types,
 * this action provides a way to easily manage linked external
 * documents in Kanso.
 */

exports.saveExternalDocument = function (action_options, names, 
                                         data, options, callback) {
    var doc = data.value;
    delete doc._deleted;

    if (!doc || !doc._id) {
        throw new Error(
            'saveExternalDocument: The value provided is not a valid' +
                ' document, or does not contain a valid document identifier'
        );
    }

    var appdb = db.use(duality ? duality.getDBURL(): '/');
    appdb.saveDoc(
        doc, function (err, rv) {
            if (err) {
                throw new Error(
                    'saveExternalDocument: Failed to save document' +
                        ' with identifier `' + doc._id + '`'
                );
            }
            /* Indicate success */
            callback(true, doc);
        }
    );
};



})};

/********** couchtypes/actions **********/

kanso.moduleCache["couchtypes/actions"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Widgets define the way a Field object is displayed when rendered as part of a
 * Form. Changing a Field's widget will be reflected in the admin app.
 *
 * @module
 */

var _ = require('underscore')._;

var modules = [
    './actions.core', './actions.dialog', './actions.embed'
];

_.reduce(modules, function (a, m) {
    return _.extend(a, require(m));
}, exports);



})};

/********** couchtypes/fields **********/

kanso.moduleCache["couchtypes/fields"] = {load: (function (module, exports, require) {

/**
 * The building blocks of Types and Forms, Fields help to validate and authorize
 * changes to docuemnt values.
 *
 * @module
 */


/**
 * Module dependencies
 */

var permissions = require('couchtypes/permissions'),
    validators = require('couchtypes/validators'),
    widgets = require('couchtypes/widgets'),
    utils = require('couchtypes/utils'),
    _ = require('underscore')._;


/**
 * Field objects are used when constructing content types and forms.
 *
 * #### Options ####
 *
 * * **omit_empty**  *Boolean* - whether to omit the field from a document when
 *                               the field is empty
 * * **permissions** *Object*  - a permissions check function or an object
 *                               containing separate functions to run on create,
 *                               edit and update operations.
 * * **validators**  *Array*   - an array of validation functions (default: [])
 * * **required**    *Boolean* - whether the field is required (default: true)
 *
 * @constructor
 * @name Field
 * @param {Object} options
 * @api public
 */

var Field = exports.Field = function Field(options) {
    _.extend(this, _.defaults(options || {}, {
        widget: widgets.text(),
        omit_empty: false,
        permissions: {},
        validators: [],
        required: true
    }));
    return this;
};


/**
 * Parses a raw value returning the correct JavaScript type for this field.
 * This will usually be overridden by other field types.
 *
 * @name Field.parse(raw)
 * @param raw
 * @api public
 */

Field.prototype.parse = function (raw) {
    return raw;
};


/**
 * Test values to see if field is considered empty.
 *
 * This function accepts the raw value even though by default it only
 * checks the parsed value, so that other field types overridding this method
 * have the raw data available.
 *
 * @name Field.isEmpty(value, raw)
 * @param value - the parsed value for the field
 * @param raw - the raw value for this field
 * @returns {Boolean}
 * @api public
 */

Field.prototype.isEmpty = function (value, raw) {
    if (raw === undefined) {
        if (typeof value === 'number' && isNaN(value)) {
            return true;
        }
        raw = value;
    }
    return (raw === '' || raw === null || raw === undefined);
};

/**
 * Run the field's validation functions against a value. Returns an
 * array of validation errors, or an empty array if valid.
 *
 * @name Field.validate(doc, value, raw)
 * @param {Object} doc
 * @param value
 * @param raw
 * @returns {Array}
 * @api public
 */

Field.prototype.validate = function (doc, value, raw) {
    // don't validate empty fields, but check if required
    if (this.isEmpty(value, raw)) {
        if (this.required) {
            if(_.isFunction(this.required)) {
                if(this.required(doc, value, raw)) {
                    return [ new Error('Required field') ];
                }
            } else {
                return [ new Error('Required field') ];
            }
        }
        return [];
    }
    return _.reduce(this.validators, function (errs, v) {
        try {
            // check that v is actually a function, since IE likes to
            // insert nulls here for some reason
            if (v) {
                errs = errs.concat(v(doc, value, raw) || []);
            }
        }
        catch (e) {
            errs.push(e);
        }
        return errs;
    }, []);
};

/**
 * Check relevant field permissions to see if user is authorised to make
 * changes. Returns an array of permissions errors, or an empty array if the
 * changes are permissible.
 *
 * @name Field.authorize(newDoc, oldDoc, newVal, oldVal, userCtx)
 * @param {Object} newDoc
 * @param {Object} oldDoc
 * @param newVal
 * @param oldVal
 * @param {Object} userCtx
 * @returns {Array}
 * @api public
 */

Field.prototype.authorize = function (newDoc, oldDoc, newVal, oldVal, userCtx) {
    var perms = this.permissions;
    var errors = [];
    if (_.isFunction(perms)) {
        errors = errors.concat(
            utils.getErrors(perms, arguments)
        );
    }
    // on update
    var fn;
    // on add
    if (newDoc && !oldDoc) {
        fn = perms.add;
    }
    // on remove
    else if (!newDoc || newDoc._deleted) {
        fn = perms.remove;
    }
    // on update
    else if (newVal !== oldVal) {
        fn = perms.update;
    }
    if (fn) {
        errors = errors.concat(
            utils.getErrors(fn, arguments)
        );
    }
    return errors;
};


/**
 * Embedded objects represent a Type embedded within another Type or set of
 * Fields. Its not a true field, but like the Field constructor it acts as a
 * marker when walking through the sub-objects that make up a schema.
 *
 * Exposes the same methods as Field objects.
 *
 * #### Options ####
 *
 * * **type**        *Type Object*  - Required, the Type definition to embed
 * * **omit_empty**  *Boolean* - whether to omit the field from a document when
 *                               the field is empty
 * * **permissions** *Object*  - a permissions check function or an object
 *                               containing separate functions to run on create,
 *                               edit and update operations.
 * * **validators**  *Array*   - an array of validation functions (default: [])
 * * **required**    *Boolean* - whether the field is required (default: true)
 *
 * @name Embedded
 * @param {Object} options
 * @constructor
 * @api public
 */

var Embedded = exports.Embedded = function Embedded(options) {
    options = options || {};
    var type = options.type;
    if (!type) {
        throw new Error('No type specified');
    }
    if(options.permissions) {
        type.permissions = options.permissions;
    }
    _.extend(this, _.defaults(options, {
        required: true
    }));
    this.type = type;
};

/**
 * Test values to see if field is considered empty.
 *
 * This function accepts the raw value even though by default it only
 * checks the parsed value, so that other field types overridding this method
 * have the raw data available.
 *
 * @name Embedded.isEmpty(value, raw)
 * @param value - the parsed value for the field
 * @param raw - the raw value for this field
 * @api public
 */

Embedded.prototype.isEmpty = function (value, raw) {
    return (value === '' || value === null || value === undefined);
};

/**
 * Run the type's validate function against a value. Returns an
 * array of validation errors, or an empty array if valid.
 *
 * @name Embedded.validate(doc, value, raw)
 * @param {Object} doc
 * @param value
 * @param raw
 * @returns {Array}
 * @api public
 */

Embedded.prototype.validate = function (doc, value, raw) {
    // don't validate empty fields, but check if required
    if (this.isEmpty(value, raw)) {
        if (this.required) {
            return [ new Error('Required field') ];
        }
        return [];
    }
    if (!value._id) {
        return [new Error('Embedded document missing _id')];
    }
    return this.type.validate(value, raw);
};

/**
 * Check relevant type permissions to see if user is authorised to make
 * changes. Returns an array of permissions errors, or an empty array if the
 * changes are permissible.
 *
 * @name Embedded.authorize(newDoc, oldDoc, newVal, oldVal, user)
 * @param {Object} newDoc
 * @param {Object} oldDoc
 * @param newVal
 * @param oldVal
 * @param {Object} user
 * @returns {Array}
 * @api public
 */

Embedded.prototype.authorize = function (newDoc, oldDoc, newVal, oldVal, user) {
    if (newVal && oldVal && newVal._id !== oldVal._id) {
        oldVal = undefined;
    }
    return this.type.authorize(newVal || {_deleted: true}, oldVal, user);
};

/**
 * EmbeddedList objects represent multiple instances of a Type embedded within
 * another Type or set of Fields. Its not a true field, but like the Field
 * constructor it acts as a marker when walking through the sub-objects that
 * make up a schema.
 *
 * Exposes the same methods as Field objects.
 *
 * #### Options ####
 *
 * * **type**        *Type Object*  - Required, the Type definition to embed
 * * **omit_empty**  *Boolean* - whether to omit the field from a document when
 *                               the field is empty
 * * **permissions** *Object*  - a permissions check function or an object
 *                               containing separate functions to run on create,
 *                               edit and update operations.
 * * **validators**  *Array*   - an array of validation functions (default: [])
 * * **required**    *Boolean* - whether the field is required (default: true)
 *
 * @name EmbeddedList
 * @param {Object} options
 * @constructor
 * @api public
 */

var EmbeddedList = exports.EmbeddedList = function EmbeddedList(options) {
    options = options || {};
    var type = options.type;
    if (!type) {
        throw new Error('No type specified');
    }
    options.permissions = _.defaults((options.permissions || {}), {
        add: permissions.inherit(type),
        remove: permissions.inherit(type),
        update: permissions.inherit(type)
    });
    _.extend(this, _.defaults(options, {
        required: true
    }));
    this.type = type;
};

/**
 * Test values to see if field is considered empty.
 *
 * This function accepts the raw value even though by default it only
 * checks the parsed value, so that other field types overridding this method
 * have the raw data available.
 *
 * @name EmbeddedList.isEmpty(value, raw)
 * @param value - the parsed value for the field
 * @param raw - the raw value for this field
 * @api public
 */

EmbeddedList.prototype.isEmpty = function (value, raw) {
    return (value === '' || value === null || value === undefined);
};

/**
 * Detects embedded documents with missing _id properties and returns an
 * array of Error objects for each occurence. Returns an empty array if
 * all documents have a populated _id property.
 *
 * @name EmbeddedList.missingIDs(list)
 * @param {Array} list
 * @returns {Array}
 * @api public
 */

EmbeddedList.prototype.missingIDs = function (list) {
    var errs = [];
    _.each(list, function (v, i) {
        if (!v._id) {
            var e = new Error('Embedded document missing _id');
            e.field = [i];
            errs.push(e);
        }
    });
    return errs;
};

/**
 * Detects embedded documents with duplicate _id properties and returns an
 * array of Error objects for each occurence. Returns an empty array if
 * all documents have a unique _id property.
 *
 * @name EmbeddedList.duplicateIDs(list)
 * @param {Array} list
 * @returns {Array}
 * @api public
 */

EmbeddedList.prototype.duplicateIDs = function (list) {
    var ids = {};
    var errs = [];
    _.each(list, function (v, i) {
        if (v._id in ids) {
            var e = new Error('Embedded document duplicates an existing _id');
            e.field = [i];
            errs.push(e);
        }
        ids[v._id] = true;
    });
    return errs;
};

/**
 * Checks for missing or duplicate _ids then runs the type's validate function
 * against each embedded document. Returns an array of validation errors, or
 * an empty array if valid.
 *
 * @name EmbeddedList.validate(doc, value, raw)
 * @param {Object} doc
 * @param value
 * @param raw
 * @returns {Array}
 * @api public
 */

EmbeddedList.prototype.validate = function (doc, value, raw) {
    var type = this.type;

    // don't validate empty fields, but check if required
    if (this.isEmpty(value, raw)) {
        if (this.required) {
            return [ new Error('Required field') ];
        }
        return [];
    }

    // check all values are objects
    var non_objects = _.filter(value, function (v) {

        /* Workaround for interpreter bug:
            Saving embedList() data throws an error when running in a
            CouchDB linked against js-1.8.0. We encounter a situation where
            typeof(v) === 'object', but the 'v instanceof Object' test
            incorrectly returns false. We suspect an interpreter bug.
            Please revisit this using a CouchDB linked against js-1.8.5.
            We don't currently have the infrastructure for a test case. */
        
        /* Before: return !(v instanceof Object) || _.isArray(v); */
        return (typeof(v) !== 'object' || _.isArray(v));
    });
    if (non_objects.length) {
        return _.map(non_objects, function (v) {
            return new Error(v + ' is not an object');
        });
    }

    // check for missing ids
    var missing = this.missingIDs(value);
    if (missing.length) {
        return missing;
    }

    // check for duplicate ids
    var duplicates = this.duplicateIDs(value);
    if (duplicates.length) {
        return duplicates;
    }

    // run type validation against each embedded document
    return _.reduce(value, function (errs, v, i) {
        var r = raw ? raw[i]: undefined;
        return errs.concat(
            _.map(type.validate(v, r), function (err) {
                err.field = [i].concat(err.field || []);
                return err;
            })
        );
    }, []);
};

/**
 * Check relevant type permissions to see if user is authorised to make
 * changes. Returns an array of permissions errors, or an empty array if the
 * changes are permissible.
 *
 * @name EmbeddedList.authorize(nDoc, oDoc, nVal, oVal, user)
 * @param {Object} nDoc
 * @param {Object} oDoc
 * @param nVal
 * @param oVal
 * @param {Object} user
 * @returns {Array}
 * @api public
 */

EmbeddedList.prototype.authorize = function (nDoc, oDoc, nVal, oVal, user) {
    var type = this.type;
    var perms = this.permissions;

    nVal = nVal || [];
    oVal = oVal || [];

    // a unique list of embedded ids from both the old and new document
    var ids = _.uniq(_.pluck(nVal, '_id').concat(_.pluck(oVal, '_id')));

    return _.reduce(ids, function (errs, id, i) {

        var curr_errs = [];
        var nd = _.detect(nVal, function (v) {
            return v && v._id === id;
        });
        nd = nd || {_deleted: true};
        var od = _.detect(oVal, function (v) {
            return v && v._id === id;
        });
        var args = [nDoc, oDoc, nd, od, user];

        if (_.isFunction(perms)) {
            curr_errs = utils.getErrors(perms, args);
        }
        var fn;
        // on add
        if (nd && !od) {
            fn = perms.add;
        }
        // on remove
        else if (nd._deleted) {
            fn = perms.remove;
        }
        // on update
        else if (JSON.stringify(nd) !== JSON.stringify(od)) {
            fn = perms.update;
        }
        if (fn) {
            curr_errs = curr_errs.concat(utils.getErrors(fn, args));
        }
        curr_errs = _.map(curr_errs, function (e) {
            e.field = [i].concat(e.field || []);
            return e;
        });
        return errs.concat(curr_errs);

    }, []);
};


/**
 * Prepends a validator to an array of validator functions.
 *
 * @param {Array} arr
 * @param {Function} fn
 * @api private
 */

var prependValidator = function (options, fn) {
    options = options || {};
    options.validators = [fn].concat(options.validators || []);
    return options;
};


/**
 * Creates a new string Field
 *
 * @name string([options])
 * @param {Object} options
 * @api public
 */

exports.string = function (options) {
    return new Field(_.defaults((options || {}), {
        parse: function (raw) {
            if (raw === null || raw === undefined) {
                return '';
            }
            return '' + raw;
        }
    }));
};

/**
 * Creates a new password Field
 *
 * @name password([options])
 * @param {Object} options
 * @api public
 */

exports.password = function (options) {
    return new Field(_.defaults((options || {}), {
        widget: widgets.password(),
        parse: function (raw) {
            if (raw === null || raw === undefined) {
                return '';
            }
            return '' + raw;
        }
    }));
};

/**
 * Creates a new number Field
 *
 * @name number([options])
 * @param {Object} options
 * @api public
 */

exports.number = function (options) {
    options = prependValidator(options, function (doc, value) {
        if (isNaN(value)) {
            throw new Error('Not a number');
        }
    });
    return new Field(_.defaults((options || {}), {
        parse: function (raw) {
            if (raw === '' || raw === null || raw === undefined) {
                return NaN;
            }
            return Number(raw);
        }
    }));
};


/**
 * Creates a new boolean Field
 *
 * @name boolean([options])
 * @param {Object} options
 * @api public
 */

exports.boolean = function (options) {
    return new Field(_.defaults((options || {}), {
        widget: widgets.checkbox(),
        required: false,
        parse: Boolean
    }));
};


/**
 * Creates a URL Field
 *
 * @name url([options])
 * @param {Object} options
 * @api public
 */

exports.url = function (options) {
    options = prependValidator(options, validators.url());
    return exports.string(options);
};


/**
 * Creates an email Field
 *
 * @name email([options])
 * @param {Object} options
 * @api public
 */

exports.email = function (options) {
    options = prependValidator(options, validators.email());
    return exports.string(options);
};


/**
 * Creates a creator Field
 *
 * @name creator([options])
 * @param {Object} options
 * @api public
 */

exports.creator = function (options) {
    options = options || {};
    if (!options.permissions) {
        options.permissions = {};
    }
    var p = options.permissions;
    if (p.add) {
        p.add = permissions.all([
            permissions.matchUsername(),
            p.add
        ]);
    }
    else {
        p.add = permissions.matchUsername();
    }
    if (p.update) {
        p.update = permissions.all([
            permissions.fieldUneditable(),
            p.update
        ]);
    }
    else {
        p.update = permissions.fieldUneditable();
    }
    return exports.string(_.defaults(options, {
        required: false,
        widget: widgets.creator(),
        default_value: function (req) {
            return (req.userCtx && req.userCtx.name) || '';
        }
    }));
};


/**
 * Creates a createdTime timestamp Field
 *
 * @name createdTime([options])
 * @param {Object} options
 * @api public
 */

exports.createdTime = function (options) {
    options = options || {};
    if (!options.permissions) {
        options.permissions = {};
    }
    var p = options.permissions;
    if (p.update) {
        p.update = permissions.all([
            permissions.fieldUneditable(),
            p.update
        ]);
    }
    else {
        p.update = permissions.fieldUneditable();
    }
    return exports.number(_.defaults(options, {
        widget: widgets.computed(),
        default_value: function (req) {
            return new Date().getTime();
        }
    }));
};


/**
 * Creates a choice Field
 *
 * Required option: values - an array of possible choices, each an array
 * with the first item as the value and the second as its label.
 *
 * @name choice([options])
 * @param {Object} options
 * @api public
 */

exports.choice = function (options) {
    if (!options || !options.values) {
        throw new Error('No values defined');
    }
    options = prependValidator(options, function (doc, value) {
        for (var i = 0; i < options.values.length; i++) {
            if (value === options.values[i][0]) {
                return;
            }
        }
        throw new Error('Invalid choice');
    });
    // use value as label if no label defined
    options.values = _.map(options.values, function (v) {
        return _.isArray(v) ? v: [v, v];
    });
    return new Field(_.defaults(options, {
        widget: widgets.select({values: options.values})
    }));
};


/**
 * Creates a number choice Field
 *
 * @name numberChoice([options])
 * @param {Object} options
 * @api public
 */

exports.numberChoice = function (options) {
    options = options || {};
    prependValidator(options, function (doc, value) {
        if (isNaN(value)) {
            throw new Error('Not a number');
        }
    });
    return exports.choice(_.defaults(options, {
        parse: function (raw) {
            if (raw === null || raw === '') {
                return '';
            }
            return Number(raw);
        }
    }));
};


/**
 * Creates an Embedded Field
 *
 * Required option: type - the Type definition to embed
 *
 * @name embed([options])
 * @param {Object} options
 * @api public
 */

exports.embed = function (options) {
    return new Embedded(_.defaults((options || {}), {
        widget: widgets.embedList({
            singleton: true,
            widget: widgets.defaultEmbedded()
        })
    }));
};


/**
 * Creates an EmbeddedList Field
 *
 * Required option: type - the Type definition to embed
 *
 * @name embedList([options])
 * @param {Object} options
 * @api public
 */

exports.embedList = function (options) {
    return new EmbeddedList(_.defaults((options || {}), {
        widget: widgets.embedList({
            singleton: false,
            widget: widgets.defaultEmbedded()
        })
    }));
};


/**
 * Creates a array Field. The default parse function expects a single row of
 * comma separated values.
 *
 * To accept an array of values other than strings, add a function to options
 * called parseEach which accepts the string value for each item and performs
 * the transformation.
 *
 * @name array([options])
 * @param {Object} options
 * @api public
 */

exports.array = function (options) {
    options = options || {};
    options.hint = options.hint || "Values should be comma separated";
    prependValidator(options, function (doc, value) {
        if (!_.isArray(value)) {
            throw new Error('Not an array');
        }
    });
    return exports.string(_.defaults(options, {
        parse: function (raw) {
            var result = utils.parseCSV(raw || '')[0] || [];
            if (options.parseEach) {
                result = _.map(result, options.parseEach);
            }
            return result;
        }
    }));
};

/**
 * Creates a number array Field, same as the array field only each value is
 * parsed as a number instead of a string.
 *
 * @name numberArray([options])
 * @param {Object} options
 * @api public
 */

exports.numberArray = function (options) {
    options = options || {};
    options.parseEach = options.parseEach || function (v) {
        return Number(v);
    };
    prependValidator(options, function (doc, value) {
        for (var i = 0, len = value.length; i < len; i++) {
            if (isNaN(value[i])) {
                throw new Error('Not a number');
            }
        }
    });
    return exports.array(options);
};


/**
 * AttachmentField objects are used when constructing content types and forms,
 * and are handled slightly differently than Field objects when parsing requests
 * and validating.
 *
 * AttachmentField 'inherits' from Field (so an AttachmentField object will
 * return true for instanceof Field and instanceof AttachmentField).
 *
 * #### Options ####
 *
 * * **omit_empty**  *Boolean* - whether to omit the field from a document when
 *                               the field is empty
 * * **permissions** *Object*  - a permissions check function or an object
 *                               containing separate functions to run on create,
 *                               edit and update operations.
 * * **validators**  *Array*   - an array of validation functions (default: [])
 * * **required**    *Boolean* - whether the field is required (default: true)
 *
 * @constructor
 * @name Field
 * @param {Object} options
 * @api public
 */

exports.AttachmentField = function AttachmentField(options) {
    exports.Field.call(this, options);
};
exports.AttachmentField.prototype = new exports.Field();


/**
 * Creates a file attachment field.
 *
 * @name attachment([options])
 * @param {Object} options
 * @api public
 */

exports.attachments = function (options) {
    options = options || {};
    options.widget = options.widget || widgets.file();
    return new exports.AttachmentField(options);
};


})};

/********** couchtypes/fieldset **********/

kanso.moduleCache["couchtypes/fieldset"] = {load: (function (module, exports, require) {

/**
 * Functions for dealing with collections of fields. Used by both the
 * types and forms modules.
 *
 * @module
 */

/**
 * Module dependencies
 */

var _ = require('underscore')._,
    utils = require('couchtypes/utils');


/**
 * Returns a hierachy of default values for a given set of Field objects
 *
 * @name createDefaults(fields, req)
 * @param {Object} fields
 * @param {Object} req - the request object to pass to default_value functions
 * @returns {Object}
 * @api public
 */

exports.createDefaults = function (fields, req, doc, path) {
    path = path || [];
    var fields_module = require('./fields');
    return _.reduce(_.keys(fields), function (result, k) {
        var f = fields[k];
        if (f instanceof fields_module.AttachmentField) {
            if (f.hasOwnProperty('default_value')) {
                var val,
                    dir = path.concat([k]).join('/');
                if (_.isFunction(f.default_value)) {
                    val = f.default_value(req);
                }
                else {
                    val = f.default_value;
                }
                var d = doc || result;
                if (!d._attachments) {
                    d._attachments = {};
                }
                for (var ak in val) {
                    d._attachments[dir + '/' + ak] = val[ak];
                }
            }
        }
        else if (f instanceof fields_module.Field ||
                 f instanceof fields_module.Embedded ||
                 f instanceof fields_module.EmbeddedList) {

            if (f.hasOwnProperty('default_value')) {
                if (_.isFunction(f.default_value)) {
                    result[k] = f.default_value(req);
                }
                else {
                    result[k] = f.default_value;
                }
            }
        }
        else if (f instanceof Object) {
            result[k] = exports.createDefaults(
                f, req, doc || result, path.concat([k])
            );
        } else {
            throw new Error(
                'The field type `' + (typeof f) + '` is not supported.'
            );
        }
        return result;
    }, {});
};

/**
 * Validate a specific field, returning all validation errors as an array with
 * each error's field property prefixed by the current path.
 *
 * @name validateField(field, doc, value, raw, path)
 * @param {Field} field
 * @param {Object} doc
 * @param value
 * @param raw
 * @param {Array} path
 * @returns {Array}
 * @api public
 */

exports.validateField = function (field, doc, value, raw, path) {
    return _.map(field.validate(doc, value, raw), function (err) {
        err.field = path.concat(err.field || []);
        err.has_field = true;
        return err;
    });
};

/**
 * Validates an object containing fields or other sub-objects, iterating over
 * each property and recursing through sub-objects to find all Fields.
 *
 * Returns an array of validation errors, each with a field property set to the
 * path of the field which caused the error.
 *
 * @name validate(fields, doc, values, raw, path, extra)
 * @param {Object} fields
 * @param {Object} doc
 * @param {Object} values
 * @param {Object} raw
 * @param {Array} path
 * @param {Boolean} extra - whether to allow extra values not covered by a field
 * @returns {Array}
 * @api public
 */

exports.validate = function (fields, doc, values, raw, path, extra) {
    values = values || {};
    fields = fields || {};
    raw = raw || {};

    // Expecting sub-object, not a value
    if (typeof values !== 'object') {
        var e = new Error('Unexpected property - validation 1');
        e.field = path;
        e.has_field = false;
        return [e];
    }

    // Ensure we walk through all paths of both fields and values by combining
    // the keys of both. Otherwise, we might miss out checking for missing
    // required fields, or may not detect the presence of extra fields.

    var keys = _.uniq(_.keys(fields).concat(_.keys(values)));
    var fields_module = require('./fields');

    return _.reduce(keys, function (errs, k) {
        var f = fields[k];
        if (f === undefined) {
            // Extra value with no associated field detected
            if (!extra) {
                // ignore system properties
                if (!(path.length === 0 && k.charAt(0) === '_')) {
                    var e = new Error('Unexpected property - validation 2');
                    e.field = path.concat([k]);
                    e.has_field = false;
                    errs.push(e);
                }
            }
            return errs;
        }
        var val = values[k];
        var fn = exports.validate;
        if (f instanceof fields_module.Field ||
            f instanceof fields_module.Embedded ||
            f instanceof fields_module.EmbeddedList) {
            fn = exports.validateField;
        }
        if (f instanceof fields_module.AttachmentField) {
            val = utils.attachmentsBelowPath(doc, path.concat([k]));
        }
        return errs.concat(
            fn.call(this, f, doc, val, raw[k], path.concat([k]), extra)
        );
    }, []);
};

/**
 * Authorize a specific field, returning all permissions errors as an array with
 * each error's field property prefixed by the current path.
 *
 * @name authField(f, nDoc, oDoc, nVal, oVal, user, path)
 * @param {Field} f     - field object
 * @param {Object} nDoc - new document
 * @param {Object} oDoc - old document
 * @param nVal          - new field value
 * @param oVal          - old field value
 * @param {Object} user - user context object
 * @param {Array} path  - current path
 * @returns {Array}
 * @api public
 */

exports.authField = function (f, nDoc, oDoc, nVal, oVal, user, path) {
    //log('authField: ' + path.join('.'));
    return _.map(f.authorize(nDoc, oDoc, nVal, oVal, user), function (err) {
        err.field = path.concat(err.field || []);
        err.has_field = true;
        return err;
    });
};

/**
 * Authorizes an object containing fields or other sub-objects, iterating over
 * each property and recursing through sub-objects to find all Fields.
 *
 * Returns an array of permissions errors, each with a field property set to the
 * path of the field which caused the error.
 *
 * @name authFieldSet(f, nDoc, oDoc, nVal, oVal, user, path)
 * @param {Field} f     - field object
 * @param {Object} nDoc - new document
 * @param {Object} oDoc - old document
 * @param nVal          - new field value
 * @param oVal          - old field value
 * @param {Object} user - user context object
 * @param {Array} path  - current path
 * @param {Boolean} extra - whether to raise an error on additional fields
 * @returns {Array}
 * @api public
 */

exports.authFieldSet = function (f, nDoc, oDoc, nVal, oVal, user, path, extra) {
    //log('authFieldSet: ' + path.join('.'));
    nVal = nVal || {};
    oVal = oVal || {};
    f = f || {};

    // Expecting sub-object, not a value
    // This *should* be picked up by validation, and be raised as a validation
    // error before it gets to the auth stage
    if (typeof nVal !== 'object') {
        var e = new Error('Unexpected property 1');
        e.field = path;
        e.has_field = false;
        return [e];
    }

    // Ensure we walk through all paths of both fields and values by combining
    // the keys of both. Otherwise, we might miss out checking for missing
    // required fields, or may not detect the presence of extra fields.

    var fKeys = _.keys(f);
    var newKeys = _.keys(nVal);
    var oldKeys = _.keys(oVal);
    var keys = _.uniq(fKeys.concat(newKeys).concat(oldKeys));

    var fields_module = require('./fields');

    return _.reduce(keys, function (errs, k) {
        var field = f[k];
        if (field === undefined) {
            // Extra value with no associated field detected
            // This *should* be picked up by validation, and be raised as a
            // validation error before it gets to the auth stage
            if (!extra) {
                // ignore system properties
                if (!(path.length === 0 && k.charAt(0) === '_')) {
                    var e = new Error('Unexpected property 2');
                    e.field = path.concat([k]);
                    e.has_field = false;
                    errs.push(e);
                }
            }
            return errs;
        }
        var fn = exports.authFieldSet;
        var nv = nVal[k];
        var ov = oVal[k];
        if (field instanceof fields_module.Field ||
            field instanceof fields_module.Embedded ||
            field instanceof fields_module.EmbeddedList) {
            fn = exports.authField;
        }
        if (field instanceof fields_module.AttachmentField) {
            nv = utils.attachmentsBelowPath(nDoc, path.concat([k]));
            ov = utils.attachmentsBelowPath(oDoc, path.concat([k]));
        }
        return errs.concat(fn(
            field, nDoc, oDoc, nv, ov, user, path.concat([k]), extra
        ));
    }, []);
};



})};

/********** couchtypes/forms **********/

kanso.moduleCache["couchtypes/forms"] = {load: (function (module, exports, require) {

/**
 * Functions for the rendering, parsing and validation of forms.
 *
 * @module
 */

/**
 * Module dependencies
 */

var utils = require('couchtypes/utils'),
    fieldset = require('couchtypes/fieldset'),
    render = require('./render'),
    _ = require('underscore')._;


/**
 * Form object, presents fields and parses responses.
 *
 * #### Options ####
 *
 * <table class="options">
 *   <tr>
 *      <td class="name">exclude</td>
 *      <td class="type">Array</td>
 *      <td class="description">a list of field names to exclude</td>
 *   </tr>
 *   <tr>
 *      <td class="name">fields</td>
 *      <td class="type">Array</td>
 *      <td class="description">
 *          a subset of fields to use (inverse of excluded)
 *      </td>
 *   </tr>
 * </table>
 *
 * @name Form(fields | type, [doc])
 * @param {Object} fields  - an object literal containing fields or a Type
 * @param {Object} doc     - (optional) the original document being edited
 * @param {Object} options - (optional) see available options above
 * @constructor
 * @api public
 */

var Form = exports.Form = function Form(fields, doc, options) {
    this.options = options || {};

    this.values = null;
    if (doc) {
        this.values = JSON.parse(JSON.stringify(doc)); /* Copy */
        this.initial_doc = doc;
    }
    if (fields && fields.fields) {
        this.type = fields;
        this.fields = this.type.fields;
    }
    else {
        this.fields = fields;
    }
    /*if (utils.constructorName(fields) === 'Type') {
        this.type = fields;
        this.fields = this.type.field;
    }
    else {
        this.fields = fields;
    }*/
};


/**
 * Overrides values in doc_a with values in doc_b, only when a field is present
 * for that value. This means properties not in fields (or in excluded fields)
 * are retained, while properties which are covered by the fieldset are
 * replaced.
 *
 * This is used when updating the form's values with a request when its been
 * initiated with a previous document. You shouldn't normally need to call this
 * directly.
 *
 * Returns the updated doc_a object.
 *
 * @name override(excludes, field_subset, fields, doc_a, doc_b, path)
 * @param {Array | null} excludes
 * @param {Array | null} field_subset
 * @param {Object} Fields
 * @param {Object} doc_a
 * @param {Object} doc_b
 * @param {Array} path
 * @returns {Object}
 * @api public
 */

exports.override = function (excludes, field_subset, fields, doc_a, doc_b, path) {
    fields = fields || {};
    doc_a = doc_a || {};

    var fields_module = require('./fields');
    var exclude_paths = _.map((excludes || []), function (p) {
        return p.split('.');
    });
    var subset_paths = _.map((field_subset || []), function (p) {
        return p.split('.');
    });

    var keys = _.keys(doc_b);

    _.each(keys, function (k) {
        if (path.length === 0 && k === '_attachments') {
            return;
        }
        var f = fields[k];
        var b = doc_b[k];
        var f_path = path.concat([k]);

        if (typeof b !== 'object' ||
            f instanceof fields_module.Field ||
            f instanceof fields_module.Embedded ||
            f instanceof fields_module.EmbeddedList) {

            if (excludes) {
                for (var i = 0; i < exclude_paths.length; i++) {
                    if (utils.isSubPath(exclude_paths[i], f_path)) {
                        return;
                    }
                }
            }
            if (field_subset) {
                var in_subset = false;
                for (var j = 0; j < subset_paths.length; j++) {
                    if (utils.isSubPath(subset_paths[j], f_path)) {
                        in_subset = true;
                    }
                }
                if (!in_subset) {
                    return;
                }
            }
            doc_a[k] = b;
        }
        else {
            doc_a[k] = exports.override(
                excludes, field_subset, fields[k], doc_a[k], b, f_path
            );
        }
    });
    if (path.length === 0) {
        return exports.overrideAttachments(
            excludes, field_subset, fields, doc_a, doc_b
        );
    }
    return doc_a;
};

exports.overrideAttachments = function (excludes, field_subset, fields, doc_a, doc_b) {
    var exclude_paths = _.map((excludes || []), function (p) {
        return p.split('.');
    });
    var subset_paths = _.map((field_subset || []), function (p) {
        return p.split('.');
    });

    var a = doc_a._attachments || {};
    var b = doc_b._attachments || {};

    var a_keys = _.keys(a);
    var b_keys = _.keys(b);
    var all_keys = _.uniq(a_keys.concat(b_keys).sort(), true);

    var fields_module = require('./fields');

    _.each(all_keys, function (k) {
        var parts = k.split('/');
        var filename = parts.pop();
        var dir = parts.join('/');
        var f = utils.getPropertyPath(fields, dir);


        if (f instanceof fields_module.AttachmentField) {
            if (excludes) {
                for (var i = 0; i < exclude_paths.length; i++) {
                    if (utils.isSubPath(exclude_paths[i], dir.split('/'))) {
                        return;
                    }
                }
            }
            if (field_subset) {
                var in_subset = false;
                for (var j = 0; j < subset_paths.length; j++) {
                    if (utils.isSubPath(subset_paths[j], dir.split('/'))) {
                        in_subset = true;
                    }
                }
                if (!in_subset) {
                    return;
                }
            }
            // clear existing attachments
            for (var ak in a) {
                if (ak.slice(0, dir.length + 1) === dir + '/') {
                    delete a[ak];
                }
            }
            // copy over new attachments
            for (var bk in b) {
                if (bk.slice(0, dir.length + 1) === dir + '/') {
                    if (!doc_a._attachments) {
                        a = doc_a._attachments = {};
                    }
                    a[bk] = b[bk];
                }
            }
        }
        else if (b.hasOwnProperty(k)) {
            if (!doc_a._attachments) {
                a = doc_a._attachments = {};
            }
            a[k] = b[k];
        }
    });

    return doc_a;
};

/**
 * Parses a request and validates the result, binding values and errors to
 * the form instance.
 *
 * @name Form.validate(req)
 * @param {Object} req
 * @returns {Form}
 * @api public
 */

Form.prototype.validate = function (req) {
    /* This is the request payload:
        This contains all of the form fields that are used by
        formValuesToTree and parseRaw, and must be copied first. */

    this.raw = (req.form || {});

    var type_class = require('./types').Type;
    var tree = exports.formValuesToTree(this.raw);

    this.values = exports.override(
        this.options.exclude,
        this.options.fields,
        this.fields,
        this.values || fieldset.createDefaults(this.fields, req) || {},
        exports.parseRaw(this.fields, tree),
        []
    );

    this.errors = fieldset.validate(
        this.fields, this.values, this.values, this.raw, [], false
    );

    if (this.type) {
        if (this.type instanceof type_class) {
            // run top level permissions first
            var type_errs = this.type.authorizeTypeLevel(
                this.values, this.initial_doc, req.userCtx
            );
            if (type_errs.length) {
                this.errors = this.errors.concat(type_errs);
            }
            else {
                // if no top-level permissions errors, check each field
                this.errors = this.errors.concat(
                    this.type.authorize(
                        this.values, this.initial_doc, req.userCtx
                    )
                );
            }
        } else {
            /* Programmer error: display a useful diagnostic message */
            throw new Error(
                'Encountered a type object that is not an instance of' +
                    ' `Type`; check lib/types.js for proper instansiation'
            );
        }

    }
    else {
        this.errors = this.errors.concat(fieldset.authFieldSet(
            this.fields, this.values, this.initial_doc, this.values,
            this.initial_doc, req.userCtx, [], true
        ));
    }

    // clear field properties on errors for excluded fields
    var excludes = this.options.exclude;
    if (excludes) {
        var excl_paths = _.map(excludes, function (p) {
            return p.split('.');
        });
        this.errors = _.map(this.errors, function (e) {
            if (!e.field) {
                return e;
            }
            for (var i = 0, len = excl_paths.length; i < len; i++) {
                var path = excl_paths[i];
                if (utils.isSubPath(path, e.field)) {
                    e.message = e.field.join('.') + ': ' + (
                        e.message || e.toString()
                    );
                    delete e.field;
                    return e;
                }
            }
            return e;
        });
    }

    // clear field properties on errors not in fields subset
    var field_subset = this.options.fields;
    if (field_subset) {
        var subset_paths = _.map(field_subset, function (p) {
            return p.split('.');
        });
        this.errors = _.map(this.errors, function (e) {
            if (!e.field) {
                return e;
            }
            for (var i = 0, len = subset_paths.length; i < len; i++) {
                var path = subset_paths[i];
                if (!utils.isSubPath(path, e.field)) {
                    e.message = e.field.join('.') + ': ' + (
                        e.message || e.toString()
                    );
                    delete e.field;
                    return e;
                }
            }
            return e;
        });
    }

    return this;
};

/**
 * After a form has called validate, this function will return true if the form
 * is valid, false otherwise.
 *
 * @name Form.isValid()
 * @returns {Boolean}
 * @api public
 */

Form.prototype.isValid = function () {
    return !(this.errors && this.errors.length);
};

/**
 * Filters an array of errors, returning only those below a specific field path
 *
 * @param {Array} errs
 * @param {Array} path
 * @returns {Array}
 */

var errsBelowPath = function (errs, path) {
    if (!path || !path.length) {
        return errs;
    }
    return _.filter(errs, function (e) {
        if (!e.field) {
            return false;
        }
        return utils.isSubPath(path, e.field);
    });
};

/**
 * Filters a list of errors, returning only those without a field property.
 * This is used to populate the errors at the top of the form, which apply
 * generally, or cannot be attributed to a single field.
 *
 * @param {Array} errs
 * @returns {Array}
 */

var errsWithoutFields = function (errs) {
    return _.filter(errs, function (e) {
        return !e.field;
    });
};

/**
 * Converts current form to a HTML string, using an optional renderer class.
 *
 * @name Form.toHTML(req, [RendererClass])
 * @param {Object} req Request object; null for most recent. (optional)
 * @param {Renderer} RendererClass (optional)
 * @param {Object} options An object containing widget options, which
 *          will ultimately be provided to each widget's toHTML method.
 * @param {Boolean} create_defaults (optional) Set this to true if you've
 *          provided a document in {doc}, but would still like default
 *          values to be merged in to it via createDefaults. For a field f,
 *          the default value is added to {doc} if and only if doc[f]
 *          is undefined, null, or not present. Defaults to off.
 * @returns {String}
 * @returns {String}
 * @api public
 */

Form.prototype.toHTML = function (req,
                                  /* optional */ RendererClass,
                                  /* optional */ options,
                                  /* optional */ create_defaults) {
    var values = this.values;

    options = options || {};
    options.operation = options.operation || (values ? 'update': 'add');

    if (create_defaults) {
        values = _.defaults(
            values, fieldset.createDefaults(this.fields, req)
        );
    } else if (!values) {
        values = fieldset.createDefaults(this.fields, req);
    }

    RendererClass = (RendererClass || render.defaultRenderer());
    var renderer = new RendererClass();
    return (
        renderer.start(
            errsWithoutFields(this.errors)
        ) +
        this.renderFields(
            renderer, this.fields, values, this.raw, this.errors, [], options
        ) +
        renderer.end() +
        render.scriptTagForEvent('renderFinish')
    );
};

/**
 * Filters an array of errors, returning only those below a specific field path
 *
 * @param {Array} errs
 * @param {Array} path
 * @returns {Array}
 */

var errsBelowPath = function (errs, path) {
    return _.filter(errs, function (e) {
        for (var i = 0, len = path.length; i < len; ++i) {
            if (!e.field || path[i] !== e.field[i]) {
                return false;
            }
        }
        return true;
    });
};

/**
 * Iterates over fields and sub-objects calling the correct renderer function on
 * each. Returns a HTML representation of the fields. Used internally by the
 * toHTML method, you should not need to call this function directly.
 *
 * @name Form.renderFields(renderer, fields, values, raw, err, path)
 * @param {Object} renderer
 * @param {Object} fields
 * @param {Object} values
 * @param {Array} errs
 * @param {Array} path
 * @param {Object} options An object containing widget options, which
 *          will ultimately be provided to each widget's toHTML method.
 * @returns {String}
 * @api public
 */

Form.prototype.renderFields = function (renderer, fields, values,
                                        raw, errs, path, options, root) {
    fields = fields || {};
    values = values || {};
    root = root || values;
    raw = raw || {};
    errs = errs || [];
    path = path || [];

    var that = this;
    var excludes = this.options.exclude;
    var field_subset = this.options.fields;
    var keys = _.keys(fields);

    var fields_module = require('./fields');

    return _.reduce(keys, function (html, k) {

        var f_path = path.concat([k]);

        if (excludes) {
            if (_.indexOf(excludes, f_path.join('.')) !== -1) {
                return html;
            }
        }
        if (field_subset) {
            if (_.indexOf(field_subset, f_path.join('.')) === -1) {
                return html;
            }
        }

        var f_errs = errsBelowPath(errs, f_path);
        var f = fields[k];

        if (f instanceof fields_module.AttachmentField) {
            return html + renderer.field(
                f,
                f_path,
                utils.attachmentsBelowPath(root, f_path),
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs,
                (options || {})
            );
        }
        else if (f instanceof fields_module.Field ||
                 f instanceof fields_module.Embedded ||
                 f instanceof fields_module.EmbeddedList) {

            return html + renderer.field(
                f,
                f_path,
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs,
                (options || {})
            );
        }
        else if (f instanceof Object) {
            return html + (k ? renderer.beginGroup(f_path) : '') +
                that.renderFields(
                    renderer,
                    f,
                    values[k],
                    (raw[k] === undefined) ? values[k]: raw[k],
                    errs,
                    f_path,
                    (options || {}),
                    root
                ) + (k ? renderer.endGroup(f_path) : '');
        } else {
            throw new Error(
                'The field type `' + (typeof f) + '` is not supported.'
            );
        }
    }, '');
};


/**
 * Transforms a flat object from a request query to a proper
 * hierarchy of properties.
 *
 * <pre>{'one.two': 'val'} --> {one: {two: 'val'}}</pre>
 *
 * @name formValuesToTree(form)
 * @param {Object} query
 * @api public
 */

exports.formValuesToTree = function (form) {
    var tree = {};
    for (var k in form) {
        utils.setPropertyPath(tree, k.split('.'), form[k]);
    }
    return tree;
};


/**
 * Transforms a raw query object from formValuesToTree to a
 * document which follows the schema for the given type.
 *
 * @name parseRaw(fields, raw)
 * @param {Object} fields
 * @param {Object} raw
 * @returns {Object}
 * @api public
 */

exports.parseRaw = function (fields, raw, root, path) {
    var doc = {};
    path = path || [];
    root = root || doc;
    raw = raw || {};
    var fields_module = require('./fields');

    for (var k in fields) {
        var f = fields[k];
        var f_path = path.concat([k]);
        var r = raw[k];

        if (f instanceof fields_module.AttachmentField) {
            if (typeof r === 'string' && r !== '') {
                r = JSON.parse(r);
            }
            else {
                r = {};
            }
            var att = {};
            for (var rk in r) {
                att[f_path.join('/') + '/' + rk] = r[rk];
            }
            if (!root._attachments) {
                root._attachments = {};
            }
            _.extend(root._attachments, att);
        }
        else if (f instanceof fields_module.Field) {
            if (!f.isEmpty(r)) {
                doc[k] = f.parse(r);
            }
            else if (!f.omit_empty) {
                doc[k] = undefined;
            }
        }
        else if (f instanceof fields_module.Embedded) {
            if (!f.isEmpty(r)) {
                if (typeof r === 'string') {
                    if (r !== '') {
                        r = JSON.parse(r);
                    } else {
                        r = {};
                    }
                }
                doc[k] = exports.parseRaw(f.type.fields, r, root, f_path);
            }
        }
        else if (f instanceof fields_module.EmbeddedList) {
            doc[k] = [];
            if (!f.isEmpty(r)) {
                for (var i in r) {
                    if (typeof r[i] === 'string') {
                        if (r[i] !== '') {
                            r[i] = JSON.parse(r[i]);
                        } else {
                            r[i] = {};
                        }
                    }
                    doc[k][i] = exports.parseRaw(
                        f.type.fields, r[i], root, f_path.concat([i])
                    );
                }
            }
            if (!doc[k].length && f.omit_empty) {
                delete doc[k];
            }
        }
        else if (f instanceof Object) {
            doc[k] = exports.parseRaw(f, r, root, f_path);
        } else {
            throw new Error(
                'The field type `' + (typeof f) + '` is not supported.'
            );
        }
    }
    return doc;
};



})};

/********** couchtypes/permissions **********/

kanso.moduleCache["couchtypes/permissions"] = {load: (function (module, exports, require) {

/**
 * Permissions functions are used on both Fields and Types to check a given
 * user is authorized to make a change to a document.
 *
 * @module
 */


/**
 * Module dependencies
 */

var utils = require('couchtypes/utils'),
    _ = require('underscore')._;


/**
 * Field's new value should match current user's name
 *
 * @name matchUsername()
 * @returns {Function}
 * @api public
 */

exports.matchUsername = function () {
    return function (newDoc, oldDoc, newVal, oldVal, userCtx) {
        var name = userCtx.name;
        if (name !== newVal) {
            // if both are empty-like, then consider them the same
            if ((name !== null && name !== undefined && name !== '') ||
                (newVal !== null && newVal !== undefined && newVal !== '')) {
                throw new Error('Field does not match your username');
            }
        }
    };
};

/**
 * Checks if the user has a specific role
 *
 * @name hasRole(role)
 * @param {String} role
 * @returns {Function}
 * @api public
 */

exports.hasRole = function (role) {
    return function (newDoc, oldDoc, newVal, oldVal, userCtx) {
        var roles = userCtx ? (userCtx.roles || []): [];
        if (!_.include(roles, role)) {
            throw new Error('User must have "' + role + '" role.');
        }
    };
};

/**
 * Checks if the user has one of the given roles
 *
 * @name hasAnyOfTheRoles(roles)
 * @param {Array} roles
 * @returns {Function}
 * @api public
 */

exports.hasAnyOfTheRoles = function (expectedRoles) {
    return function (newDoc, oldDoc, newVal, oldVal, userCtx) {
        var actualRoles = userCtx ? (userCtx.roles || []): [];
        if (_.intersect(expectedRoles, actualRoles).length === 0) {
            throw new Error('You must have the appropriate roles.');
        }
    };
};


/**
 * The value of this field should never change after the document has been
 * created.
 *
 * @name fieldUneditable()
 * @returns {Function}
 * @api public
 */

exports.fieldUneditable = function () {
    return function (newDoc, oldDoc, newValue, oldValue, userCtx) {
        if (oldDoc) {
            if (newValue !== oldValue) {
                throw new Error('Field cannot be edited once created');
            }
        }
    };
};

/**
 * User's name should match the *old* value of the given field. A field can be
 * specified using a string or an array of strings (like a path).
 *
 * <pre>
 * eg: usernameMatchesField('creator')
 *     usernameMatchesField(['meta','creator'])
 *
 *     {
 *         creator: 'name',
 *         meta: {creator: 'name2'}
 *     }
 * </pre>
 *
 * @name usernameMatchesField(path)
 * @param {String | Array} path
 * @returns {Function}
 * @api public
 */

exports.usernameMatchesField = function (path) {
    if (!_.isArray(path)) {
        path = [path];
    }
    return function (newDoc, oldDoc, newValue, oldValue, userCtx) {
        var field = utils.getPropertyPath(oldDoc, path);
        if (userCtx.name !== field) {
            throw new Error('Username does not match field ' + path.join('.'));
        }
    };
};

/**
 * Checks that user's context has a username
 *
 * @name loggedIn()
 * @returns {Function}
 * @api public
 */

exports.loggedIn = function () {
    return function (newDoc, oldDoc, newValue, oldValue, userCtx) {
        if (!userCtx || !userCtx.name) {
            throw new Error('You must be logged in');
        }
    };
};

/**
 * Runs an array of permissions functions and checks that all of them pass,
 * returning all failures.
 *
 * @name all(perms)
 * @param {Array} perms
 * @returns {Function}
 * @api public
 */

exports.all = function (perms) {
    return function () {
        var args = arguments;
        return _.reduce(perms, function (errs, p) {
            return errs.concat(utils.getErrors(p, args));
        }, []);
    };
};

/**
 * Tests to see if any one permission function passes, returning on the
 * first success. If all permissions fail, then all errors are returned.
 *
 * @name any(perms)
 * @param {Array} perms
 * @api public
 */

exports.any = function (perms) {
    return function () {
        var errs = [];
        for (var i = 0, len = perms.length; i < len; i++) {
            try {
                var p_errs = (perms[i].apply(this, arguments) || []);
                errs = errs.concat(p_errs);
                if (!p_errs.length) {
                    // return as soon as one passes
                    return [];
                }
            }
            catch (e) {
                // store the first error to re-throw if none pass
                errs.push(e);
            }
        }
        return errs;
    };
};

/**
 * Treat new and old values like new documents of a given type, and attempt to
 * authorize the value against the type's permissions. Useful when handling
 * permissions for an embedded type.
 *
 * Can be combined with permissions.any or permissions.all to extend the
 * permissions for an embedded type field. For example, the following might
 * allow both the owner of the parent document and the owner of the comment
 * itself to remove it.
 *
 * <pre><code class="javascript">
 *     comment: fields.embed({
 *         type: types.comment,
 *         permissions: {
 *             remove: permissions.any([
 *                 permissions.usernameMatchesField('creator'),
 *                 permissions.inherit(types.comment)
 *             ])
 *         }
 *     });
 * </code></pre>
 *
 * @name inherit(type)
 * @param {Type} type
 * @api public
 */

exports.inherit = function (type) {
    return function (newDoc, oldDoc, newValue, oldValue, userCtx) {
        return type.authorize(newValue || {_deleted: true}, oldValue, userCtx);
    };
};


})};

/********** couchtypes/render **********/

kanso.moduleCache["couchtypes/render"] = {load: (function (module, exports, require) {

/**
 * Renderer constructors and general utilities for rendering Form objects
 * as HTML.
 *
 * @module
 */

/**
 * Module dependencies
 */

var events = require('events'),
    sanitize = require('sanitize'),
    _ = require('underscore')._;

var h = sanitize.escapeHtml;


/**
 * This module is an EventEmitter
 */

var exports = module.exports = new events.EventEmitter();

// set a high listener limit since there may be one for each field
exports.setMaxListeners(1000);


/**
 * Generates a script tag that fires the event named {name}.
 */

exports.scriptTagForEvent = function (name) {
    var rv = (
        '<script type="text/javascript">' +
        "// <![CDATA[\n" +
            "if (typeof require !== 'undefined') {\n" +
            "  require('couchtypes/render').emit('" +
                sanitize.cdata(sanitize.js(name)) +
            "');\n" +
            "}" +
        "// ]]>" +
        '</script>'
    );

    return rv;
}; 

/**
 * Renders HTML for error messages.
 *
 * @name errorHTML(errors)
 * @param {Array} errors
 * @returns {String}
 * @api public
 */

exports.errorHTML = function (errors) {
    if (errors && errors.length) {
        var html = '<ul class="errors right">';
        for (var i = 0; i < errors.length; i++) {
            html += (
                '<li class="error_msg">' +
                    h(errors[i].message || errors[i].toString()) +
                '</li>'
            );
        }
        html += '</ul>';
        return html;
    }
    return '';
};

/**
 * Generates the text for a field's label, depending on whether
 * a custom label is defined or not. If not, the name is captialized and
 * underscores are replaces with spaces to produce the label's text.
 *
 * @name labelText(field, name)
 * @param {Object} field
 * @param {String} name
 * @returns {String}
 * @api public
 */

exports.labelText = function (field, name) {
    if (field.label) {
        return field.label;
    }
    return name.substr(0, 1).toUpperCase() + name.substr(1).replace(/_/g, ' ');
};

/**
 * Generates HTML for label tags.
 *
 * @name labelHTML(field, name, id)
 * @param {Object} field
 * @param {String} name
 * @param {String} id
 * @returns {String}
 * @api public
 */

exports.labelHTML = function (field, name, opt) {
    opt = opt || {};
    var id = opt.id || sanitize.generateDomIdentifier(
        name, opt.offset, opt.path_extra
    );
    return '<label for="' + h(id) + '">' +
        h(exports.labelText(field, (opt.caption || name), id)) +
    '</label>';
};

/**
 * Generates HTML for field descriptions (if defined).
 *
 * @name descriptionHTML(obj)
 * @param {Object} obj
 * @returns {String}
 * @api public
 */

exports.descriptionHTML = function (obj) {
    if (obj.description) {
        return '<div class="description">' + h(obj.description) + '</div>';
    }
    return '';
};

/**
 * Generates HTML for field hints (if defined).
 *
 * @name hintHTML(obj)
 * @param {Object} obj
 * @returns {String}
 * @api public
 */

exports.hintHTML = function (obj) {
    if (obj.hint) {
        return '<div class="hint">' + h(obj.hint) + '</div>';
    }
    return '';
};

/**
 * Creates an array of default class names for a field. This includes
 * 'required' for required fields and 'error' for fields failing validation.
 *
 * All fields are given a 'field' class.
 *
 * @name classes(field, errors)
 * @param {Object} field
 * @param {Array} errors
 * @returns {Array}
 * @api public
 */

exports.classes = function (field, errors) {
    var r = ['field'];
    if (errors && errors.length) {
        r.push('error');
    }
    if (field.required) {
        r.push('required');
    }
    return r;
};

/**
 * Determines the default renderer class, to be used globally by 
 * instances of forms.Form that fail to specify a renderer class.
 *
 * @name defaultRenderer()
 * @returns {Function}
 * @api public
 */

exports.defaultRenderer = function () {
    return exports.div;
};


/**
 *  Renders a form using a series of properly-nested <div> tags.
 *  These <div>s are labelled with specific CSS classes, some of which
 *  provide depth information (with the aim of simplifying CSS rules).
 *  See style.css for details on how to style this output.
 */
exports.div = function () {
    /**
     * Constructor for renderer; initializes object. The string returned
     * from this function is prepended to the form's markup.
     *
     * @constructor
    */
    this.start = function (errs) {
        this.depth = 0;
        var html = '<div class="render render-div">';
        if (errs && errs.length) {
            html += '<ul class="errors">';
            _.each(errs, function (e) {
                html += '<li>' + (e.message || e.toString()) + '</li>';
            });
            html += '</ul>';
        }
        return html;
    };

    /**
     * Called by the forms layer when it encounters a new
     * nesting context (i.e. a new grouping of fields). The
     * path parameter is an array of strings that describes
     * the path (in terms of document keys) to the new group.
     * When concatenated together with a dot, this array yields
     * the new prefix for named HTML form fields.
     *
     * @param {Array} path
    */
    this.beginGroup = function (path) {
        this.depth += 1;
        var name = _.last(path);
        var css_class = 'clear group level-' + this.depth;
        return (
            '<fieldset class="' + h(css_class) + '">' +
            '<legend>' +
                h(name.substr(0, 1).toUpperCase() +
                    name.substr(1).replace(/_/g, ' ')) +
            '</legend>'
        );
    };

    /**
     * Called by the forms layer when it encounters the end
     * of a nesting context. In the absence of errors, this
     * function is guaranteed to be called once for each time
     * that beginGroup is called; the order will be nested and
     * properly balanced. The path argument is the same as it was
     * for the corresponding beginGroup call; see beginGroup.
     *
     * @param {Array} path
    */
    this.endGroup = function (path) {
        this.depth -= 1;
        return '</fieldset>';
    };

    /**
     * Called by the forms layer when it encounters any regular
     * field -- i.e. one that is neither an embed nor an embedList.
     *
     * @param {Object} field
     * @param {Array} path
     * @param {Object} value
     * @param {String} raw
     * @param {Array} errors
     * @param {Object} options An object containing widget options, which
     *          will ultimately be provided to each widget's toHTML method.
    */
    this.field = function (field, path, value, raw, errors, options) {
        var name = path.join('.');
        var caption = path.slice(this.depth).join(' ');

        exports.once('renderFinish', function () {
            if (field.widget.clientInit) {
                setTimeout(function () {
                    field.widget.clientInit(
                        field, path, value, raw, errors, (options || {})
                    );
                }, 0);
            }
        });

        if (field.widget.type === 'hidden') {
            return field.widget.toHTML(
                name, value, raw, field, (options || {})
            );
        }

        options.caption = caption;
        return (
            '<div class="' +
                exports.classes(field, errors).join(' ') + '">' +
                '<div class="form-label">' +
                    exports.labelHTML(field, name, options) +
                    exports.descriptionHTML(field) +
                '</div>' +
                '<div class="form-content">' +
                    '<div class="inner">' +
                        field.widget.toHTML(
                            name, value, raw, field, (options || {})
                        ) +
                    '</div>' +
                    '<div class="hint">' +
                        exports.hintHTML(field) +
                    '</div>' +
                    '<div class="errors">' +
                        exports.errorHTML(errors) +
                    '</div>' +
                    '<div class="clear"></div>' +
                '</div>' +
            '</div>'
        );
    };

    /**
     * Called by the forms layer when it is finished rendering a form.
     * Markup returned from this function is appended to the form output.
     *
     * @param {String} field
     * @param {Array} path
     * @param {Object} value
     * @param {String} raw
     * @param {Array} errors
    */
    this.end = function () {
        return (
            '<div class="final"></div>' +
            '</div>'
        );
    };
};



})};

/********** couchtypes/types **********/

kanso.moduleCache["couchtypes/types"] = {load: (function (module, exports, require) {

/**
 * Document types can be used to ease the validation of updates and check
 * permissions when creating, editing or deleting documents.
 *
 * @module
 */


/**
 * Module dependencies
 */

var utils = require('./utils'),
    db = require('db'),
    fields = require('couchtypes/fields'),
    fieldset = require('couchtypes/fieldset'),
    widgets = require('couchtypes/widgets'),
    permissions = require('couchtypes/permissions'),
    _ = require('underscore')._;


/**
 * Creates a new Type object
 *
 * #### Options ####
 *
 * * **fields**       *Object* - Field objects to use as the Types's schema
 * * **permissions**  *Object* - a permissions check function or an object
 *                    containing separate functions to run on add, remove
 *                    and update operations.
 * * **display_name** *Function|String|Array* - name to be used when displaying
 *                    the document in the admin tool. A string or array
 *                    will become the property to display. A function
 *                    should take the document as a object and return
 *                    the display name.
 *
 * @name Type(name, options)
 * @param {Object} options
 * @constructor
 * @api public
 */

var Type = exports.Type = function Type(name, options) {
    if (typeof name !== 'string') {
        throw new Error('First argument must be the type name');
    }
    this.name = name;
    options = options || {};

    var f = {};
    f._id = fields.string({
        omit_empty: true,
        required: false,
        widget: widgets.hidden(),
        permissions: {
            update: permissions.fieldUneditable()
        },
        default_value: function (req) {
            return req.uuid;
        }
    });
    f._rev = fields.string({
        omit_empty: true,
        required: false,
        widget: widgets.hidden()
    });
    f._deleted = fields.boolean({
        omit_empty: true,
        required: false,
        widget: widgets.hidden()
    });
    f.type = fields.string({
        default_value: name,
        widget: widgets.hidden(),
        validators: [
            function (doc, val, raw) {
                if (val !== name) {
                    throw new Error('Unexpected value for type');
                }
            },
        ]
    });
    for (var k in options.fields) {
        if (options.fields.hasOwnProperty(k)) {
            f[k] = options.fields[k];
        }
    }

    options.fields = f;
    _.extend(this, _.defaults(options, {
        permissions: []
    }));

    if (options.display_name) {
        if (typeof options.display_name !== 'function') {
            this.display_name = function (doc) {
                if (typeof options.display_name === 'string') {
                    options.display_name = [options.display_name];
                }
                return utils.getPropertyPath(doc, options.display_name);
            };
        }
    }
};

/**
 * Run field validators against document and check for missing required
 * fields or extra fields when the Types's allow_extra_fields property is
 * set to false.
 *
 * @name Type.validate(doc, rawDoc)
 * @param {Object} doc
 * @param {Object} rawDoc
 * @returns {Array}
 * @api public
 */

Type.prototype.validate = function (doc, rawDoc) {
    rawDoc = rawDoc || doc;
    return fieldset.validate(
        this.fields,
        doc,
        doc,
        rawDoc,
        [],
        this.allow_extra_fields
    );
};


/**
 * Run field permissions checks against userCtx and document.
 *
 * @name Type.authorize(nDoc, oDoc, user)
 * @param {Object} nDoc - new document
 * @param {Object} oDoc - old document
 * @param {Object} userCtx - user context object
 * @returns {Array}
 * @api public
 */

Type.prototype.authorize = function (nDoc, oDoc, user) {
    var errs = this.authorizeTypeLevel(nDoc, oDoc, user);
    return errs.concat(fieldset.authFieldSet(
        this.fields, nDoc, oDoc, nDoc, oDoc, user, [], this.allow_extra_fields
    ));
};

/**
 * Runs top type-level permissions checks only.
 *
 * @name Type.authorizeTypeLevel(nDoc, oDoc, user)
 * @param {Object} nDoc - new document
 * @param {Object} oDoc - old document
 * @param {Object} userCtx - user context object
 * @returns {Array}
 * @api public
 */

Type.prototype.authorizeTypeLevel = function (nDoc, oDoc, user) {
    var perms = this.permissions;
    var errs = [];
    if (_.isFunction(perms)) {
        errs = errs.concat(
            utils.getErrors(perms, [nDoc, oDoc, null, null, user])
        );
    }
    // on update
    var fn = perms.update;
    // on add
    if (nDoc && !oDoc) {
        fn = perms.add;
    }
    // on remove
    else if (!nDoc || nDoc._deleted) {
        fn = perms.remove;
    }
    if (fn) {
        errs = errs.concat(
            utils.getErrors(fn, [nDoc, oDoc, null, null, user])
        );
    }
    return errs;
};

/**
 * Create's a new object for this Type. Pre-filling any default values and
 * providing a new _id value. This is a convenient function to use when creating
 * a type to embed within another.
 *
 * @name Type.create(userCtx, callback)
 * @param {Object} userCtx
 * @param {Function} callback
 * @api public
 */

Type.prototype.create = function (userCtx, callback) {
    var doc = fieldset.createDefaults(this.fields, {userCtx: userCtx});
    db.newUUID(100, function (err, uuid) {
        if (err) {
            return callback(err);
        }
        doc._id = uuid;
        callback(null, doc);
    });
};

/**
 * Calls validation and permissions functions relevant to a document update.
 * This should be called from your app's exported validate_doc_update function
 * if you wish to use kanso Types in you project.
 *
 * Throws on error.
 *
 * @name validate_doc_update(types, newDoc, oldDoc, userCtx)
 * @param {Object} types
 * @param {Object} newDoc
 * @param {Object} oldDoc
 * @param {Object} userCtx
 * @api public
 */

exports.validate_doc_update = function (types, newDoc, oldDoc, userCtx) {
    var type = (oldDoc && oldDoc.type) || newDoc.type;
    if (type && types[type]) {
        var t = types[type];
        if (!newDoc._deleted) {
            var validation_errors = t.validate(newDoc);
            if (validation_errors.length) {
                var err = validation_errors[0];
                var msg = err.message || err.toString();
                if (err.field && err.field.length) {
                    msg = err.field.join('.') + ': ' + msg;
                }
                throw {forbidden: msg};
            }
        }
        var permissions_errors = t.authorize(newDoc, oldDoc, userCtx);
        if (permissions_errors.length) {
            var err2 = permissions_errors[0];
            var msg2 = err2.message || err2.toString();
            if (err2.field && err2.field.length) {
                msg2 = err2.field.join('.') + ': ' + msg2;
            }
            throw {unauthorized: msg2};
        }
        if (t.validate_doc_update) {
            t.validate_doc_update(newDoc, oldDoc, userCtx);
        }
    }
};

/**
 * This type wraps a reference to a document. The _id attribute is
 * auto-generated as usual; the id of the document being referred to
 * is stored in the 'ref' attribute. In lists, this has the effect of
 * allowing multiple references to a single document.
 */

exports.reference = function (options) {
    if (!(options.type instanceof Type)) {
        throw new Error(
            'reference: The `type` option was not specified,' +
                'or is not an instance of the `Type` class.'
        );
    }
    var type = new Type('reference', {
        fields: {
            ref: fields.string({
                omit_empty: true,
                required: !!options.required,
                widget: widgets.hidden(),
                permissions: {},
                default_value: function (req) {
                    return undefined;
                }
            })
        }
    });
    type.type = options.type;
    return type;
};

/**
 * This type wraps a reference to a document. The _id attribute is
 * made writeable, and is used to store the id of the document being
 * referred to. In lists, has the effect of constraining each reference
 * to appear no more than once.
 */

exports.uniqueReference = function (options) {
    if (!(options.type instanceof Type)) {
        throw new Error(
            'uniqueReference: The `type` option was not specified,' +
                'or is not an instance of the `Type` class.'
        );
    }
    var type = new Type('unique_reference', {
        fields: {}
    });
    type.fields._id = fields.string({
        omit_empty: true,
        required: !!options.required,
        widget: widgets.hidden(),
        permissions: {},
        default_value: function (req) {
            return req.uuid;
        }
    });
    type.type = options.type;
    return type;
};


})};

/********** couchtypes/utils **********/

kanso.moduleCache["couchtypes/utils"] = {load: (function (module, exports, require) {

/**
 * General utility functions used by CouchTypes modules.
 *
 * @module
 */


/**
 * Module dependencies
 */

var _ = require('underscore')._;


/**
 * Some functions calculate results differently depending on the execution
 * environment. The isBrowser value is used to set the correct environment
 * for these functions, and is only exported to make unit testing easier.
 */

exports.isBrowser = function () {
    return (typeof(window) !== 'undefined');
};


/**
 * A named empty function. Use this when you wish to take
 * no action for a callback or string-generating  function.
 */

exports.emptyFunction = function ()
{
    return '';
};

/**
 * Traverses an object and its sub-objects using an array of property names.
 * Returns the value of the matched path, or undefined if the property does not
 * exist.
 *
 * If a string if used for the path, it is assumed to be a path with a single
 * key (the given string).
 *
 * <pre>
 * getPropertyPath({a: {b: 'foo'}}, ['a','b']) -> 'foo'
 * getPropertyPath({a: {b: 'foo'}}, 'a') -> {b: 'foo'}
 * </pre>
 *
 * @name getPropertyPath(obj, path)
 * @param {Object} obj
 * @param {Array|String} path
 * @api public
 */

exports.getPropertyPath = function (obj, path) {
    if (!_.isArray(path)) {
        path = [path];
    }
    if (!path.length || !obj) {
        return obj;
    }
    return exports.getPropertyPath(obj[path[0]], path.slice(1));
};

/**
 * Traverses an object and its sub-objects using an array of property names.
 * Sets the value of the matched property.
 *
 * If a string if used for the path, it is assumed to be a path with a single
 * key (the given string).
 *
 * <pre>
 * setPropertyPath({}, ['a','b'], 'foo') -> {a: {b: 'foo'}}
 * setPropertyPath({}, 'a', 'foo') -> {a: 'foo'}
 * </pre>
 *
 * @name setPropertyPath(obj, path, val)
 * @param {Object} obj
 * @param {Array|String} path
 * @api public
 */

exports.setPropertyPath = function (obj, path, val) {
    if (!_.isArray(path)) {
        path = [path];
    }
    if (!path.length) {
        throw new Error('No property path given');
    }
    if (path.length === 1) {
        obj[path[0]] = val;
        return;
    }
    var next = path[0];
    path = path.slice(1);
    if (obj[next] === undefined) {
        obj[next] = {};
    }
    else if (typeof obj[next] !== 'object' && path.length) {
        throw new Error('Property path conflicts with existing value');
    }
    exports.setPropertyPath(obj[next], path, val);
};

/**
 * Call function with arguments, catch any errors and add to an array,
 * returning the modified array.
 *
 * @param {Array} arr
 * @param {Function} fn
 * @param {Array} args
 * @returns {Array}
 * @api private
 */

exports.getErrors = function (fn, args) {
    var arr = [];
    try {
        arr = arr.concat(fn.apply(this, args) || []);
    }
    catch (e) {
        arr.push(e);
    }
    return arr;
};

/**
 * Parse CSV strings into an array of rows, each row an array of values.
 * Used by the array field's default CSV widget.
 *
 * @name parseCSV(csvString)
 * @param {String} csvString
 * @returns {Array}
 * @api public
 */

// Parsing comma-separated values (CSV) in JavaScript by M. A. SRIDHAR
// http://yawgb.blogspot.com/2009/03/parsing-comma-separated-values-in.html
exports.parseCSV = function (csvString) {
    var fieldEndMarker  = /([,\015\012] *)/g;
    var qFieldEndMarker = /("")*"([,\015\012] *)/g;
    var startIndex = 0;
    var records = [], currentRecord = [];
    do {
        var ch = csvString.charAt(startIndex);
        var endMarkerRE = (ch === '"') ? qFieldEndMarker : fieldEndMarker;
        endMarkerRE.lastIndex = startIndex;
        var matchArray = endMarkerRE.exec(csvString);
        if (!matchArray || !matchArray.length) {
            break;
        }
        var endIndex = endMarkerRE.lastIndex;
        endIndex -= matchArray[matchArray.length - 1].length;
        var match = csvString.substring(startIndex, endIndex);
        if (match.charAt(0) === '"') {
            match = match.substring(1, match.length - 1).replace(/""/g, '"');
        }
        currentRecord.push(match);
        var marker = matchArray[0];
        if (marker.indexOf(',') < 0) {
            records.push(currentRecord);
            currentRecord = [];
        }
        startIndex = endMarkerRE.lastIndex;
    } while (true);
    if (startIndex < csvString.length) {
        var remaining = csvString.substring(startIndex).trim();
        if (remaining) {
            currentRecord.push(remaining);
        }
    }
    if (currentRecord.length > 0) {
        records.push(currentRecord);
    }
    return records;
};

/**
 * Tests if path b is equal to or a sub-path of a.
 *
 * @name isSubPath(a, b)
 * @param {Array} a
 * @param {Array} b
 * @returns {Boolean}
 * @api public
 */

exports.isSubPath = function (a, b) {
    for (var i = 0, len = a.length; i < len; i++) {
        if (a[i] !== b[i]) {
            return false;
        }
    }
    return true;
};

/**
 * Return a title-case version of the supplied string.
 * @name titleize(str)
 * @param str The string to transform.
 * @returns {String}
 * @api public
 */

exports.titleize = function (str) {
    return (str || '').toLowerCase().replace(/_+/, ' ').replace(
        /(?:^|\s+)\w/g, function (m) {
            return m.toUpperCase();
        }
    );
};

/**
 * Returns a function that executes {closure} in the context of {context}.
 * Use this function if you'd like to preserve the current context
 * across callbacks, event handlers, and other cases where the value of
 * {this} is set for you. If you're coming from the Prototype framework,
 * this function is similar to bind() there.
 *
 * @name bindContext(context, closure)
 * @param {Object} context The context to use when executing closure.
 *          Usually, you will specify the current value of 'this'.
 * @param {Function} closure The function to to bind to {context}.
 * @api public
 */

exports.bindContext = function (context, closure) {
    return function () {
        return closure.apply(context, arguments);
    };
};


/**
 * Returns attachments below a given path from a document, returning an object
 * with the attachment names relative to that path. Example:
 *
 *     var doc = {_attachments: {
 *         'foo/bar.ext': {data: 'one', ...},
 *         'foo/baz.ext': {data: 'two', ...},
 *         'asdf.ext':    {data: 'blah', ...}
 *     }};
 *
 *     utils.attachmentsBelowPath(doc, 'foo') => {
 *         'bar.ext': {data: 'one', ...},
 *         'baz.ext': {data: 'two', ...}
 *     }
 *
 * @name attachmentsBelowPath(doc, path)
 * @param {Object} doc
 * @param {String | Array} path
 * @api public
 */

exports.attachmentsBelowPath = function (doc, path) {
    if (!doc || !doc._attachments) {
        return {};
    }
    if (_.isArray(path)) {
        path = path.join('/');
    }
    var results = {};
    for (var k in doc._attachments) {
        if (k.substr(0, path.length + 1) === path + '/') {
            results[k.substr(path.length + 1)] = doc._attachments[k];
        }
    };
    return results;
};


})};

/********** couchtypes/validators **********/

kanso.moduleCache["couchtypes/validators"] = {load: (function (module, exports, require) {

/**
 * Validation functions used to validate Field contents.
 *
 * @module
 */

var _ = require('underscore')._;


/**
 * Tests that the field's value is greater than 'min'.
 *
 * @name min(min)
 * @param {Number} min
 * @returns {Function}
 * @api public
 */

exports.min = function (min) {
    return function (doc, value) {
        if (value < min) {
            throw new Error(
                'Please enter a value greater than or equal to ' + min
            );
        }
    };
};

/**
 * Tests that the field's value is less than 'max'
 *
 * @name max(max)
 * @param {Number} max
 * @returns {Function}
 * @api public
 */

exports.max = function (max) {
    return function (doc, value) {
        if (value > max) {
            throw new Error(
                'Please enter a value less than or equal to ' + max
            );
        }
    };
};

/**
 * Tests that the field's value is greater than 'min' AND less than 'max'
 *
 * @name range(min, max)
 * @param {Number} min
 * @param {Number} max
 * @returns {Function}
 * @api public
 */

exports.range = function (min, max) {
    return function (doc, value) {
        if (value < min || value > max) {
            throw new Error(
                'Please enter a value between ' + min + ' and ' + max
            );
        }
    };
};

/**
 * Tests that the field's value length is greater than 'val'
 *
 * @name minlength(val)
 * @param {Number} val
 * @returns {Function}
 * @api public
 */

exports.minlength = function (val) {
    return function (doc, value) {
        if (value.length < val) {
            throw new Error('Please enter at least ' + val + ' characters');
        }
    };
};

/**
 * Tests that the field's value length is less than 'val'
 *
 * @name maxlength(val)
 * @param {Number} val
 * @returns {Function}
 * @api public
 */

exports.maxlength = function (val) {
    return function (doc, value) {
        if (value.length > val) {
            throw new Error('Please enter no more than ' + val + ' characters');
        }
    };
};

/**
 * Tests that the field's value length is greater than 'min' AND less than 'max'
 *
 * @name rangelength(min, max)
 * @param {Number} min
 * @param {Number} max
 * @returns {Function}
 * @api public
 */

exports.rangelength = function (min, max) {
    return function (doc, value) {
        if (value.length < min || value.length > max) {
            throw new Error(
                'Please enter a value between ' + min + ' and ' + max +
                ' characters long'
            );
        }
    };
};

/**
 * Tests field's value against a regular expression
 *
 * @name regexp(re, message)
 * @param {RegExp} re - can be a string or RegExp object
 * @param {String} message - (optional) a custom error message to throw
 * @returns {Function}
 * @api public
 */

exports.regexp = function (re, message) {
    re = (typeof re === 'string') ? new RegExp(re): re;
    return function (doc, value) {
        if (!re.test(value)) {
            throw new Error(message || 'Invalid format');
        }
    };
};

/**
 * Tests that field's value is a valid email address using a regular expression.
 *
 * @name email()
 * @returns {Function}
 * @api public
 */

exports.email = function () {
    // regular expression by Scott Gonzalez:
    // http://projects.scottsplayground.com/email_address_validation/
    return exports.regexp(new RegExp("^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?$", "i"), 'Please enter a valid email address');
};

/**
 * Tests that field's value is a valid URL using a regular expression.
 *
 * @name url()
 * @returns {Function}
 * @api public
 */

exports.url = function () {
    // regular expression by Scott Gonzalez:
    // http://projects.scottsplayground.com/iri/
    return exports.regexp(new RegExp("^(https?|ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(\\#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$", "i"), 'Please enter a valid URL');
};

/**
 * Detects embedded documents with missing _id properties and returns an
 * array of Error objects for each occurence. Returns an empty array if
 * all documents have a populated _id property.
 *
 * Used by the EmbeddedList field type.
 *
 * @name missingIDs()
 * @param {Object} doc
 * @param {Array} value
 * @returns {Array}
 * @api public
 */

exports.missingIDs = function () {
    return function (doc, value) {
        var errs = [];
        _.each(value, function (v, i) {
            if (!v._id) {
                var e = new Error('Embedded document missing _id');
                e.field = [i];
                errs.push(e);
            }
        });
        return errs;
    };
};



})};

/********** couchtypes/widgets.core **********/

kanso.moduleCache["couchtypes/widgets.core"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Widgets define the way a Field object is displayed when rendered as part of a
 * Form. Changing a Field's widget will be reflected in the admin app.
 *
 * @module
 */

/**
 * Module dependencies
 */

var sanitize = require('sanitize'),
    session = require('session'),
    _ = require('underscore')._;

var h = sanitize.escapeHtml;


/**
 * Widget constructor, creates a new Widget object.
 *
 * @name Widget(type, [options])
 * @param {String} type
 * @param {Object} options
 * @constructor
 * @returns {Widget Object}
 * @api public
 */

var Widget = exports.Widget = function Widget(type, options) {
    options = (options || {});
    this.classes = (options.classes || []);
    this.options = options;
    this.id = options.id;
    this.type = type;
};

/**
 * Generates an id string for a widget.
 *
 * @param {String} name - field name on the HTML form
 * @param {String} extension - optional; a string to be added
 *                  to the generated identifier. Use this when you
 *                  want to make an identifier that is related to
 *                  an existing identifier, but is still unique.
 * @returns {String}
 */

Widget.prototype._id = function (name /* , ... */) {
    return sanitize.generateDomIdentifier.apply(
        this, [ this.id || name ].concat(
            Array.prototype.slice.call(arguments, 1)
        )
    );
};

/**
 * Generates a name string for a widget.
 *
 * @param {String} name - field name on the HTML form
 * @param {String} extension - optional; a string to be added
 *                  to the generated identifier. Use this when you
 *                  want to make an identifier that is related to
 *                  an existing identifier, but is still unique.
 * @returns {String}
 */

Widget.prototype._name = function (name /* , ... */) {
    return sanitize.generateDomName.apply(
        this, [ name ].concat(
            Array.prototype.slice.call(arguments, 1)
        )
    );
};

/**
 * Converts an input element's value attribute to a valid
 * in-memory representation of the document or document fragment.
 * This function tries to interpret the string as JSON if it's
 * appropriate; otherwise the string is left alone.
 *
 * @name _parse_value(value)
 * @param {String} value The string value to parse. If value is
 *          already an object, it it returned with no modifications.
 * @returns {Object}
 */

Widget.prototype._parse_value = function (value)
{
    var rv = value;

    if (typeof(rv) === 'string') {
        rv = JSON.parse(rv);
    }

    return rv;
};

/**
 * Converts an in-memory representation of the document or
 * document fragment in to an encoded string. If the value
 * passed is already encoded, this function does nothing.
 *
 * @name _stringify_value(value)
 * @param {String} value The value to encode. If value is already
 *          a string, it is returned with no modifications.
 * @returns {Object}
 */

Widget.prototype._stringify_value = function (value)
{
    var rv = value;

    if (typeof(rv) !== 'string') {
        rv = JSON.stringify(rv);
    }

    return rv;
};

/**
 * Converts a widget to HTML using the provided name and parsed and raw values
 *
 * @name Widget.toHTML(name, value, raw, field, options)
 * @param {String} name
 * @param value
 * @param raw
 * @param field
 * @param options
 * @returns {String}
 * @api public
 */

Widget.prototype.toHTML = function (name, value, raw, field, options) {
    if (raw === undefined) {
        raw = (value === undefined) ? '': '' + value;
    }
    if (raw === null || raw === undefined) {
        raw = '';
    }
    var html = '<input';
    html += (this.type ? ' type="' + h(this.type) + '"': '');
    html += ' value="' + h(raw) + '"';
    html += ' name="' + this._name(name, options.offset) + '" id="';
    html += this._id(name, options.offset, options.path_extra) + '"';

    // additionaly parameters optionally passed to widget
    if ('maxlength' in this.options) {
        html += ' maxlength="' + h(this.options.maxlength) + '"';
    }
    if ('size' in this.options) {
        html += ' size="' + h(this.options.size) + '"';
    }
    if ('disabled' in this.options) {
        html += ' disabled="' + h(this.options.disabled) + '"';
    }
    if ('readonly' in this.options) {
        html += ' readonly="' + h(this.options.readonly) + '"';
    }
    return html + ' />';
};

/**
 * Initializes a widget on the client-side only, using the browser's
 * script interpreter. This function is guaranteed to be called
 * after toHTML, and any DOM elements created by toHTML are
 * guaranteed to be accessible
 *
 * @name Widget.clientInit(path, value, raw, field, options)
 * @param {Array} path
 * @param value
 * @param raw
 * @param field
 * @param options
 * @returns {Boolean}
 * @api public
 */

Widget.prototype.clientInit = function (path, value, raw, field, options) {
    return true;
};

/**
 * Called by CouchTypes when it becomes necessary to rename this widget
 * instance. The widget should respond by updating the id and name
 * attributes.
 *
 * @name Widget.updateName(path)
 * @param {String} elt An element that contains one or
 *          more instances of the widget referenced by `this'.
 * @param {String} path The widget's new path; combine this using
 *          the _name or _id function to generate a usable string.
 * @param {Object} options A new set of toHTML/clientInit options.
 *          This may or may not influence the widget's name.
 * @api public
 */

Widget.prototype.updateName = function (elt, path, options) {
    var e = $('input[type=hidden]', elt);
    e.attr('id', this._id(path, options.offset, options.path_extra));
    e.attr('name', this._name(path, options.offset, options.path_extra));
};

/**
 * Called by CouchTypes when it becomes necessary to rename this widget
 * instance. The widget should respond by updating the value attribute.
 *
 * @name Widget.updateValue(elt, path, value, options)
 * @param {String} elt An element that contains one or
 *          more instances of the widget referenced by `this'.
 * @param {String} path The path to the widget.
 * @param {Object} value The new value for the widget, unencoded.
 * @param {Object} options An up-to-date set of toHTML/clientInit options.
 * @api public
 */

Widget.prototype.updateValue = function (elt, path, value, options) {
    elt = $(elt).closestChild('input[type=hidden]');
    elt.val(this._stringify_value(value));
};

/**
 * Called by CouchTypes when it becomes necessary to interrogate this
 * widget to determine its value. The widget should respond by
 * returning an unencoded value (typically as an object).
 *
 * @name Widget.getValue(elt, path, options)
 * @param {String} elt An element that contains one or
 *          more instances of the widget referenced by `this'.
 * @param {String} path The path to the widget.
 * @param {Object} options An up-to-date set of toHTML/clientInit options.
 * @api public
 */

Widget.prototype.getValue = function (elt, path, options) {
    return this._parse_value(
        $(elt).closestChild('input[type=hidden]').val()
    );
};

/**
 * Called by CouchTypes when it becomes necessary to validate the
 * contents of this widget -- i.e. to ensure it's in a consistent
 * state before using its value or proceeding. Most widgets will
 * not implement this method; its primary use is complex widgets
 * that host validation-enabled forms and/or types. Returns true
 * if the contents is consistent and valid; false otherwise.
 *
 * @name Widget.validate(elt, path, options)
 * @param {String} elt An element that contains one or
 *          more instances of the widget referenced by `this'.
 * @param {String} path The path to the widget.
 * @param {Object} options An up-to-date set of toHTML/clientInit options.
 * @api public
 */

Widget.prototype.validate = function (elt, path, options) {
    return true;
};

/**
 * Creates a new text input widget.
 *
 * @name text([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.text = function (options) {
    return new Widget('text', options);
};

/**
 * Creates a new password input widget.
 *
 * @name password([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.password = function (options) {
    return new Widget('password', options);
};

/**
 * Creates a new hidden input widget.
 *
 * @name hidden([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.hidden = function (options) {
    return new Widget('hidden', options);
};

/**
 * Creates a new textarea widget.
 *
 * @name textarea([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.textarea = function (_options) {
    var w = new Widget('textarea', _options || {});
    w.options = _options;
    w.toHTML = function (name, value, raw, field, options) {
        if (raw === undefined) {
            raw = (value === undefined) ? '': '' + value;
        }
        if (raw === null || raw === undefined) {
            raw = '';
        }
        var html = '<textarea';
        html += ' name="' + this._name(name, options.offset) + '" id="';
        html += this._id(name, options.offset, options.path_extra) + '"';

        if (this.options.hasOwnProperty('cols')) {
            html += ' cols="' + h(this.options.cols) + '"';
        }
        if (this.options.hasOwnProperty('rows')) {
            html += ' rows="' + h(this.options.rows) + '"';
        }
        html += '>' + h(raw);
        html += '</textarea>';
        return html;
    };
    return w;
};

/**
 * Creates a new checkbox widget.
 *
 * @name checkbox([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.checkbox = function (_options) {
    var w = new Widget('checkbox', _options || {});
    w.toHTML = function (name, value, raw, field, options) {
        var html = '<input type="checkbox"';
        html += ' name="' + this._name(name, options.offset) + '" id="';
        html += this._id(name, options.offset, options.path_extra) + '"';
        html += (value ? ' checked="checked"': '');
        return (html + ' />');
    };
    return w;
};

/**
 * Creates a new select widget.
 *
 * @name select([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.select = function (_options) {
    var w = new Widget('select', _options || {});
    w.values = _options.values;
    w.toHTML = function (name, value, raw, field, options) {
        if (value === null || value === undefined) {
            value = '';
        }

        var html = '<select';
        html += ' name="' + this._name(name, options.offset) + '" id="';
        html += this._id(name, options.offset, options.path_extra) + '">';

        for (var i = 0; i < this.values.length; i++) {
            var opt = this.values[i];
            html += '<option value="' + h(opt[0]) + '"';
            if (opt[0] === value) {
                html += ' selected="selected"';
            }
            html += '>';
            html += h(opt[1]);
            html += '</option>';
        }
        html += '</select>';
        return html;
    };
    return w;
};

/**
 * Creates a new computed widget. Computed widgets display a string, but are
 * uneditable, working as a hidden field behind the scenes.
 *
 * @name computed([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.computed = function (_options) {
    var w = new Widget('computed', _options);
    w.toHTML = function (name, value, raw, field, options) {
        if (raw === undefined) {
            raw = (value === undefined) ? '': '' + value;
        }
        if (raw === null || raw === undefined) {
            raw = '';
        }
        var html = '<div id="';
        html += this._id(name, options.offset, options.path_extra) + '">';
        html += '<input type="hidden" value="' + h(raw) + '"';
        html += ' name="' + this._name(name, options.offset) + '" />';
        html += '<span>' + h(raw) + '</span>';
        html += '</div>';
        return html;
    };
    return w;
};

/**
 * Creates a new computed input widget which sets the value of the field to
 * the current user on new documents, responding to sessionChange events
 *
 * @name creator([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.creator = function (options) {
    var w = exports.computed(options);
    var _toHTML = w.toHTML;
    var el_name; // store input name provided by renderer
    var el; // store reference to element so we can detect when its been removed

    w.toHTML = function (name/*, ...*/) {
        el_name = name;
        return _toHTML.apply(this, arguments);
    };
    w.clientInit = function (field, path, value, raw, errors, options) {
        if (options.operation === 'add') {

            var id = w._id(el_name, options.offset, options.path_extra);

            // store reference to container element
            el = $('#' + id)[0];

            var update_val = function (userCtx, req) {
                var container = $('#' + id)[0];
                if (el !== container) {
                    // element has been removed
                    session.removeListener('change', update_val);
                    return;
                }
                if (container) {
                    $('input', container).val(userCtx.name || '');
                    $('span', container).text(userCtx.name || '');
                }
                else {
                    // element has been removed from page (or was never there?)
                    session.removeListener('change', update_val);
                }
            };
            session.on('change', update_val);
        }
    };
    return w;
};


/**
 * Creates a new file input widget.
 *
 * @name file([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.file = function (options) {
    var w = new Widget('file', options);
    w.name;
    w.val = {};

    w.toHTML = function (name, value, raw, field, options) {
        this.name = name;
        this.val = value || {};

        var id = this._id(name, options.offset, options.path_extra);
        var html = '<div id="' + id + '">';
        html += '<input type="hidden" value="' + h(JSON.stringify(value)) + '"';
        html += ' name="' + this._name(name, options.offset) + '" />';

        html += '<ul class="files">';
        for (var k in this.val) {
            html += '<li>';
            html += h(k + ' (' + this.val[k].length + ' bytes) ');
            html += '<a class="remove" href="#" rel="';
            html += escape(k) + '">Remove</a>';
            html += '</li>';
        }
        html += '</ul>';

        html += '<input type="file" />';
        html += '</div>';
        return html;
    };
    w.updateValue = function (options) {
        var str = JSON.stringify(this.val);
        var id = this._id(this.name, options.offset, options.path_extra);
        $('input[name="' + this.name + '"]').val(str);
        var html = '';
        for (var k in this.val) {
            html += '<li>';
            html += h(k + ' (' + this.val[k].length + ' bytes) ');
            html += '<a class="remove" href="#" rel="';
            html += escape(k) + '">Remove</a>';
            html += '</li>';
        }
        $('#' + id + ' ul.files').html(html);
    };
    w.addFile = function (name, obj, options) {
        this.val[name] = obj;
        this.updateValue(options);
    };
    w.removeFile = function (name, options) {
        delete this.val[name];
        this.updateValue(options);
    };
    w.clientInit = function (path, value, raw, field, options) {
        var id = this._id(this.name, options.offset, options.path_extra);
        $('#' + id + ' :file').change(function () {
            var att = {};
            _.each(this.files, function (f) {
                var reader = new FileReader();
                reader.onloadend = function (ev) {
                    var result = ev.target.result;
                    var data = result.slice(result.indexOf(',') + 1);
                    var obj = {
                        content_type: f.type,
                        length: f.size,
                        data: data
                    };
                    w.addFile(f.name, obj, options);
                };
                reader.readAsDataURL(f);
            });
            w.updateValue(att, options);
        });
        $('#' + id + ' ul.files li a.remove').click(function (ev) {
            ev.preventDefault();
            var filename = unescape($(this).attr('rel'));
            w.removeFile(filename, options);
            return false;
        });
    };
    return w;
};


})};

/********** couchtypes/widgets.embed **********/

kanso.moduleCache["couchtypes/widgets.embed"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Widgets define the way a Field object is displayed when rendered as part of a
 * Form. Changing a Field's widget will be reflected in the admin app.
 *
 * @module
 */

/**
 * Module dependencies
 */

var core = require('./widgets.core'),
    db = require('db'),
    session = require('session'),
    events = require('events'),
    forms = require('couchtypes/forms'),
    actions = require('couchtypes/actions'),
    render = require('couchtypes/render'),
    sanitize = require('sanitize'),
    utils = require('couchtypes/utils'),
    querystring = require('querystring'),
    _ = require('underscore')._,
    duality;


try {
    duality = require('duality/core');
}
catch (e) {
    // may not be available
}


var h = sanitize.escapeHtml;


/**
 * This module is an EventEmitter.
 */

var exports = module.exports = new events.EventEmitter();


/**
 * Creates a new field for storing/displaying an embedded object.
 * This is automatically added to embed and embedList field types
 * that don't specify a widget.
 *
 * @name embedded([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.embedList = function (_options) {
    var w = new core.Widget('embedList', _options);

    w.sortable = _options.sortable;
    w.singleton = _options.singleton;
    w.widget = (_options.widget || exports.defaultEmbedded());
    w.actions = actions.parse(_options.actions || {});

    w.toHTML = function (name, value, raw, field, options) {

        this.cacheInit();

        this.field = field;
        this.render_options = (options || {});
        value = this.normalizeValue(value || []);

        var id = this._id(
            name, 'list', this.render_options.offset,
                this.render_options.path_extra
        );
        var html = (
            '<div class="embed-list" rel="' +
                h(this.field.type.name) + '" id="' + h(id) + '">' +
                    '<div class="items" rel="' + h(name) + '">'
        );

        for (var i = 0, len = value.length; i < len; ++i) {
            html += this.htmlForListItem({
                offset: (this.singleton ? null : i),
                name: name,
                value: value[i],
                raw: raw
            });
        }
        html += (
                '</div>' +
                '<div class="embed-actions">' +
                    this.htmlForAddButton() +
                '</div>' +
            '</div>'
        );
        return html;
    };

    w.clientInit = function (field, path, value, raw, errors, options) {

        this.cacheInit();

        this.path = path;
        this.field = field;
        this.render_options = (options || {});
        value = this.normalizeValue(value || []);

        var item_elts = (
            this.discoverListItemsElement().children('.item')
        );

        for (var i = 0, len = item_elts.length; i < len; ++i) {
            this.bindEventsForListItem(item_elts[i]);

            if (_.isFunction(this.widget.clientInit)) {
                this.widget.clientInit(
                    this.field, this.path, value[i], value[i], [], {
                        offset: (this.singleton ? null : i)
                    }
                );
            }
        }

        this.renumberList();
        this.bindEventsForList();
    };

    /** private: **/

    w.cacheInit = function () {
        this.discoverListElement = _.memoize(this._discoverListElement);
        this.discoverListName = _.memoize(this._discoverListName);
        this.discoverListType = _.memoize(this._discoverListType);
        this.discoverListItemsElement =
            _.memoize(this._discoverListItemsElement);
    };

    w.normalizeValue = function (value) {
        value = this._parse_value(value);
        if (this.singleton) {
            if (value && !_.isArray(value)) {
                value = [ value ];
            }
        }
        return value;
    };

    w._discoverListElement = function () {
        return $('#' + this._id(
            this.path, 'list', this.render_options.offset,
                this.render_options.path_extra
        ));
    };

    w._discoverListName = function () {
        var list_elt = this.discoverListElement();
        var actions_elt = $(list_elt).closestChild('.embed-actions');
        return actions_elt.attr('rel');
    };
    
    w._discoverListType = function () {
        var list_elt = this.discoverListElement();
        return list_elt.attr('rel');
    };

    w._discoverListItemsElement = function () {
        var list_elt = this.discoverListElement();
        return list_elt.closestChild('.items');
    };

    w.discoverListItems = function () {
        return (
            this.discoverListItemsElement().children('.item')
        );
    };

    w.countListItems = function () {
        return this.discoverListItems().length;
    };

    w.bindEventsForList = function () {
        var list_elt = this.discoverListElement();
        var add_elt = $(list_elt).closestChild('.embed-actions .add');

        add_elt.bind('click', utils.bindContext(this, function (ev) {
            return this.handleAddButtonClick(ev);
        }));
    };

    w.bindEventsForListItem = function (item_elt) {
        item_elt = $(item_elt);
        var edit_elt = item_elt.closestChild('.embed-actions .edit');
        var delete_elt = item_elt.closestChild('.embed-actions .delete');

        edit_elt.bind('click', utils.bindContext(this, function (ev) {
            return this.handleEditButtonClick(ev);
        }));

        delete_elt.bind('click', utils.bindContext(this, function (ev) {
            return this.handleDeleteButtonClick(ev);
        }));

        if (this.sortable) {
            var up_elt = item_elt.closestChild('.embed-actions .up');
            var down_elt = item_elt.closestChild('.embed-actions .down');

            up_elt.bind('click', utils.bindContext(this, function (ev) {
                return this.handleUpButtonClick(ev);
            }));

            down_elt.bind('click', utils.bindContext(this, function (ev) {
                return this.handleDownButtonClick(ev);
            }));
        }
    };

    w.renumberList = function () {
        var item_elts =
            this.discoverListItemsElement().children('.item');

        for (var i = 0, len = item_elts.length; i < len; ++i) {
            var item = $(item_elts[i]);
            this.renumberListItem(item, i);
            this.updateListItemActions(item, i, len);

        }
        return this.updateListActions(len);
    };

    w.renumberListItem = function (elt, offset) {
        var widget_options = {
            offset: (this.singleton ? null : offset)
        };
        if (_.isFunction(this.widget.updateName)) {
            this.widget.updateName(elt, this.path, widget_options);
        }
    };

    w.updateListActions = function (offset) {
        var list_elt = this.discoverListElement();
        var add_elt = list_elt.closestChild('.embed-actions .add');

        if (this.singleton && offset > 0) {
            add_elt.hide();
        } else {
            add_elt.show();
        }
        return offset;
    };

    w.updateListItemActions = function (item_elt, offset, count) {
        if (this.sortable) {
            var attr = 'disabled';
            var up_elt = item_elt.closestChild('.embed-actions .up');
            var down_elt = item_elt.closestChild('.embed-actions .down');

            if (offset <= 0) {
                up_elt.attr(attr, attr);
            } else {
                up_elt.removeAttr(attr);
            }
            if (offset + 1 >= count) {
                down_elt.attr(attr, attr);
            } else {
                down_elt.removeAttr(attr);
            }
        }
    };

    w.moveExistingItem = function (after_elt, item_elt) {
        if (after_elt) {
            $(after_elt).after(item_elt);
        } else {
            var items_elt = this.discoverListItemsElement();
            items_elt.append(item_elt);
        }
        this.renumberList();
        this.bindEventsForListItem(item_elt);
    };

    w.deleteExistingItem = function (item_elt) {
        $(item_elt).remove();
        this.renumberList();
    };

    w.insertNewItemAtEnd = function (callback) {
        var list_elt = this.discoverListElement();

        var item_elts =
            this.discoverListItemsElement().children('.item');

        var last_elt = item_elts.last();

        return this.insertNewItem(
            (this.singleton ? null : item_elts.length),
                last_elt[0], callback
        );
    };

    w.insertNewItem = function (offset, after_elt, callback) {
        var list_elt = this.discoverListElement();
        var list_type = this.discoverListType();

        db.newUUID(100, utils.bindContext(this, function (err, uuid) {
            var value = { type: list_type, _id: uuid };

            var item_elt = $(this.htmlForListItem({
                name: this._name(this.path),
                offset: offset,
                value: value,
                raw: value
            }));

            this.moveExistingItem(after_elt, item_elt);

            if (_.isFunction(this.widget.clientInit)) {
                this.widget.clientInit(
                    this.field, this.path, value, null, [], {
                        offset: (this.singleton ? null : offset)
                    }
                );
            }

            if (callback) {
                callback(item_elt[0]);
            }
        }));
    };

    w.setListItemValue = function (item_elt, value, options) {
        if (this.widget.updateValue) {
            this.widget.updateValue(
                item_elt, this.path, value, options
            );
        }
    };

    w.htmlForListItem = function (item) {
        var html = (
            '<div class="item">' +
                this.widget.toHTML(
                    item.name, item.value, item.raw, this.field,
                        { offset: item.offset }
                ) +
                '<div class="embed-actions">' +
                    (this.sortable ? this.htmlForDownButton() : '') +
                    (this.sortable ? this.htmlForUpButton() : '') +
                    this.htmlForEditButton() +
                    this.htmlForDeleteButton() +
                '</div>' +
            '</div>'
        );
        return html;
    };

    w.htmlForAddButton = function () {
        return (
            '<input type="button" class="add action" value="Add" />'
        );
    };

    w.htmlForEditButton = function () {
        return (
            '<input type="button" class="edit action" value="Edit" />'
        );
    };

    w.htmlForDeleteButton = function () {
        return (
            '<input type="button" class="delete action" value="Delete" />'
        );
    };

    w.htmlForUpButton = function () {
        return (
            '<input type="button" class="up action" value="&uarr;" />'
        );
    };

    w.htmlForDownButton = function () {
        return (
            '<input type="button" class="down action" value="&darr;" />'
        );
    };

    w.dispatchEventToAction = function (target_elt, action_name,
                                        value_for_action, callback) {

        var name = this._name(this.path);
        var type_name = this.discoverListType();
        var item_elt = $(target_elt).closest('.item');
        var offset = item_elt.prevAll('.item').length;

        var widget_options = {
            offset: offset,
            path_extra: (this.render_options.path_extra || [])
        };

        if (value_for_action === undefined) {

            /* Action has no payload:
                Query the widget for its current value, and use that. */

            value_for_action = this.widget.getValue(
                item_elt, this.path, this.render_options
            );
        }

        if (!value_for_action) {
            return callback(
                this, target_elt, widget_options, false, undefined
            );
        }

        /* Grab closure for action */
        var action_handler = (
            this.actions[action_name] ||
                this.defaultActionFor(action_name)
        );

        /* Create a completion callback:
            The action handler will transfer control here when finished. */

        var cb = utils.bindContext(this,
            function (successful, new_value) {
                if (callback) {
                    callback.call(
                        this, target_elt,
                            widget_options, successful, new_value
                    );
                }
            }
        );

        /* Trigger action */
        if (action_handler) {
            action_handler(
                { action: action_name, type: type_name },

                { element: target_elt, raw: null,
                  field: this.field, path: this.path,
                  value: value_for_action, errors: [] },
                
                widget_options, cb
            );
        }
    };

    w.handleUpButtonClick = function (ev) {
        var item_elt = $(ev.target).closest('.item');
        item_elt.insertBefore(item_elt.prev('.item'));
        this.renumberList();
    };

    w.handleDownButtonClick = function (ev) {
        var item_elt = $(ev.target).closest('.item');
        item_elt.insertAfter(item_elt.next('.item'));
        this.renumberList();
    };

    w.handleAddButtonClick = function (ev) {
        var callback = utils.bindContext(
            this, this.handleAddCompletion
        );
        this.insertNewItemAtEnd(
            utils.bindContext(this, function (item_elt) {
                this.dispatchEventToAction(
                    $('.edit', item_elt), 'add', undefined, callback
                );
            })
        );
    };

    w.handleEditButtonClick = function (ev) {
        var callback = utils.bindContext(
            this, this.handleEditCompletion
        );

        this.dispatchEventToAction(
            ev.target, 'edit', undefined, callback
        );
    };

    w.handleDeleteButtonClick = function (ev) {
        var callback = utils.bindContext(
            this, this.handleDeleteCompletion
        );

        this.deleteExistingItem(
            $(ev.target).closest('.item', this)
        );

        this.dispatchEventToAction(
            ev, 'delete', undefined, callback
        );
    };

    w.handleAddCompletion = function (target_elt, offset,
                                      is_successful, new_value) {
        var item_elt =
            $(target_elt).closest('.item', this);

        if (is_successful) {
            var callback = utils.bindContext(
                this, this.handleSaveCompletion
            );
            this.dispatchEventToAction(
                item_elt, 'save', new_value, callback
            );
        } else {
            this.deleteExistingItem(item_elt);
        }
    };

    w.handleEditCompletion = function (target_elt, options,
                                       is_successful, new_value) {
        if (is_successful) {

            var callback = utils.bindContext(
                this, this.handleSaveCompletion
            );
            this.dispatchEventToAction(
                target_elt, 'save', new_value, callback
            );

        } else {

            /* Edit action was unsuccessful:
                This means the edit was canceled or otherwise aborted,
                and no changes should be made to the underlying data. */

            return this;
        }
    };

    w.handleDeleteCompletion = function (target_elt, options,
                                         is_successful, new_value) {
        exports.emit('delete', target_elt, options, is_successful, new_value);
        return;
    };

    w.handleSaveCompletion = function (target_elt, options,
                                       is_successful, new_value) {
        exports.emit('save', target_elt, options, is_successful, new_value);
        return;
    };

    w.defaultActionFor = function (name) {

        switch (name) {
        case 'add':
        case 'edit':
            return this.makeDefaultAction('showDialog', {
                widget: exports.embedForm({
                    type: this.field.type,
                    style: 'popup'
                })
            });
            /* break */
        case 'save':
            return this.makeDefaultAction('defaultEmbedSave', {});
            /* break */
        case 'delete':
            break;
        }
        return null;
    };

    w.makeDefaultAction = function (name, options) {
        return utils.bindContext(this, function () {
            actions[name].apply(
                this, [ options ].concat(
                    Array.prototype.slice.apply(arguments)
                )
            );
        });
    };

    return w;
};

/**
 * Creates a new field for storing/displaying an embedded object.
 * This is automatically added to embed and embedList field types
 * that don't specify a widget.
 *
 * @name defaultEmbedded([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.defaultEmbedded = function (_options) {
    var w = new core.Widget('defaultEmbedded', _options);

    w.toHTML = function (name, value, raw, field, options) {
        value = this._parse_value(value);
        var display_name = (value ? value._id: '');
        var fval = (value ? this._stringify_value(value) : '');

        if (field && field.type.display_name && value) {
            display_name = field.type.display_name(value);
        }
        var html = (
            '<div class="default-embed">' +
                '<input type="hidden" value="' + h(fval) + '" name="' +
                    h(this._name(name, options.offset)) + '" />' +
                '<span class="value" style="display: none">' + h(display_name) + '</span>' +
            '</div>'
        );
        return html;
    };
    return w;
};

/**
 * Creates a new instance of an embedded *form* for the specified type.
 * This is the basis for the presentation of complex data types,
 * and is used within an embedList to add and/or edit items.
 *
 * @name embedForm([options])
 * @param options
 * @returns {Widget Object}
 * @api public
 */

exports.embedForm = function (_options) {
    var w = new core.Widget('embedForm', _options);
    w.options = (_options || {});

    w.toHTML = function (name, value, raw, field, options, errors) {

        this.cacheInit();
        this.field = field;
        this.render_options = (options || {});

        value = this._parse_value(value);

        var id = this._id(
            name, 'form', this.render_options.offset,
                this.render_options.path_extra
        );

        this.is_reference = _.include(
            [ 'reference', 'unique_reference' ], this.field.type.name
        );

        if (this.is_reference && !this.options.noDereference) {

            /* Start progress indicator:
                We might be running server-side; show a progress indicator
                until we're able to make the XHR request in clientInit. */

            this.type = utils.getPropertyPath(field, [ 'type', 'type' ]);

            return (
                '<div id="' + id + '" class="embedded form">' +
                    '<div class="spinner" />' +
                '</div>'
            );
        } else {

            /* Not a reference type:
                Go ahead and render the full form synchronously. */

            this.type = this.options.type;

            return (
                '<div id="' + id + '" class="embedded form">' +
                    '<form>' +
                        this.renderEmbedded(value, errors) +
                    '</form>' +
                '</div>'
            );
        }
    };

    w.clientInit = function (field, path, value, raw, errors, options) {

        this.cacheInit();
        this.field = field;
        this.render_options = (options || {});

        if (this.is_reference) {

            /* Dereference document:
                Since we were provided with a reference type, we
                need to use the reference's ref or id attribute to
                look up the actual value to be used while rendering
                the form. This has to be asynchronous, so we do it here. */

            var document_id = (value.ref || value._id);

            var appdb = db.use(duality ? duality.getDBURL(): '/');
            appdb.getDoc(
                document_id,
                utils.bindContext(this, function (err, rv) {
                    if (err) {
                        throw new Error(
                            'Unable to locate the identifier `' +
                                document_id + '`, referenced from the' +
                                ' reference document `' + value._id + '`'
                        );
                    }

                    /* Render form:
                        The enclosing div already exists; include the form
                        element, since we're replacing the whole contents. */

                    var container_elt = this.discoverContainerElement(path);

                    $(container_elt).html(
                        this.renderEmbedded(rv, errors)
                    );
                })
            );
        }
    };

    w.getValue = function (elt, path, options) {

        this.validate(elt, path, options);
        return this.parsed_value;
    };

    w.validate = function (elt, path, options) {

        var f = this.form;
        var container_elt = this.discoverContainerElement(path);
        var form_elt = container_elt.closestChild('form');

        f.validate({
            form: this.serialize(form_elt),
            userCtx: session.userCtx
        });

        if (f.errors.length <= 0) {
            this.parsed_value = this.form.values;
        } else {
            this.parsed_value = undefined;
        }

        return f.errors;
    };

    /** private: **/

    w.cacheInit = function () {
        this.discoverContainerElement = this._discoverContainerElement;
    };

    w.serialize = function (form_elt) {
        return querystring.parse(
            form_elt.serialize().replace(/\+/g, '%20')
        );
    };

    w._discoverContainerElement = function (path) {
        var id = this._id(
            path, 'form', this.render_options.offset,
                this.render_options.path_extra
        );
        return $('#' + id);
    };

    w.renderEmbedded = function (value, errors) {

        this.form = new forms.Form(this.type);
        this.form.values = value;
        
        if(errors) {
            this.form.errors = errors;
        }

        var html = (
            '<form>' +
            this.form.toHTML(
                null, render.defaultRenderer(),
                    this.render_options, true /* create defaults */
            ) +
            '</form>'
        );

        return html;
    };

    return w;
};


})};

/********** couchtypes/widgets.jquery **********/

kanso.moduleCache["couchtypes/widgets.jquery"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Module dependencies
 */

var utils = require('couchtypes/utils');


/* 
 * closestChild for jQuery
 * Copyright 2011, Tobias Lindig
 * 
 * Dual licensed under the MIT license and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.opensource.org/licenses/gpl-license.php
 * 
 */

if (utils.isBrowser()) {
    (function ($) {
        $.fn.closestChild = function (selector) {
            /* Breadth-first search for the first matched node */
            if (selector && selector !== '') {
                var queue = [];
                queue.push(this);
                while (queue.length > 0) {
                    var node = queue.shift();
                    var children = node.children();
                    for (var i = 0; i < children.length; ++i) {
                        var child = $(children[i]);
                        if (child.is(selector)) {
                            return child;
                        }
                        queue.push(child);
                    }
                }
            }
            return $(); /* Nothing found */
        };
    }($));
}



})};

/********** couchtypes/widgets **********/

kanso.moduleCache["couchtypes/widgets"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Widgets define the way a Field object is displayed when rendered as part of a
 * Form. Changing a Field's widget will be reflected in the admin app.
 *
 * @module
 */

var _ = require('underscore')._;

var modules = [
    './widgets.core', './widgets.embed',
    './widgets.selector', './widgets.jquery'
];

_.reduce(modules, function (a, m) {
    return _.extend(a, require(m));
}, exports);



})};

/********** couchtypes/widgets.selector **********/

kanso.moduleCache["couchtypes/widgets.selector"] = {load: (function (module, exports, require) {

/*global $: false, kanso: true*/

/**
 * Widgets define the way a Field object is displayed when rendered as part of a
 * Form. Changing a Field's widget will be reflected in the admin app.
 *
 * @module
 */

/**
 * Module dependencies
 */

var core = require('./widgets.core'),
    db = require('db'),
    settings = require('settings/root'),
    sanitize = require('sanitize'),
    utils = require('couchtypes/utils'),
    _ = require('underscore')._,
    duality;


try {
    duality = require('duality/core');
}
catch (e) {
    // may not be available
}


var h = sanitize.escapeHtml,
    css = sanitize.escapeAttributeSelectorValue;


/**
 * Creates a new document selector widget. This widget allows the
 * user to select a document from a CouchDB view (specified in options).
 * The options available for this widget are explained briefly below:
 *
 * <table class="options">
 *   <tr>
 *      <td class="name">viewName</td>
 *      <td class="type">String</td>
 *      <td class="description">
 *          The name of the CouchDB view that you'd like to select
 *          documents from. If this option is not specified, it will
 *          look for a view with the same name as this widget's field.
 *      </td>
 *   </tr>
 *   <tr>
 *      <td class="name">db</td>
 *      <td class="type">String</td>
 *      <td class="description">
 *          The CouchDB database containing the view for this widget. If
 *          this option is not specified, the current database will be used.
 *      </td>
 *   </tr>
 *   <tr>
 *      <td class="name">useJSON</td>
 *      <td class="type">String</td>
 *      <td class="description">
 *          Set this option to false if this widget should yield a string
 *          containing a single document id. Set this option to true (the
 *          default) to yield a JSON string.
 *      </td>
 *   </tr>
 *   <tr>
 *      <td class="name">storeEntireDocument</td>
 *      <td class="type">String</td>
 *      <td class="description">
 *          Set this option to false if this widget should yield *only*
 *          a document identifier, effectively storing a reference to a
 *          document. Set this option to true (the default) to include
 *          all fields from the selected document. If useJSON is false,
 *          then this option is ignored and treated as if it were false.
 *      </td>
 *   </tr>
 *   <tr>
 *      <td class="name">optionDesc</td>
 *      <td class="type">Function</td>
 *      <td class="description">
 *          Pass in another function to help with rendering of the &lt;option&gt;
 *          html element.  The only param to this function is the row that is
 *          fetched from the view. This is the text displayed in the select box. 
 *      </td>
 *   </tr>
 * </table>
 *
 * @constructor
 * @param options
 */

exports.documentSelector = function (_options) {
    var w = new core.Widget('documentSelector', _options);

    w.options = _.defaults(_options || {}, {
        useJSON: true,
        storeEntireDocument: true
    });

    w.toHTML = function (name, value, raw, field, options) {
        this.cacheInit();

        var html_name = this._name(
            name, options.offset
        );
        var container_id = this._id(
            name, 'widget', options.offset, options.path_extra
        );
        var select_id = this._id(
            name, options.offset, options.path_extra
        );
        var select_html = (
            '<select class="document-selector" id="' + select_id +
                '" name="' + html_name + '" />'
        );
        var html = (
            '<div id="' + container_id + '"' +
                ' class="document-selector widget">' +
                select_html +
                '<div class="spinner" style="display: none;" />' +
            '</div>'
        );

        return html;
    };

    w.updateName = function (elt, path, options) {
        this.cacheInit();
        var select_elt = this.discoverSelectionElement(elt);

        select_elt.attr('id', this._id(
            path, options.offset, options.path_extra
        ));
        select_elt.attr('name', this._name(
            path, options.offset
        ));
    };

    w.updateValue = function (elt, path, value, options) {
        var new_value = value;
        var select_elt = this.discoverSelectionElement(elt);

        if (this.options.useJSON) {
            new_value = this._stringify_value(new_value);
        }

        /* Update <select> element contents, if necessary:
            If we're storing a JSON-encoded object, then we need to
            modify the <option> affected by the value change. This
            ensures that the selected item remains selected, despite
            changes to other fields (and possible property reordering). */

        if (this.options.useJSON) {
            if (value && value._id) {
                var selector = (
                    'option[rel="' + css(
                        (this.useReferenceKey() ? value.ref : value._id)
                    ) + '"]'
                );
                var option_elt = $(selector, select_elt);
                option_elt.val(new_value);
            }
        }

        select_elt.val(new_value);
    };

    w.getValue = function (elt, path, options) {
        var select_elt = this.discoverSelectionElement(elt);
        return this._parse_value(select_elt.val());
    };

    w.clientInit = function (field, path, value, raw, errors, options) {
        var id = this._id(
            path, 'widget', options.offset, options.path_extra
        );
        var container_elt = $('#' + id);
        var widget_options = (this.options || {});
        var spinner_elt = container_elt.closestChild('.spinner');
        var select_elt = this.discoverSelectionElement(container_elt);

        /* Start progress */
        spinner_elt.show();

        /* Load options from view */
        this.populateSelectElement(
            container_elt, field, path, value, widget_options, function () {
                spinner_elt.hide();
            }
        );
    };

    /** private: **/

    w.populateSelectElement = function (container_elt, field,
                                        path, val, options, callback) {
        var select_elt =
            this.discoverSelectionElement(container_elt);

        var appdb = db.use(options.db || duality ? duality.getDBURL(): '/');
        appdb.getView(
            options.appname || settings.name,
            options.viewName,
            { include_docs: options.storeEntireDocument },
            { useCache: true, db: options.db, appName: options.appName },

            utils.bindContext(this, function (err, rv) {
                /* Error handling for getView */
                if (err) {
                    throw new Error(
                        'Failed to request content from view `' +
                            options.viewName + '`'
                    );
                }

                /* Option element for 'no selection' */
                var nil_option = $('<option />');
                if (!val) {
                    nil_option.attr('selected', 'selected');
                }
                select_elt.append(nil_option);

                /* All other option elements */
                _.each(rv.rows || [], utils.bindContext(this, function (r) {
                    var option_elt = $('<option />');

                    if (this.isOptionSelected(r, val, options)) {
                        option_elt.attr('selected', 'selected');
                    }
                    this.generateOptionValue(
                        field, r, val, options,
                        utils.bindContext(this, function (err, v) {
                            if (err) {
                                throw new Error(
                                    'Failed to generate uuid for' +
                                        ' field `' + this._name(path) + '`'
                                );
                            }
                            /* Insert new <option> */
                            option_elt.val(v);
                            if (options.optionDesc) {
                                option_elt.text(options.optionDesc(r));
                            } else {
                                option_elt.text(r.value);
                            }
                            option_elt.attr('rel', r.id);
                            select_elt.append(option_elt);
                        })
                    );
                }));

                /* Finished:
                    Flow will transfer back to clientInit. */

                callback();
            })
        );
    };

    w.useReferenceKey = function () {
        return (
            this.options.useJSON && !this.options.unique &&
                !this.options.storeEntireDocument
        );
    };

    w.isOptionSelected = function (row, value, options) {
        if (options.useJSON) {
            if (this.useReferenceKey()) {
                return ((value || {}).ref === row.id);
            } else {
                return ((value || {})._id === row.id);
            }
        } else {
            return (value === row.id);
        }
    };

    w.generateOptionValue = function (field, row, value, options, callback) {
        if (options.useJSON) {
            if (options.storeEntireDocument) {

                /* Embed actual document:
                    Duplicates are automatically disallowed. */

                callback(false, JSON.stringify(row.doc));

            } else if (options.unique) {

                /* Reference, duplicates disallowed:
                    Store id inside of _id attribute. */

                callback(false, JSON.stringify({
                    _id: row.id,
                    type: field.type.name
                }));

            } else {

                /* Reference, duplicates allowed:
                    Store id inside of the ref attribute, and generate
                    a new UUID for the _id attribute if it's necessary. */

                var return_value = function (uuid) {
                    callback(false, JSON.stringify({
                        _id: uuid,
                        ref: row.id,
                        type: field.type.name
                    }));
                };

                var forward_error = function (err) {
                    callback(err, null);
                };

                if (value && value._id) {
                    return_value(value._id);
                } else {
                    db.newUUID(100, function (err, uuid) {
                        if (err) {
                            forward_error(err);
                        } else {
                            return_value(uuid);
                        }
                    });
                }
            }
        } else {

            /* Not using JSON:
                The type is a scalar; just use the _id. */

            callback(false, row.id);
        }

        return this;
    };

    w.cacheInit = function () {
        this.discoverSelectionElement = this._discoverSelectionElement;
    };

    w._discoverSelectionElement = function (container_elt) {
        return $(container_elt).closestChild('select.document-selector');
    };

    return w;
};



})};

/********** lib/app **********/

kanso.moduleCache["lib/app"] = {load: (function (module, exports, require) {

/**
 * Values exported from this module will automatically be used to generate
 * the design doc pushed to CouchDB.
 */

module.exports = {
    types: require('./types'),
    shows: require('./shows'),
    lists: require('./lists'),
    views: require('./views'),
    updates: require('./updates'),
    filters: require('./filters'),
    rewrites: require('./rewrites'),
    validate_doc_update: require('./validate')
};

// bind event handlers
require('./events');


})};

/********** lib/events **********/

kanso.moduleCache["lib/events"] = {load: (function (module, exports, require) {

/**
 * Bindings to Kanso events
 */

var duality_events = require('duality/events'),
    session = require('session');


/**
 * The init method fires when the app is initially loaded from a page rendered
 * by CouchDB.
 */

/**
 * duality_events.on('init', function () {
 *     // app initialization code goes here...
 * });
 */


/**
 * The sessionChange event fires when the app is first loaded and the user's
 * session information becomes available. It is also fired whenever a change
 * to the user's session is detected, for example after logging in or out.
 */

/**
 * session.on('change', function (userCtx, req) {
 *     // session change handling code goes here...
 * });
 */


/**
 * The updateFailure event fires when an update function returns a document as
 * the first part of an array, but the client-side request to update the
 * document fails.
 */

duality_events.on('updateFailure', function (err, info, req, res, doc) {
    alert(err.message || err.toString());
});


})};

/********** lib/filters **********/

kanso.moduleCache["lib/filters"] = {load: (function (module, exports, require) {

/**
 * Filter functions to be exported from the design doc.
 */


})};

/********** lib/rewrites **********/

kanso.moduleCache["lib/rewrites"] = {load: (function (module, exports, require) {

/**
 * Rewrite settings to be exported from the design doc
 */

module.exports = [
    {from: '/static/*', to: 'static/*'},
    {from: '/', to: '_show/welcome'},
    {from: '*', to: '_show/not_found'}
];


})};

/********** lib/lists **********/

kanso.moduleCache["lib/lists"] = {load: (function (module, exports, require) {

/**
 * List functions to be exported from the design doc.
 */


})};

/********** lib/shows **********/

kanso.moduleCache["lib/shows"] = {load: (function (module, exports, require) {

/**
 * Show functions to be exported from the design doc.
 */

var templates = require('duality/templates');


exports.welcome = function (doc, req) {
    return {
        title: 'The Blinking Project',
        content: templates.render('welcome.html', req, {})
    };
};

exports.not_found = function (doc, req) {
    return {
        title: '404 - Not Found',
        content: templates.render('404.html', req, {})
    };
};


})};

/********** lib/updates **********/

kanso.moduleCache["lib/updates"] = {load: (function (module, exports, require) {

/**
 * Update functions to be exported from the design doc.
 */


})};

/********** lib/types **********/

kanso.moduleCache["lib/types"] = {load: (function (module, exports, require) {

/**
 * Kanso document types to export
 */


})};

/********** lib/validate **********/

kanso.moduleCache["lib/validate"] = {load: (function (module, exports, require) {

/**
 * The validate_doc_update function to be exported from the design doc.
 */

var types = require('couchtypes/types'),
    app_types = require('./types');


module.exports = function (newDoc, oldDoc, userCtx) {
    types.validate_doc_update(app_types, newDoc, oldDoc, userCtx);
};


})};

/********** lib/views **********/

kanso.moduleCache["lib/views"] = {load: (function (module, exports, require) {

/**
 * Views to be exported from the design doc.
 */


})};

/********** settings/packages/tbp **********/

kanso.moduleCache["settings/packages/tbp"] = {load: (function (module, exports, require) {

module.exports = {"name":"tbp","version":"0.0.1","description":"My new CouchApp","load":"lib/app","modules":"lib","attachments":"static","handlebars":{"templates":"templates"},"duality":{"base_template":"base.html"},"dependencies":{"modules":null,"properties":null,"attachments":null,"settings":null,"duality":null,"handlebars":null,"handlebars-helpers":null,"duality-handlebars":null,"couchtypes":null},"minify":false,"open":false};

})};

/********** settings/root **********/

kanso.moduleCache["settings/root"] = {load: (function (module, exports, require) {

module.exports = require("settings/packages/tbp");

})};


