Resources/js/can.jquery-1.1.6.js
changeset 0 80c32ef237c6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/js/can.jquery-1.1.6.js	Mon Sep 02 02:22:21 2013 -0700
@@ -0,0 +1,4538 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Mon, 15 Jul 2013 04:14:43 GMT
+ * Licensed MIT
+ * Includes: can/construct/construct.js,can/observe/observe.js,can/observe/compute/compute.js,can/model/model.js,can/view/view.js,can/view/ejs/ejs.js,can/control/control.js,can/route/route.js,can/control/route/route.js,can/observe/backup/backup.js,can/util/object/object.js,can/util/string/string.js
+ * Download from: http://bitbuilder.herokuapp.com/can.custom.js?configuration=jquery&plugins=can%2Fconstruct%2Fconstruct.js&plugins=can%2Fobserve%2Fobserve.js&plugins=can%2Fobserve%2Fcompute%2Fcompute.js&plugins=can%2Fmodel%2Fmodel.js&plugins=can%2Fview%2Fview.js&plugins=can%2Fview%2Fejs%2Fejs.js&plugins=can%2Fcontrol%2Fcontrol.js&plugins=can%2Froute%2Froute.js&plugins=can%2Fcontrol%2Froute%2Froute.js&plugins=can%2Fobserve%2Fbackup%2Fbackup.js&plugins=can%2Futil%2Fobject%2Fobject.js&plugins=can%2Futil%2Fstring%2Fstring.js
+ */
+(function(undefined) {
+
+    // ## can/util/can.js
+    var __m5 = (function() {
+        var can = window.can || {};
+        if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) {
+            window.can = can;
+        }
+
+        can.isDeferred = function(obj) {
+            var isFunction = this.isFunction;
+            // Returns `true` if something looks like a deferred.
+            return obj && isFunction(obj.then) && isFunction(obj.pipe);
+        };
+
+        var cid = 0;
+        can.cid = function(object, name) {
+            if (object._cid) {
+                return object._cid
+            } else {
+                return object._cid = (name || "") + (++cid)
+            }
+        }
+        can.VERSION = '@EDGE';
+        return can;
+    })();
+
+    // ## can/util/array/each.js
+    var __m6 = (function(can) {
+        can.each = function(elements, callback, context) {
+            var i = 0,
+                key;
+            if (elements) {
+                if (typeof elements.length === 'number' && elements.pop) {
+                    if (elements.attr) {
+                        elements.attr('length');
+                    }
+                    for (key = elements.length; i < key; i++) {
+                        if (callback.call(context || elements[i], elements[i], i, elements) === false) {
+                            break;
+                        }
+                    }
+                } else if (elements.hasOwnProperty) {
+                    for (key in elements) {
+                        if (elements.hasOwnProperty(key)) {
+                            if (callback.call(context || elements[key], elements[key], key, elements) === false) {
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            return elements;
+        };
+
+        return can;
+    })(__m5);
+
+    // ## can/util/jquery/jquery.js
+    var __m3 = (function($, can) {
+        // _jQuery node list._
+        $.extend(can, $, {
+                trigger: function(obj, event, args) {
+                    if (obj.trigger) {
+                        obj.trigger(event, args);
+                    } else {
+                        $.event.trigger(event, args, obj, true);
+                    }
+                },
+                addEvent: function(ev, cb) {
+                    $([this]).bind(ev, cb);
+                    return this;
+                },
+                removeEvent: function(ev, cb) {
+                    $([this]).unbind(ev, cb);
+                    return this;
+                },
+                // jquery caches fragments, we always needs a new one
+                buildFragment: function(elems, context) {
+                    var oldFragment = $.buildFragment,
+                        ret;
+
+                    elems = [elems];
+                    // Set context per 1.8 logic
+                    context = context || document;
+                    context = !context.nodeType && context[0] || context;
+                    context = context.ownerDocument || context;
+
+                    ret = oldFragment.call(jQuery, elems, context);
+
+                    return ret.cacheable ? $.clone(ret.fragment) : ret.fragment || ret;
+                },
+                $: $,
+                each: can.each
+            });
+
+        // Wrap binding functions.
+        $.each(['bind', 'unbind', 'undelegate', 'delegate'], function(i, func) {
+            can[func] = function() {
+                var t = this[func] ? this : $([this]);
+                t[func].apply(t, arguments);
+                return this;
+            };
+        });
+
+        // Wrap modifier functions.
+        $.each(["append", "filter", "addClass", "remove", "data", "get"], function(i, name) {
+            can[name] = function(wrapped) {
+                return wrapped[name].apply(wrapped, can.makeArray(arguments).slice(1));
+            };
+        });
+
+        // Memory safe destruction.
+        var oldClean = $.cleanData;
+
+        $.cleanData = function(elems) {
+            $.each(elems, function(i, elem) {
+                if (elem) {
+                    can.trigger(elem, "destroyed", [], false);
+                }
+            });
+            oldClean(elems);
+        };
+
+        return can;
+    })(jQuery, __m5, __m6);
+
+    // ## can/util/string/string.js
+    var __m2 = (function(can) {
+        // ##string.js
+        // _Miscellaneous string utility functions._  
+
+        // Several of the methods in this plugin use code adapated from Prototype
+        // Prototype JavaScript framework, version 1.6.0.1.
+        // © 2005-2007 Sam Stephenson
+        var strUndHash = /_|-/,
+            strColons = /\=\=/,
+            strWords = /([A-Z]+)([A-Z][a-z])/g,
+            strLowUp = /([a-z\d])([A-Z])/g,
+            strDash = /([a-z\d])([A-Z])/g,
+            strReplacer = /\{([^\}]+)\}/g,
+            strQuote = /"/g,
+            strSingleQuote = /'/g,
+
+            // Returns the `prop` property from `obj`.
+            // If `add` is true and `prop` doesn't exist in `obj`, create it as an
+            // empty object.
+            getNext = function(obj, prop, add) {
+                var result = obj[prop];
+
+                if (result === undefined && add === true) {
+                    result = obj[prop] = {}
+                }
+                return result
+            },
+
+            // Returns `true` if the object can have properties (no `null`s).
+            isContainer = function(current) {
+                return (/^f|^o/).test(typeof current);
+            };
+
+        can.extend(can, {
+                // Escapes strings for HTML.
+
+                esc: function(content) {
+                    // Convert bad values into empty strings
+                    var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN'));
+                    return ("" + (isInvalid ? '' : content))
+                        .replace(/&/g, '&amp;')
+                        .replace(/</g, '&lt;')
+                        .replace(/>/g, '&gt;')
+                        .replace(strQuote, '&#34;')
+                        .replace(strSingleQuote, "&#39;");
+                },
+
+
+                getObject: function(name, roots, add) {
+
+                    // The parts of the name we are looking up
+                    // `['App','Models','Recipe']`
+                    var parts = name ? name.split('.') : [],
+                        length = parts.length,
+                        current,
+                        r = 0,
+                        i, container, rootsLength;
+
+                    // Make sure roots is an `array`.
+                    roots = can.isArray(roots) ? roots : [roots || window];
+
+                    rootsLength = roots.length
+
+                    if (!length) {
+                        return roots[0];
+                    }
+
+                    // For each root, mark it as current.
+                    for (r; r < rootsLength; r++) {
+                        current = roots[r];
+                        container = undefined;
+
+                        // Walk current to the 2nd to last object or until there
+                        // is not a container.
+                        for (i = 0; i < length && isContainer(current); i++) {
+                            container = current;
+                            current = getNext(container, parts[i]);
+                        }
+
+                        // If we found property break cycle
+                        if (container !== undefined && current !== undefined) {
+                            break
+                        }
+                    }
+
+                    // Remove property from found container
+                    if (add === false && current !== undefined) {
+                        delete container[parts[i - 1]]
+                    }
+
+                    // When adding property add it to the first root
+                    if (add === true && current === undefined) {
+                        current = roots[0]
+
+                        for (i = 0; i < length && isContainer(current); i++) {
+                            current = getNext(current, parts[i], true);
+                        }
+                    }
+
+                    return current;
+                },
+                // Capitalizes a string.
+
+                capitalize: function(s, cache) {
+                    // Used to make newId.
+                    return s.charAt(0).toUpperCase() + s.slice(1);
+                },
+
+                // Underscores a string.
+
+                underscore: function(s) {
+                    return s
+                        .replace(strColons, '/')
+                        .replace(strWords, '$1_$2')
+                        .replace(strLowUp, '$1_$2')
+                        .replace(strDash, '_')
+                        .toLowerCase();
+                },
+                // Micro-templating.
+
+                sub: function(str, data, remove) {
+                    var obs = [];
+
+                    str = str || '';
+
+                    obs.push(str.replace(strReplacer, function(whole, inside) {
+
+                                // Convert inside to type.
+                                var ob = can.getObject(inside, data, remove === true ? false : undefined);
+
+                                if (ob === undefined) {
+                                    obs = null;
+                                    return "";
+                                }
+
+                                // If a container, push into objs (which will return objects found).
+                                if (isContainer(ob) && obs) {
+                                    obs.push(ob);
+                                    return "";
+                                }
+
+                                return "" + ob;
+                            }));
+
+                    return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs);
+                },
+
+                // These regex's are used throughout the rest of can, so let's make
+                // them available.
+                replacer: strReplacer,
+                undHash: strUndHash
+            });
+        return can;
+    })(__m3);
+
+    // ## can/construct/construct.js
+    var __m1 = (function(can) {
+
+        // ## construct.js
+        // `can.Construct`  
+        // _This is a modified version of
+        // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).  
+        // It provides class level inheritance and callbacks._
+
+        // A private flag used to initialize a new class instance without
+        // initializing it's bindings.
+        var initializing = 0;
+
+
+        can.Construct = function() {
+            if (arguments.length) {
+                return can.Construct.extend.apply(can.Construct, arguments);
+            }
+        };
+
+
+        can.extend(can.Construct, {
+
+                newInstance: function() {
+                    // Get a raw instance object (`init` is not called).
+                    var inst = this.instance(),
+                        arg = arguments,
+                        args;
+
+                    // Call `setup` if there is a `setup`
+                    if (inst.setup) {
+                        args = inst.setup.apply(inst, arguments);
+                    }
+
+                    // Call `init` if there is an `init`  
+                    // If `setup` returned `args`, use those as the arguments
+                    if (inst.init) {
+                        inst.init.apply(inst, args || arguments);
+                    }
+
+                    return inst;
+                },
+                // Overwrites an object with methods. Used in the `super` plugin.
+                // `newProps` - New properties to add.  
+                // `oldProps` - Where the old properties might be (used with `super`).  
+                // `addTo` - What we are adding to.
+                _inherit: function(newProps, oldProps, addTo) {
+                    can.extend(addTo || newProps, newProps || {})
+                },
+                // used for overwriting a single property.
+                // this should be used for patching other objects
+                // the super plugin overwrites this
+                _overwrite: function(what, oldProps, propName, val) {
+                    what[propName] = val;
+                },
+                // Set `defaults` as the merger of the parent `defaults` and this 
+                // object's `defaults`. If you overwrite this method, make sure to
+                // include option merging logic.
+
+                setup: function(base, fullName) {
+                    this.defaults = can.extend(true, {}, base.defaults, this.defaults);
+                },
+                // Create's a new `class` instance without initializing by setting the
+                // `initializing` flag.
+                instance: function() {
+
+                    // Prevents running `init`.
+                    initializing = 1;
+
+                    var inst = new this();
+
+                    // Allow running `init`.
+                    initializing = 0;
+
+                    return inst;
+                },
+                // Extends classes.
+
+                extend: function(fullName, klass, proto) {
+                    // Figure out what was passed and normalize it.
+                    if (typeof fullName != 'string') {
+                        proto = klass;
+                        klass = fullName;
+                        fullName = null;
+                    }
+
+                    if (!proto) {
+                        proto = klass;
+                        klass = null;
+                    }
+                    proto = proto || {};
+
+                    var _super_class = this,
+                        _super = this.prototype,
+                        name, shortName, namespace, prototype;
+
+                    // Instantiate a base class (but only create the instance,
+                    // don't run the init constructor).
+                    prototype = this.instance();
+
+                    // Copy the properties over onto the new prototype.
+                    can.Construct._inherit(proto, _super, prototype);
+
+                    // The dummy class constructor.
+
+                    function Constructor() {
+                        // All construction is actually done in the init method.
+                        if (!initializing) {
+                            return this.constructor !== Constructor && arguments.length ?
+                            // We are being called without `new` or we are extending.
+                            arguments.callee.extend.apply(arguments.callee, arguments) :
+                            // We are being called with `new`.
+                            this.constructor.newInstance.apply(this.constructor, arguments);
+                        }
+                    }
+
+                    // Copy old stuff onto class (can probably be merged w/ inherit)
+                    for (name in _super_class) {
+                        if (_super_class.hasOwnProperty(name)) {
+                            Constructor[name] = _super_class[name];
+                        }
+                    }
+
+                    // Copy new static properties on class.
+                    can.Construct._inherit(klass, _super_class, Constructor);
+
+                    // Setup namespaces.
+                    if (fullName) {
+
+                        var parts = fullName.split('.'),
+                            shortName = parts.pop(),
+                            current = can.getObject(parts.join('.'), window, true),
+                            namespace = current,
+                            _fullName = can.underscore(fullName.replace(/\./g, "_")),
+                            _shortName = can.underscore(shortName);
+
+
+
+                        current[shortName] = Constructor;
+                    }
+
+                    // Set things that shouldn't be overwritten.
+                    can.extend(Constructor, {
+                            constructor: Constructor,
+                            prototype: prototype,
+
+                            namespace: namespace,
+
+                            _shortName: _shortName,
+
+                            fullName: fullName,
+                            _fullName: _fullName
+                        });
+
+                    // Dojo and YUI extend undefined
+                    if (shortName !== undefined) {
+                        Constructor.shortName = shortName;
+                    }
+
+                    // Make sure our prototype looks nice.
+                    Constructor.prototype.constructor = Constructor;
+
+
+                    // Call the class `setup` and `init`
+                    var t = [_super_class].concat(can.makeArray(arguments)),
+                        args = Constructor.setup.apply(Constructor, t);
+
+                    if (Constructor.init) {
+                        Constructor.init.apply(Constructor, args || t);
+                    }
+
+
+                    return Constructor;
+
+                }
+
+            });
+        return can.Construct;
+    })(__m2);
+
+    // ## can/util/bind/bind.js
+    var __m8 = (function(can) {
+
+
+        // ## Bind helpers
+        can.bindAndSetup = function() {
+            // Add the event to this object
+            can.addEvent.apply(this, arguments);
+            // If not initializing, and the first binding
+            // call bindsetup if the function exists.
+            if (!this._init) {
+                if (!this._bindings) {
+                    this._bindings = 1;
+                    // setup live-binding
+                    this._bindsetup && this._bindsetup();
+
+                } else {
+                    this._bindings++;
+                }
+
+            }
+
+            return this;
+        };
+
+        can.unbindAndTeardown = function(ev, handler) {
+            // Remove the event handler
+            can.removeEvent.apply(this, arguments);
+
+            this._bindings--;
+            // If there are no longer any bindings and
+            // there is a bindteardown method, call it.
+            if (!this._bindings) {
+                this._bindteardown && this._bindteardown();
+            }
+            return this;
+        }
+
+        return can;
+
+    })(__m3);
+
+    // ## can/observe/observe.js
+    var __m7 = (function(can, bind) {
+        // ## observe.js  
+        // `can.Observe`  
+        // _Provides the observable pattern for JavaScript Objects._  
+        // Returns `true` if something is an object with properties of its own.
+        var canMakeObserve = function(obj) {
+            return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject(obj) || (obj instanceof can.Observe));
+        },
+
+            // Removes all listeners.
+            unhookup = function(items, namespace) {
+                return can.each(items, function(item) {
+                    if (item && item.unbind) {
+                        item.unbind("change" + namespace);
+                    }
+                });
+            },
+            // Listens to changes on `child` and "bubbles" the event up.  
+            // `child` - The object to listen for changes on.  
+            // `prop` - The property name is at on.  
+            // `parent` - The parent object of prop.
+            // `ob` - (optional) The Observe object constructor
+            // `list` - (optional) The observable list constructor
+            hookupBubble = function(child, prop, parent, Ob, List) {
+                Ob = Ob || Observe;
+                List = List || Observe.List;
+
+                // If it's an `array` make a list, otherwise a child.
+                if (child instanceof Observe) {
+                    // We have an `observe` already...
+                    // Make sure it is not listening to this already
+                    // It's only listening if it has bindings already.
+                    parent._bindings && unhookup([child], parent._cid);
+                } else if (can.isArray(child)) {
+                    child = new List(child);
+                } else {
+                    child = new Ob(child);
+                }
+                // only listen if something is listening to you
+                if (parent._bindings) {
+                    // Listen to all changes and `batchTrigger` upwards.
+                    bindToChildAndBubbleToParent(child, prop, parent)
+                }
+
+
+                return child;
+            },
+            bindToChildAndBubbleToParent = function(child, prop, parent) {
+                child.bind("change" + parent._cid, function() {
+                    // `batchTrigger` the type on this...
+                    var args = can.makeArray(arguments),
+                        ev = args.shift();
+                    args[0] = (prop === "*" ? [parent.indexOf(child), args[0]] : [prop, args[0]]).join(".");
+
+                    // track objects dispatched on this observe		
+                    ev.triggeredNS = ev.triggeredNS || {};
+
+                    // if it has already been dispatched exit
+                    if (ev.triggeredNS[parent._cid]) {
+                        return;
+                    }
+
+                    ev.triggeredNS[parent._cid] = true;
+                    // send change event with modified attr to parent	
+                    can.trigger(parent, ev, args);
+                    // send modified attr event to parent
+                    //can.trigger(parent, args[0], args);
+                });
+            }
+            // An `id` to track events for a given observe.
+        observeId = 0,
+        // A helper used to serialize an `Observe` or `Observe.List`.  
+        // `observe` - The observable.  
+        // `how` - To serialize with `attr` or `serialize`.  
+        // `where` - To put properties, in an `{}` or `[]`.
+        serialize = function(observe, how, where) {
+            // Go through each property.
+            observe.each(function(val, name) {
+                // If the value is an `object`, and has an `attrs` or `serialize` function.
+                where[name] = canMakeObserve(val) && can.isFunction(val[how]) ?
+                // Call `attrs` or `serialize` to get the original data back.
+                val[how]() :
+                // Otherwise return the value.
+                val;
+            });
+            return where;
+        },
+        attrParts = function(attr, keepKey) {
+            if (keepKey) {
+                return [attr];
+            }
+            return can.isArray(attr) ? attr : ("" + attr).split(".");
+        },
+        // Which batch of events this is for -- might not want to send multiple
+        // messages on the same batch.  This is mostly for event delegation.
+        batchNum = 1,
+        // how many times has start been called without a stop
+        transactions = 0,
+        // an array of events within a transaction
+        batchEvents = [],
+        stopCallbacks = [],
+        makeBindSetup = function(wildcard) {
+            return function() {
+                var parent = this;
+                this._each(function(child, prop) {
+                    if (child && child.bind) {
+                        bindToChildAndBubbleToParent(child, wildcard || prop, parent)
+                    }
+                })
+            };
+        };
+
+
+        var Observe = can.Map = can.Observe = can.Construct({
+
+                // keep so it can be overwritten
+                bind: can.bindAndSetup,
+                unbind: can.unbindAndTeardown,
+                id: "id",
+                canMakeObserve: canMakeObserve,
+                // starts collecting events
+                // takes a callback for after they are updated
+                // how could you hook into after ejs
+
+                startBatch: function(batchStopHandler) {
+                    transactions++;
+                    batchStopHandler && stopCallbacks.push(batchStopHandler);
+                },
+
+                stopBatch: function(force, callStart) {
+                    if (force) {
+                        transactions = 0;
+                    } else {
+                        transactions--;
+                    }
+
+                    if (transactions == 0) {
+                        var items = batchEvents.slice(0),
+                            callbacks = stopCallbacks.slice(0);
+                        batchEvents = [];
+                        stopCallbacks = [];
+                        batchNum++;
+                        callStart && this.startBatch();
+                        can.each(items, function(args) {
+                            can.trigger.apply(can, args);
+                        });
+                        can.each(callbacks, function(cb) {
+                            cb();
+                        });
+                    }
+                },
+
+                triggerBatch: function(item, event, args) {
+                    // Don't send events if initalizing.
+                    if (!item._init) {
+                        if (transactions == 0) {
+                            return can.trigger(item, event, args);
+                        } else {
+                            event = typeof event === "string" ? {
+                                type: event
+                            } :
+                                event;
+                            event.batchNum = batchNum;
+                            batchEvents.push([
+                                    item,
+                                    event,
+                                    args
+                                ]);
+                        }
+                    }
+                },
+
+                keys: function(observe) {
+                    var keys = [];
+                    Observe.__reading && Observe.__reading(observe, '__keys');
+                    for (var keyName in observe._data) {
+                        keys.push(keyName);
+                    }
+                    return keys;
+                }
+            },
+
+            {
+                setup: function(obj) {
+                    // `_data` is where we keep the properties.
+                    this._data = {};
+
+                    // The namespace this `object` uses to listen to events.
+                    can.cid(this, ".observe");
+                    // Sets all `attrs`.
+                    this._init = 1;
+                    this.attr(obj);
+                    this.bind('change' + this._cid, can.proxy(this._changes, this));
+                    delete this._init;
+                },
+                _bindsetup: makeBindSetup(),
+                _bindteardown: function() {
+                    var cid = this._cid;
+                    this._each(function(child) {
+                        unhookup([child], cid)
+                    })
+                },
+                _changes: function(ev, attr, how, newVal, oldVal) {
+                    Observe.triggerBatch(this, {
+                            type: attr,
+                            batchNum: ev.batchNum
+                        }, [newVal, oldVal]);
+                },
+                _triggerChange: function(attr, how, newVal, oldVal) {
+                    Observe.triggerBatch(this, "change", can.makeArray(arguments))
+                },
+                // no live binding iterator
+                _each: function(callback) {
+                    var data = this.__get();
+                    for (var prop in data) {
+                        if (data.hasOwnProperty(prop)) {
+                            callback(data[prop], prop)
+                        }
+                    }
+                },
+
+                attr: function(attr, val) {
+                    // This is super obfuscated for space -- basically, we're checking
+                    // if the type of the attribute is not a `number` or a `string`.
+                    var type = typeof attr;
+                    if (type !== "string" && type !== "number") {
+                        return this._attrs(attr, val)
+                    } else if (arguments.length === 1) { // If we are getting a value.
+                        // Let people know we are reading.
+                        Observe.__reading && Observe.__reading(this, attr)
+                        return this._get(attr)
+                    } else {
+                        // Otherwise we are setting.
+                        this._set(attr, val);
+                        return this;
+                    }
+                },
+
+                each: function() {
+                    Observe.__reading && Observe.__reading(this, '__keys');
+                    return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
+                },
+
+                removeAttr: function(attr) {
+                    // Info if this is List or not
+                    var isList = this instanceof can.Observe.List,
+                        // Convert the `attr` into parts (if nested).
+                        parts = attrParts(attr),
+                        // The actual property to remove.
+                        prop = parts.shift(),
+                        // The current value.
+                        current = isList ? this[prop] : this._data[prop];
+
+                    // If we have more parts, call `removeAttr` on that part.
+                    if (parts.length) {
+                        return current.removeAttr(parts)
+                    } else {
+                        if (isList) {
+                            this.splice(prop, 1)
+                        } else if (prop in this._data) {
+                            // Otherwise, `delete`.
+                            delete this._data[prop];
+                            // Create the event.
+                            if (!(prop in this.constructor.prototype)) {
+                                delete this[prop]
+                            }
+                            // Let others know the number of keys have changed
+                            Observe.triggerBatch(this, "__keys");
+                            this._triggerChange(prop, "remove", undefined, current);
+
+                        }
+                        return current;
+                    }
+                },
+                // Reads a property from the `object`.
+                _get: function(attr) {
+                    var value = typeof attr === 'string' && !! ~attr.indexOf('.') && this.__get(attr);
+                    if (value) {
+                        return value;
+                    }
+
+                    // break up the attr (`"foo.bar"`) into `["foo","bar"]`
+                    var parts = attrParts(attr),
+                        // get the value of the first attr name (`"foo"`)
+                        current = this.__get(parts.shift());
+                    // if there are other attributes to read
+                    return parts.length ?
+                    // and current has a value
+                    current ?
+                    // lookup the remaining attrs on current
+                    current._get(parts) :
+                    // or if there's no current, return undefined
+                    undefined :
+                    // if there are no more parts, return current
+                    current;
+                },
+                // Reads a property directly if an `attr` is provided, otherwise
+                // returns the "real" data object itself.
+                __get: function(attr) {
+                    return attr ? this._data[attr] : this._data;
+                },
+                // Sets `attr` prop as value on this object where.
+                // `attr` - Is a string of properties or an array  of property values.
+                // `value` - The raw value to set.
+                _set: function(attr, value, keepKey) {
+                    // Convert `attr` to attr parts (if it isn't already).
+                    var parts = attrParts(attr, keepKey),
+                        // The immediate prop we are setting.
+                        prop = parts.shift(),
+                        // The current value.
+                        current = this.__get(prop);
+
+                    // If we have an `object` and remaining parts.
+                    if (canMakeObserve(current) && parts.length) {
+                        // That `object` should set it (this might need to call attr).
+                        current._set(parts, value)
+                    } else if (!parts.length) {
+                        // We're in "real" set territory.
+                        if (this.__convert) {
+                            value = this.__convert(prop, value)
+                        }
+                        this.__set(prop, value, current)
+                    } else {
+                        throw "can.Observe: Object does not exist"
+                    }
+                },
+                __set: function(prop, value, current) {
+
+                    // Otherwise, we are setting it on this `object`.
+                    // TODO: Check if value is object and transform
+                    // are we changing the value.
+                    if (value !== current) {
+                        // Check if we are adding this for the first time --
+                        // if we are, we need to create an `add` event.
+                        var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
+
+                        // Set the value on data.
+                        this.___set(prop,
+
+                            // If we are getting an object.
+                            canMakeObserve(value) ?
+
+                            // Hook it up to send event.
+                            hookupBubble(value, prop, this) :
+                            // Value is normal.
+                            value);
+
+                        if (changeType == "add") {
+                            // If there is no current value, let others know that
+                            // the the number of keys have changed
+
+                            Observe.triggerBatch(this, "__keys", undefined);
+
+                        }
+                        // `batchTrigger` the change event.
+                        this._triggerChange(prop, changeType, value, current);
+
+                        //Observe.triggerBatch(this, prop, [value, current]);
+                        // If we can stop listening to our old value, do it.
+                        current && unhookup([current], this._cid);
+                    }
+
+                },
+                // Directly sets a property on this `object`.
+                ___set: function(prop, val) {
+                    this._data[prop] = val;
+                    // Add property directly for easy writing.
+                    // Check if its on the `prototype` so we don't overwrite methods like `attrs`.
+                    if (!(prop in this.constructor.prototype)) {
+                        this[prop] = val
+                    }
+                },
+
+
+                bind: can.bindAndSetup,
+
+                unbind: can.unbindAndTeardown,
+
+                serialize: function() {
+                    return serialize(this, 'serialize', {});
+                },
+
+                _attrs: function(props, remove) {
+
+                    if (props === undefined) {
+                        return serialize(this, 'attr', {})
+                    }
+
+                    props = can.extend({}, props);
+                    var prop,
+                        self = this,
+                        newVal;
+                    Observe.startBatch();
+                    this.each(function(curVal, prop) {
+                        newVal = props[prop];
+
+                        // If we are merging...
+                        if (newVal === undefined) {
+                            remove && self.removeAttr(prop);
+                            return;
+                        }
+
+                        if (self.__convert) {
+                            newVal = self.__convert(prop, newVal)
+                        }
+
+                        // if we're dealing with models, want to call _set to let converter run
+                        if (newVal instanceof can.Observe) {
+                            self.__set(prop, newVal, curVal)
+                            // if its an object, let attr merge
+                        } else if (canMakeObserve(curVal) && canMakeObserve(newVal) && curVal.attr) {
+                            curVal.attr(newVal, remove)
+                            // otherwise just set
+                        } else if (curVal != newVal) {
+                            self.__set(prop, newVal, curVal)
+                        }
+
+                        delete props[prop];
+                    })
+                    // Add remaining props.
+                    for (var prop in props) {
+                        newVal = props[prop];
+                        this._set(prop, newVal, true)
+                    }
+                    Observe.stopBatch()
+                    return this;
+                },
+
+
+                compute: function(prop) {
+                    return can.compute(this, prop);
+                }
+            });
+        // Helpers for `observable` lists.
+        var splice = [].splice,
+
+            list = Observe(
+
+                {
+                    setup: function(instances, options) {
+                        this.length = 0;
+                        can.cid(this, ".observe")
+                        this._init = 1;
+                        if (can.isDeferred(instances)) {
+                            this.replace(instances)
+                        } else {
+                            this.push.apply(this, can.makeArray(instances || []));
+                        }
+                        // this change needs to be ignored
+                        this.bind('change' + this._cid, can.proxy(this._changes, this));
+                        can.extend(this, options);
+                        delete this._init;
+                    },
+                    _triggerChange: function(attr, how, newVal, oldVal) {
+
+                        Observe.prototype._triggerChange.apply(this, arguments)
+                        // `batchTrigger` direct add and remove events...
+                        if (!~attr.indexOf('.')) {
+
+                            if (how === 'add') {
+                                Observe.triggerBatch(this, how, [newVal, +attr]);
+                                Observe.triggerBatch(this, 'length', [this.length]);
+                            } else if (how === 'remove') {
+                                Observe.triggerBatch(this, how, [oldVal, +attr]);
+                                Observe.triggerBatch(this, 'length', [this.length]);
+                            } else {
+                                Observe.triggerBatch(this, how, [newVal, +attr])
+                            }
+
+                        }
+
+                    },
+                    __get: function(attr) {
+                        return attr ? this[attr] : this;
+                    },
+                    ___set: function(attr, val) {
+                        this[attr] = val;
+                        if (+attr >= this.length) {
+                            this.length = (+attr + 1)
+                        }
+                    },
+                    _each: function(callback) {
+                        var data = this.__get();
+                        for (var i = 0; i < data.length; i++) {
+                            callback(data[i], i)
+                        }
+                    },
+                    _bindsetup: makeBindSetup("*"),
+                    // Returns the serialized form of this list.
+
+                    serialize: function() {
+                        return serialize(this, 'serialize', []);
+                    },
+
+                    splice: function(index, howMany) {
+                        var args = can.makeArray(arguments),
+                            i;
+
+                        for (i = 2; i < args.length; i++) {
+                            var val = args[i];
+                            if (canMakeObserve(val)) {
+                                args[i] = hookupBubble(val, "*", this, this.constructor.Observe, this.constructor)
+                            }
+                        }
+                        if (howMany === undefined) {
+                            howMany = args[1] = this.length - index;
+                        }
+                        var removed = splice.apply(this, args);
+                        can.Observe.startBatch();
+                        if (howMany > 0) {
+                            this._triggerChange("" + index, "remove", undefined, removed);
+                            unhookup(removed, this._cid);
+                        }
+                        if (args.length > 2) {
+                            this._triggerChange("" + index, "add", args.slice(2), removed);
+                        }
+                        can.Observe.stopBatch();
+                        return removed;
+                    },
+
+                    _attrs: function(items, remove) {
+                        if (items === undefined) {
+                            return serialize(this, 'attr', []);
+                        }
+
+                        // Create a copy.
+                        items = can.makeArray(items);
+
+                        Observe.startBatch();
+                        this._updateAttrs(items, remove);
+                        Observe.stopBatch()
+                    },
+
+                    _updateAttrs: function(items, remove) {
+                        var len = Math.min(items.length, this.length);
+
+                        for (var prop = 0; prop < len; prop++) {
+                            var curVal = this[prop],
+                                newVal = items[prop];
+
+                            if (canMakeObserve(curVal) && canMakeObserve(newVal)) {
+                                curVal.attr(newVal, remove)
+                            } else if (curVal != newVal) {
+                                this._set(prop, newVal)
+                            } else {
+
+                            }
+                        }
+                        if (items.length > this.length) {
+                            // Add in the remaining props.
+                            this.push.apply(this, items.slice(this.length));
+                        } else if (items.length < this.length && remove) {
+                            this.splice(items.length)
+                        }
+                    }
+                }),
+
+            // Converts to an `array` of arguments.
+            getArgs = function(args) {
+                return args[0] && can.isArray(args[0]) ?
+                    args[0] :
+                    can.makeArray(args);
+            };
+        // Create `push`, `pop`, `shift`, and `unshift`
+        can.each({
+
+                push: "length",
+
+                unshift: 0
+            },
+            // Adds a method
+            // `name` - The method name.
+            // `where` - Where items in the `array` should be added.
+
+            function(where, name) {
+                var orig = [][name]
+                list.prototype[name] = function() {
+                    // Get the items being added.
+                    var args = [],
+                        // Where we are going to add items.
+                        len = where ? this.length : 0,
+                        i = arguments.length,
+                        res,
+                        val,
+                        constructor = this.constructor;
+
+                    // Go through and convert anything to an `observe` that needs to be converted.
+                    while (i--) {
+                        val = arguments[i];
+                        args[i] = canMakeObserve(val) ?
+                            hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) :
+                            val;
+                    }
+
+                    // Call the original method.
+                    res = orig.apply(this, args);
+
+                    if (!this.comparator || args.length) {
+
+                        this._triggerChange("" + len, "add", args, undefined);
+                    }
+
+                    return res;
+                }
+            });
+
+        can.each({
+
+                pop: "length",
+
+                shift: 0
+            },
+            // Creates a `remove` type method
+
+            function(where, name) {
+                list.prototype[name] = function() {
+
+                    var args = getArgs(arguments),
+                        len = where && this.length ? this.length - 1 : 0;
+
+                    var res = [][name].apply(this, args)
+
+                    // Create a change where the args are
+                    // `len` - Where these items were removed.
+                    // `remove` - Items removed.
+                    // `undefined` - The new values (there are none).
+                    // `res` - The old, removed values (should these be unbound).
+                    this._triggerChange("" + len, "remove", undefined, [res])
+
+                    if (res && res.unbind) {
+                        res.unbind("change" + this._cid)
+                    }
+                    return res;
+                }
+            });
+
+        can.extend(list.prototype, {
+
+                indexOf: function(item) {
+                    this.attr('length')
+                    return can.inArray(item, this)
+                },
+
+
+                join: [].join,
+
+
+                reverse: [].reverse,
+
+
+                slice: function() {
+                    var temp = Array.prototype.slice.apply(this, arguments);
+                    return new this.constructor(temp);
+                },
+
+
+                concat: function() {
+                    var args = [];
+                    can.each(can.makeArray(arguments), function(arg, i) {
+                        args[i] = arg instanceof can.Observe.List ? arg.serialize() : arg;
+                    });
+                    return new this.constructor(Array.prototype.concat.apply(this.serialize(), args));
+                },
+
+
+                forEach: function(cb, thisarg) {
+                    can.each(this, cb, thisarg || this);
+                },
+
+
+                replace: function(newList) {
+                    if (can.isDeferred(newList)) {
+                        newList.then(can.proxy(this.replace, this));
+                    } else {
+                        this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || [])));
+                    }
+
+                    return this;
+                }
+            });
+
+        can.List = Observe.List = list;
+        Observe.setup = function() {
+            can.Construct.setup.apply(this, arguments);
+            // I would prefer not to do it this way. It should
+            // be using the attributes plugin to do this type of conversion.
+            this.List = Observe.List({
+                    Observe: this
+                }, {});
+        }
+        return Observe;
+    })(__m3, __m8, __m1);
+
+    // ## can/observe/compute/compute.js
+    var __m9 = (function(can, bind) {
+
+        // returns the
+        // - observes and attr methods are called by func
+        // - the value returned by func
+        // ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
+        var getValueAndObserved = function(func, self) {
+
+            var oldReading;
+            if (can.Observe) {
+                // Set a callback on can.Observe to know
+                // when an attr is read.
+                // Keep a reference to the old reader
+                // if there is one.  This is used
+                // for nested live binding.
+                oldReading = can.Observe.__reading;
+                can.Observe.__reading = function(obj, attr) {
+                    // Add the observe and attr that was read
+                    // to `observed`
+                    observed.push({
+                            obj: obj,
+                            attr: attr + ""
+                        });
+                };
+            }
+
+            var observed = [],
+                // Call the "wrapping" function to get the value. `observed`
+                // will have the observe/attribute pairs that were read.
+                value = func.call(self);
+
+            // Set back so we are no longer reading.
+            if (can.Observe) {
+                can.Observe.__reading = oldReading;
+            }
+            return {
+                value: value,
+                observed: observed
+            };
+        },
+            // Calls `callback(newVal, oldVal)` everytime an observed property
+            // called within `getterSetter` is changed and creates a new result of `getterSetter`.
+            // Also returns an object that can teardown all event handlers.
+            computeBinder = function(getterSetter, context, callback, computeState) {
+                // track what we are observing
+                var observing = {},
+                    // a flag indicating if this observe/attr pair is already bound
+                    matched = true,
+                    // the data to return 
+                    data = {
+                        // we will maintain the value while live-binding is taking place
+                        value: undefined,
+                        // a teardown method that stops listening
+                        teardown: function() {
+                            for (var name in observing) {
+                                var ob = observing[name];
+                                ob.observe.obj.unbind(ob.observe.attr, onchanged);
+                                delete observing[name];
+                            }
+                        }
+                    },
+                    batchNum;
+
+                // when a property value is changed
+                var onchanged = function(ev) {
+                    // If the compute is no longer bound (because the same change event led to an unbind)
+                    // then do not call getValueAndBind, or we will leak bindings.
+                    if (computeState && !computeState.bound) {
+                        return;
+                    }
+                    if (ev.batchNum === undefined || ev.batchNum !== batchNum) {
+                        // store the old value
+                        var oldValue = data.value,
+                            // get the new value
+                            newvalue = getValueAndBind();
+
+                        // update the value reference (in case someone reads)
+                        data.value = newvalue;
+                        // if a change happened
+                        if (newvalue !== oldValue) {
+                            callback(newvalue, oldValue);
+                        }
+                        batchNum = batchNum = ev.batchNum;
+                    }
+
+
+                };
+
+                // gets the value returned by `getterSetter` and also binds to any attributes
+                // read by the call
+                var getValueAndBind = function() {
+                    var info = getValueAndObserved(getterSetter, context),
+                        newObserveSet = info.observed;
+
+                    var value = info.value;
+                    matched = !matched;
+
+                    // go through every attribute read by this observe
+                    can.each(newObserveSet, function(ob) {
+                        // if the observe/attribute pair is being observed
+                        if (observing[ob.obj._cid + "|" + ob.attr]) {
+                            // mark at as observed
+                            observing[ob.obj._cid + "|" + ob.attr].matched = matched;
+                        } else {
+                            // otherwise, set the observe/attribute on oldObserved, marking it as being observed
+                            observing[ob.obj._cid + "|" + ob.attr] = {
+                                matched: matched,
+                                observe: ob
+                            };
+                            ob.obj.bind(ob.attr, onchanged);
+                        }
+                    });
+
+                    // Iterate through oldObserved, looking for observe/attributes
+                    // that are no longer being bound and unbind them
+                    for (var name in observing) {
+                        var ob = observing[name];
+                        if (ob.matched !== matched) {
+                            ob.observe.obj.unbind(ob.observe.attr, onchanged);
+                            delete observing[name];
+                        }
+                    }
+                    return value;
+                };
+                // set the initial value
+                data.value = getValueAndBind();
+
+                data.isListening = !can.isEmptyObject(observing);
+                return data;
+            }
+
+            // if no one is listening ... we can not calculate every time
+
+        can.compute = function(getterSetter, context, eventName) {
+            if (getterSetter && getterSetter.isComputed) {
+                return getterSetter;
+            }
+            // stores the result of computeBinder
+            var computedData,
+                // how many listeners to this this compute
+                bindings = 0,
+                // the computed object
+                computed,
+                // an object that keeps track if the computed is bound
+                // onchanged needs to know this. It's possible a change happens and results in
+                // something that unbinds the compute, it needs to not to try to recalculate who it
+                // is listening to
+                computeState = {
+                    bound: false,
+                    // true if this compute is calculated from other computes and observes
+                    hasDependencies: false
+                },
+                // The following functions are overwritten depending on how compute() is called
+                // a method to setup listening
+                on = function() {},
+                // a method to teardown listening
+                off = function() {},
+                // the current cached value (only valid if bound = true)
+                value,
+                // how to read the value
+                get = function() {
+                    return value
+                },
+                // sets the value
+                set = function(newVal) {
+                    value = newVal;
+                },
+                // this compute can be a dependency of other computes
+                canReadForChangeEvent = true;
+
+            computed = function(newVal) {
+                // setting ...
+                if (arguments.length) {
+                    // save a reference to the old value
+                    var old = value;
+
+                    // setter may return a value if 
+                    // setter is for a value maintained exclusively by this compute
+                    var setVal = set.call(context, newVal, old);
+
+                    // if this has dependencies return the current value
+                    if (computed.hasDependencies) {
+                        return get.call(context);
+                    }
+
+                    if (setVal === undefined) {
+                        // it's possible, like with the DOM, setting does not
+                        // fire a change event, so we must read
+                        value = get.call(context);
+                    } else {
+                        value = setVal;
+                    }
+                    // fire the change
+                    if (old !== value) {
+                        can.Observe.triggerBatch(computed, "change", [value, old]);
+                    }
+                    return value;
+                } else {
+                    // Let others know to listen to changes in this compute
+                    if (can.Observe.__reading && canReadForChangeEvent) {
+                        can.Observe.__reading(computed, 'change');
+                    }
+                    // if we are bound, use the cached value
+                    if (computeState.bound) {
+                        return value;
+                    } else {
+                        return get.call(context);
+                    }
+                }
+            }
+            if (typeof getterSetter === "function") {
+                set = getterSetter;
+                get = getterSetter;
+                canReadForChangeEvent = eventName === false ? false : true;
+                computed.hasDependencies = false;
+                on = function(update) {
+                    computedData = computeBinder(getterSetter, context || this, update, computeState);
+                    computed.hasDependencies = computedData.isListening
+                    value = computedData.value;
+                }
+                off = function() {
+                    computedData && computedData.teardown();
+                }
+            } else if (context) {
+
+                if (typeof context == "string") {
+                    // `can.compute(obj, "propertyName", [eventName])`
+
+                    var propertyName = context,
+                        isObserve = getterSetter instanceof can.Observe;
+                    if (isObserve) {
+                        computed.hasDependencies = true;
+                    }
+                    get = function() {
+                        if (isObserve) {
+                            return getterSetter.attr(propertyName);
+                        } else {
+                            return getterSetter[propertyName];
+                        }
+                    }
+                    set = function(newValue) {
+                        if (isObserve) {
+                            getterSetter.attr(propertyName, newValue)
+                        } else {
+                            getterSetter[propertyName] = newValue;
+                        }
+                    }
+                    var handler;
+                    on = function(update) {
+                        handler = function() {
+                            update(get(), value)
+                        };
+                        can.bind.call(getterSetter, eventName || propertyName, handler)
+
+                        // use getValueAndObserved because
+                        // we should not be indicating that some parent
+                        // reads this property if it happens to be binding on it
+                        value = getValueAndObserved(get).value
+                    }
+                    off = function() {
+                        can.unbind.call(getterSetter, eventName || propertyName, handler)
+                    }
+
+                } else {
+                    // `can.compute(initialValue, setter)`
+                    if (typeof context === "function") {
+                        value = getterSetter;
+                        set = context;
+                    } else {
+                        // `can.compute(initialValue,{get:, set:, on:, off:})`
+                        value = getterSetter;
+                        var options = context;
+                        get = options.get || get;
+                        set = options.set || set;
+                        on = options.on || on;
+                        off = options.off || off;
+                    }
+
+                }
+
+
+
+            } else {
+                // `can.compute(5)`
+                value = getterSetter;
+            }
+
+            computed.isComputed = true;
+
+            can.cid(computed, "compute")
+
+            var updater = function(newValue, oldValue) {
+                value = newValue;
+                // might need a way to look up new and oldVal
+                can.Observe.triggerBatch(computed, "change", [newValue, oldValue])
+            }
+
+            return can.extend(computed, {
+                    _bindsetup: function() {
+                        computeState.bound = true;
+                        // setup live-binding
+                        on.call(this, updater)
+                    },
+                    _bindteardown: function() {
+                        off.call(this, updater)
+                        computeState.bound = false;
+                    },
+
+                    bind: can.bindAndSetup,
+
+                    unbind: can.unbindAndTeardown
+                });
+        };
+        can.compute.binder = computeBinder;
+        return can.compute;
+    })(__m3, __m8);
+
+    // ## can/model/model.js
+    var __m10 = (function(can) {
+
+        // ## model.js  
+        // `can.Model`  
+        // _A `can.Observe` that connects to a RESTful interface._
+        // Generic deferred piping function
+
+        var pipe = function(def, model, func) {
+            var d = new can.Deferred();
+            def.then(function() {
+                var args = can.makeArray(arguments);
+                args[0] = model[func](args[0]);
+                d.resolveWith(d, args);
+            }, function() {
+                d.rejectWith(this, arguments);
+            });
+
+            if (typeof def.abort === 'function') {
+                d.abort = function() {
+                    return def.abort();
+                }
+            }
+
+            return d;
+        },
+            modelNum = 0,
+            ignoreHookup = /change.observe\d+/,
+            getId = function(inst) {
+                // Instead of using attr, use __get for performance.
+                // Need to set reading
+                can.Observe.__reading && can.Observe.__reading(inst, inst.constructor.id)
+                return inst.__get(inst.constructor.id);
+            },
+            // Ajax `options` generator function
+            ajax = function(ajaxOb, data, type, dataType, success, error) {
+
+                var params = {};
+
+                // If we get a string, handle it.
+                if (typeof ajaxOb == "string") {
+                    // If there's a space, it's probably the type.
+                    var parts = ajaxOb.split(/\s+/);
+                    params.url = parts.pop();
+                    if (parts.length) {
+                        params.type = parts.pop();
+                    }
+                } else {
+                    can.extend(params, ajaxOb);
+                }
+
+                // If we are a non-array object, copy to a new attrs.
+                params.data = typeof data == "object" && !can.isArray(data) ?
+                    can.extend(params.data || {}, data) : data;
+
+                // Get the url with any templated values filled out.
+                params.url = can.sub(params.url, params.data, true);
+
+                return can.ajax(can.extend({
+                            type: type || "post",
+                            dataType: dataType || "json",
+                            success: success,
+                            error: error
+                        }, params));
+            },
+            makeRequest = function(self, type, success, error, method) {
+                var args;
+                // if we pass an array as `self` it it means we are coming from
+                // the queued request, and we're passing already serialized data
+                // self's signature will be: [self, serializedData]
+                if (can.isArray(self)) {
+                    args = self[1];
+                    self = self[0];
+                } else {
+                    args = self.serialize();
+                }
+                args = [args];
+                var deferred,
+                    // The model.
+                    model = self.constructor,
+                    jqXHR;
+
+                // `destroy` does not need data.
+                if (type == 'destroy') {
+                    args.shift();
+                }
+                // `update` and `destroy` need the `id`.
+                if (type !== 'create') {
+                    args.unshift(getId(self));
+                }
+
+
+                jqXHR = model[type].apply(model, args);
+
+                deferred = jqXHR.pipe(function(data) {
+                    self[method || type + "d"](data, jqXHR);
+                    return self;
+                });
+
+                // Hook up `abort`
+                if (jqXHR.abort) {
+                    deferred.abort = function() {
+                        jqXHR.abort();
+                    };
+                }
+
+                deferred.then(success, error);
+                return deferred;
+            },
+
+            // This object describes how to make an ajax request for each ajax method.  
+            // The available properties are:
+            //		`url` - The default url to use as indicated as a property on the model.
+            //		`type` - The default http request type
+            //		`data` - A method that takes the `arguments` and returns `data` used for ajax.
+
+            ajaxMethods = {
+
+                create: {
+                    url: "_shortName",
+                    type: "post"
+                },
+
+                update: {
+                    data: function(id, attrs) {
+                        attrs = attrs || {};
+                        var identity = this.id;
+                        if (attrs[identity] && attrs[identity] !== id) {
+                            attrs["new" + can.capitalize(id)] = attrs[identity];
+                            delete attrs[identity];
+                        }
+                        attrs[identity] = id;
+                        return attrs;
+                    },
+                    type: "put"
+                },
+
+                destroy: {
+                    type: "delete",
+                    data: function(id) {
+                        var args = {};
+                        args.id = args[this.id] = id;
+                        return args;
+                    }
+                },
+
+                findAll: {
+                    url: "_shortName"
+                },
+
+                findOne: {}
+            },
+            // Makes an ajax request `function` from a string.
+            //		`ajaxMethod` - The `ajaxMethod` object defined above.
+            //		`str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
+            ajaxMaker = function(ajaxMethod, str) {
+                // Return a `function` that serves as the ajax method.
+                return function(data) {
+                    // If the ajax method has it's own way of getting `data`, use that.
+                    data = ajaxMethod.data ?
+                        ajaxMethod.data.apply(this, arguments) :
+                    // Otherwise use the data passed in.
+                    data;
+                    // Return the ajax method with `data` and the `type` provided.
+                    return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
+                }
+            }
+
+
+
+        can.Model = can.Observe({
+                fullName: "can.Model",
+                _reqs: 0,
+                setup: function(base) {
+                    // create store here if someone wants to use model without inheriting from it
+                    this.store = {};
+                    can.Observe.setup.apply(this, arguments);
+                    // Set default list as model list
+                    if (!can.Model) {
+                        return;
+                    }
+                    this.List = ML({
+                            Observe: this
+                        }, {});
+                    var self = this,
+                        clean = can.proxy(this._clean, self);
+
+
+                    // go through ajax methods and set them up
+                    can.each(ajaxMethods, function(method, name) {
+                        // if an ajax method is not a function, it's either
+                        // a string url like findAll: "/recipes" or an
+                        // ajax options object like {url: "/recipes"}
+                        if (!can.isFunction(self[name])) {
+                            // use ajaxMaker to convert that into a function
+                            // that returns a deferred with the data
+                            self[name] = ajaxMaker(method, self[name]);
+                        }
+                        // check if there's a make function like makeFindAll
+                        // these take deferred function and can do special
+                        // behavior with it (like look up data in a store)
+                        if (self["make" + can.capitalize(name)]) {
+                            // pass the deferred method to the make method to get back
+                            // the "findAll" method.
+                            var newMethod = self["make" + can.capitalize(name)](self[name]);
+                            can.Construct._overwrite(self, base, name, function() {
+                                // increment the numer of requests
+                                can.Model._reqs++;
+                                var def = newMethod.apply(this, arguments);
+                                var then = def.then(clean, clean);
+                                then.abort = def.abort;
+
+                                // attach abort to our then and return it
+                                return then;
+                            })
+                        }
+                    });
+
+                    if (self.fullName == "can.Model" || !self.fullName) {
+                        self.fullName = "Model" + (++modelNum);
+                    }
+                    // Add ajax converters.
+                    can.Model._reqs = 0;
+                    this._url = this._shortName + "/{" + this.id + "}"
+                },
+                _ajax: ajaxMaker,
+                _makeRequest: makeRequest,
+                _clean: function() {
+                    can.Model._reqs--;
+                    if (!can.Model._reqs) {
+                        for (var id in this.store) {
+                            if (!this.store[id]._bindings) {
+                                delete this.store[id];
+                            }
+                        }
+                    }
+                    return arguments[0];
+                },
+
+                models: function(instancesRawData, oldList) {
+                    // until "end of turn", increment reqs counter so instances will be added to the store
+                    can.Model._reqs++;
+                    if (!instancesRawData) {
+                        return;
+                    }
+
+                    if (instancesRawData instanceof this.List) {
+                        return instancesRawData;
+                    }
+
+                    // Get the list type.
+                    var self = this,
+                        tmp = [],
+                        res = oldList instanceof can.Observe.List ? oldList : new(self.List || ML),
+                        // Did we get an `array`?
+                        arr = can.isArray(instancesRawData),
+
+                        // Did we get a model list?
+                        ml = (instancesRawData instanceof ML),
+
+                        // Get the raw `array` of objects.
+                        raw = arr ?
+
+                        // If an `array`, return the `array`.
+                        instancesRawData :
+
+                        // Otherwise if a model list.
+                        (ml ?
+
+                            // Get the raw objects from the list.
+                            instancesRawData.serialize() :
+
+                            // Get the object's data.
+                            instancesRawData.data),
+                        i = 0;
+
+
+
+                    if (res.length) {
+                        res.splice(0);
+                    }
+
+                    can.each(raw, function(rawPart) {
+                        tmp.push(self.model(rawPart));
+                    });
+
+                    // We only want one change event so push everything at once
+                    res.push.apply(res, tmp);
+
+                    if (!arr) { // Push other stuff onto `array`.
+                        can.each(instancesRawData, function(val, prop) {
+                            if (prop !== 'data') {
+                                res.attr(prop, val);
+                            }
+                        })
+                    }
+                    // at "end of turn", clean up the store
+                    setTimeout(can.proxy(this._clean, this), 1);
+                    return res;
+                },
+
+                model: function(attributes) {
+                    if (!attributes) {
+                        return;
+                    }
+                    if (attributes instanceof this) {
+                        attributes = attributes.serialize();
+                    }
+                    var id = attributes[this.id],
+                        model = (id || id === 0) && this.store[id] ?
+                            this.store[id].attr(attributes, this.removeAttr || false) : new this(attributes);
+                    if (can.Model._reqs) {
+                        this.store[attributes[this.id]] = model;
+                    }
+                    return model;
+                }
+            },
+
+
+            {
+
+                isNew: function() {
+                    var id = getId(this);
+                    return !(id || id === 0); // If `null` or `undefined`
+                },
+
+                save: function(success, error) {
+                    return makeRequest(this, this.isNew() ? 'create' : 'update', success, error);
+                },
+
+                destroy: function(success, error) {
+                    if (this.isNew()) {
+                        var self = this;
+                        var def = can.Deferred();
+                        def.then(success, error);
+                        return def.done(function(data) {
+                            self.destroyed(data)
+                        }).resolve(self);
+                    }
+                    return makeRequest(this, 'destroy', success, error, 'destroyed');
+                },
+
+                _bindsetup: function() {
+                    this.constructor.store[this.__get(this.constructor.id)] = this;
+                    return can.Observe.prototype._bindsetup.apply(this, arguments);
+                },
+
+                _bindteardown: function() {
+                    delete this.constructor.store[getId(this)];
+                    return can.Observe.prototype._bindteardown.apply(this, arguments)
+                },
+                // Change `id`.
+                ___set: function(prop, val) {
+                    can.Observe.prototype.___set.call(this, prop, val)
+                    // If we add an `id`, move it to the store.
+                    if (prop === this.constructor.id && this._bindings) {
+                        this.constructor.store[getId(this)] = this;
+                    }
+                }
+            });
+
+        can.each({
+                makeFindAll: "models",
+                makeFindOne: "model",
+                makeCreate: "model",
+                makeUpdate: "model"
+            }, function(method, name) {
+                can.Model[name] = function(oldMethod) {
+                    return function() {
+                        var args = can.makeArray(arguments),
+                            oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2),
+                            def = pipe(oldMethod.apply(this, oldArgs), this, method);
+                        def.then(args[0], args[1]);
+                        // return the original promise
+                        return def;
+                    };
+                };
+            });
+
+        can.each([
+
+                "created",
+
+                "updated",
+
+                "destroyed"
+            ], function(funcName) {
+                can.Model.prototype[funcName] = function(attrs) {
+                    var stub,
+                        constructor = this.constructor;
+
+                    // Update attributes if attributes have been passed
+                    stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
+
+                    // triggers change event that bubble's like
+                    // handler( 'change','1.destroyed' ). This is used
+                    // to remove items on destroyed from Model Lists.
+                    // but there should be a better way.
+                    can.trigger(this, "change", funcName)
+
+
+                    // Call event on the instance's Class
+                    can.trigger(constructor, funcName, this);
+                };
+            });
+
+        // Model lists are just like `Observe.List` except that when their items are 
+        // destroyed, it automatically gets removed from the list.
+
+        var ML = can.Model.List = can.Observe.List({
+                setup: function(params) {
+                    if (can.isPlainObject(params) && !can.isArray(params)) {
+                        can.Observe.List.prototype.setup.apply(this);
+                        this.replace(this.constructor.Observe.findAll(params))
+                    } else {
+                        can.Observe.List.prototype.setup.apply(this, arguments);
+                    }
+                },
+                _changes: function(ev, attr) {
+                    can.Observe.List.prototype._changes.apply(this, arguments);
+                    if (/\w+\.destroyed/.test(attr)) {
+                        var index = this.indexOf(ev.target);
+                        if (index != -1) {
+                            this.splice(index, 1);
+                        }
+                    }
+                }
+            })
+
+        return can.Model;
+    })(__m3, __m7);
+
+    // ## can/view/view.js
+    var __m11 = (function(can) {
+        // ## view.js
+        // `can.view`  
+        // _Templating abstraction._
+
+        var isFunction = can.isFunction,
+            makeArray = can.makeArray,
+            // Used for hookup `id`s.
+            hookupId = 1,
+
+            $view = can.view = can.template = function(view, data, helpers, callback) {
+                // If helpers is a `function`, it is actually a callback.
+                if (isFunction(helpers)) {
+                    callback = helpers;
+                    helpers = undefined;
+                }
+
+                var pipe = function(result) {
+                    return $view.frag(result);
+                },
+                    // In case we got a callback, we need to convert the can.view.render
+                    // result to a document fragment
+                    wrapCallback = isFunction(callback) ? function(frag) {
+                        callback(pipe(frag));
+                    } : null,
+                    // Get the result.
+                    result = $view.render(view, data, helpers, wrapCallback),
+                    deferred = can.Deferred();
+
+                if (isFunction(result)) {
+                    return result;
+                }
+
+                if (can.isDeferred(result)) {
+                    result.then(function(result, data) {
+                        deferred.resolve.call(deferred, pipe(result), data);
+                    }, function() {
+                        deferred.fail.apply(deferred, arguments);
+                    });
+                    return deferred;
+                }
+
+                // Convert it into a dom frag.
+                return pipe(result);
+            };
+
+        can.extend($view, {
+                // creates a frag and hooks it up all at once
+                frag: function(result, parentNode) {
+                    return $view.hookup($view.fragment(result), parentNode);
+                },
+
+                // simply creates a frag
+                // this is used internally to create a frag
+                // insert it
+                // then hook it up
+                fragment: function(result) {
+                    var frag = can.buildFragment(result, document.body);
+                    // If we have an empty frag...
+                    if (!frag.childNodes.length) {
+                        frag.appendChild(document.createTextNode(''));
+                    }
+                    return frag;
+                },
+
+                // Convert a path like string into something that's ok for an `element` ID.
+                toId: function(src) {
+                    return can.map(src.toString().split(/\/|\./g), function(part) {
+                        // Dont include empty strings in toId functions
+                        if (part) {
+                            return part;
+                        }
+                    }).join("_");
+                },
+
+                hookup: function(fragment, parentNode) {
+                    var hookupEls = [],
+                        id,
+                        func;
+
+                    // Get all `childNodes`.
+                    can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node) {
+                        if (node.nodeType === 1) {
+                            hookupEls.push(node);
+                            hookupEls.push.apply(hookupEls, can.makeArray(node.getElementsByTagName('*')));
+                        }
+                    });
+
+                    // Filter by `data-view-id` attribute.
+                    can.each(hookupEls, function(el) {
+                        if (el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id])) {
+                            func(el, parentNode, id);
+                            delete $view.hookups[id];
+                            el.removeAttribute('data-view-id');
+                        }
+                    });
+
+                    return fragment;
+                },
+
+
+                hookups: {},
+
+
+                hook: function(cb) {
+                    $view.hookups[++hookupId] = cb;
+                    return " data-view-id='" + hookupId + "'";
+                },
+
+
+                cached: {},
+
+                cachedRenderers: {},
+
+
+                cache: true,
+
+
+                register: function(info) {
+                    this.types["." + info.suffix] = info;
+                },
+
+                types: {},
+
+
+                ext: ".ejs",
+
+
+                registerScript: function() {},
+
+
+                preload: function() {},
+
+
+                render: function(view, data, helpers, callback) {
+                    // If helpers is a `function`, it is actually a callback.
+                    if (isFunction(helpers)) {
+                        callback = helpers;
+                        helpers = undefined;
+                    }
+
+                    // See if we got passed any deferreds.
+                    var deferreds = getDeferreds(data);
+
+                    if (deferreds.length) { // Does data contain any deferreds?
+                        // The deferred that resolves into the rendered content...
+                        var deferred = new can.Deferred(),
+                            dataCopy = can.extend({}, data);
+
+                        // Add the view request to the list of deferreds.
+                        deferreds.push(get(view, true))
+
+                        // Wait for the view and all deferreds to finish...
+                        can.when.apply(can, deferreds).then(function(resolved) {
+                            // Get all the resolved deferreds.
+                            var objs = makeArray(arguments),
+                                // Renderer is the last index of the data.
+                                renderer = objs.pop(),
+                                // The result of the template rendering with data.
+                                result;
+
+                            // Make data look like the resolved deferreds.
+                            if (can.isDeferred(data)) {
+                                dataCopy = usefulPart(resolved);
+                            } else {
+                                // Go through each prop in data again and
+                                // replace the defferreds with what they resolved to.
+                                for (var prop in data) {
+                                    if (can.isDeferred(data[prop])) {
+                                        dataCopy[prop] = usefulPart(objs.shift());
+                                    }
+                                }
+                            }
+
+                            // Get the rendered result.
+                            result = renderer(dataCopy, helpers);
+
+                            // Resolve with the rendered view.
+                            deferred.resolve(result, dataCopy);
+
+                            // If there's a `callback`, call it back with the result.
+                            callback && callback(result, dataCopy);
+                        }, function() {
+                            deferred.reject.apply(deferred, arguments)
+                        });
+                        // Return the deferred...
+                        return deferred;
+                    } else {
+                        // No deferreds! Render this bad boy.
+                        var response,
+                            // If there's a `callback` function
+                            async = isFunction(callback),
+                            // Get the `view` type
+                            deferred = get(view, async);
+
+                        // If we are `async`...
+                        if (async) {
+                            // Return the deferred
+                            response = deferred;
+                            // And fire callback with the rendered result.
+                            deferred.then(function(renderer) {
+                                callback(data ? renderer(data, helpers) : renderer);
+                            })
+                        } else {
+                            // if the deferred is resolved, call the cached renderer instead
+                            // this is because it's possible, with recursive deferreds to
+                            // need to render a view while its deferred is _resolving_.  A _resolving_ deferred
+                            // is a deferred that was just resolved and is calling back it's success callbacks.
+                            // If a new success handler is called while resoliving, it does not get fired by
+                            // jQuery's deferred system.  So instead of adding a new callback
+                            // we use the cached renderer.
+                            // We also add __view_id on the deferred so we can look up it's cached renderer.
+                            // In the future, we might simply store either a deferred or the cached result.
+                            if (deferred.state() === "resolved" && deferred.__view_id) {
+                                var currentRenderer = $view.cachedRenderers[deferred.__view_id];
+                                return data ? currentRenderer(data, helpers) : currentRenderer;
+                            } else {
+                                // Otherwise, the deferred is complete, so
+                                // set response to the result of the rendering.
+                                deferred.then(function(renderer) {
+                                    response = data ? renderer(data, helpers) : renderer;
+                                });
+                            }
+                        }
+
+                        return response;
+                    }
+                },
+
+
+                registerView: function(id, text, type, def) {
+                    // Get the renderer function.
+                    var func = (type || $view.types[$view.ext]).renderer(id, text);
+                    def = def || new can.Deferred();
+
+                    // Cache if we are caching.
+                    if ($view.cache) {
+                        $view.cached[id] = def;
+                        def.__view_id = id;
+                        $view.cachedRenderers[id] = func;
+                    }
+
+                    // Return the objects for the response's `dataTypes`
+                    // (in this case view).
+                    return def.resolve(func);
+                }
+            });
+
+        // Makes sure there's a template, if not, have `steal` provide a warning.
+        var checkText = function(text, url) {
+            if (!text.length) {
+
+                throw "can.view: No template or empty template:" + url;
+            }
+        },
+            // `Returns a `view` renderer deferred.  
+            // `url` - The url to the template.  
+            // `async` - If the ajax request should be asynchronous.  
+            // Returns a deferred.
+            get = function(url, async) {
+                var suffix = url.match(/\.[\w\d]+$/),
+                    type,
+                    // If we are reading a script element for the content of the template,
+                    // `el` will be set to that script element.
+                    el,
+                    // A unique identifier for the view (used for caching).
+                    // This is typically derived from the element id or
+                    // the url for the template.
+                    id,
+                    // The ajax request used to retrieve the template content.
+                    jqXHR;
+
+                //If the url has a #, we assume we want to use an inline template
+                //from a script element and not current page's HTML
+                if (url.match(/^#/)) {
+                    url = url.substr(1);
+                }
+                // If we have an inline template, derive the suffix from the `text/???` part.
+                // This only supports `<script>` tags.
+                if (el = document.getElementById(url)) {
+                    suffix = "." + el.type.match(/\/(x\-)?(.+)/)[2];
+                }
+
+                // If there is no suffix, add one.
+                if (!suffix && !$view.cached[url]) {
+                    url += (suffix = $view.ext);
+                }
+
+                if (can.isArray(suffix)) {
+                    suffix = suffix[0]
+                }
+
+                // Convert to a unique and valid id.
+                id = $view.toId(url);
+
+                // If an absolute path, use `steal` to get it.
+                // You should only be using `//` if you are using `steal`.
+                if (url.match(/^\/\//)) {
+                    var sub = url.substr(2);
+                    url = !window.steal ?
+                        sub :
+                        steal.config().root.mapJoin("" + steal.id(sub));
+                }
+
+                // Set the template engine type.
+                type = $view.types[suffix];
+
+                // If it is cached, 
+                if ($view.cached[id]) {
+                    // Return the cached deferred renderer.
+                    return $view.cached[id];
+
+                    // Otherwise if we are getting this from a `<script>` element.
+                } else if (el) {
+                    // Resolve immediately with the element's `innerHTML`.
+                    return $view.registerView(id, el.innerHTML, type);
+                } else {
+                    // Make an ajax request for text.
+                    var d = new can.Deferred();
+                    can.ajax({
+                            async: async,
+                            url: url,
+                            dataType: "text",
+                            error: function(jqXHR) {
+                                checkText("", url);
+                                d.reject(jqXHR);
+                            },
+                            success: function(text) {
+                                // Make sure we got some text back.
+                                checkText(text, url);
+                                $view.registerView(id, text, type, d)
+                            }
+                        });
+                    return d;
+                }
+            },
+            // Gets an `array` of deferreds from an `object`.
+            // This only goes one level deep.
+            getDeferreds = function(data) {
+                var deferreds = [];
+
+                // pull out deferreds
+                if (can.isDeferred(data)) {
+                    return [data]
+                } else {
+                    for (var prop in data) {
+                        if (can.isDeferred(data[prop])) {
+                            deferreds.push(data[prop]);
+                        }
+                    }
+                }
+                return deferreds;
+            },
+            // Gets the useful part of a resolved deferred.
+            // This is for `model`s and `can.ajax` that resolve to an `array`.
+            usefulPart = function(resolved) {
+                return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
+            };
+
+        //!steal-pluginify-remove-start
+        if (window.steal) {
+            steal.type("view js", function(options, success, error) {
+                var type = $view.types["." + options.type],
+                    id = $view.toId(options.id);
+
+                options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "',function(can){return " + "can.view.preload('" + id + "'," + options.text + ");\n})";
+                success();
+            })
+        }
+        //!steal-pluginify-remove-end
+
+        can.extend($view, {
+                register: function(info) {
+                    this.types["." + info.suffix] = info;
+
+                    //!steal-pluginify-remove-start
+                    if (window.steal) {
+                        steal.type(info.suffix + " view js", function(options, success, error) {
+                            var type = $view.types["." + options.type],
+                                id = $view.toId(options.id + '');
+
+                            options.text = type.script(id, options.text)
+                            success();
+                        })
+                    };
+                    //!steal-pluginify-remove-end
+
+                    $view[info.suffix] = function(id, text) {
+                        if (!text) {
+                            // Return a nameless renderer
+                            var renderer = function() {
+                                return $view.frag(renderer.render.apply(this, arguments));
+                            }
+                            renderer.render = function() {
+                                var renderer = info.renderer(null, id);
+                                return renderer.apply(renderer, arguments);
+                            }
+                            return renderer;
+                        }
+
+                        $view.preload(id, info.renderer(id, text));
+                        return can.view(id);
+                    }
+                },
+                registerScript: function(type, id, src) {
+                    return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
+                },
+                preload: function(id, renderer) {
+                    $view.cached[id] = new can.Deferred().resolve(function(data, helpers) {
+                        return renderer.call(data, data, helpers);
+                    });
+
+                    function frag() {
+                        return $view.frag(renderer.apply(this, arguments));
+                    }
+                    // expose the renderer for mustache
+                    frag.render = renderer;
+                    return frag;
+                }
+
+            });
+
+        return can;
+    })(__m3);
+
+    // ## can/view/elements.js
+    var __m14 = (function() {
+
+        var elements = {
+            tagToContentPropMap: {
+                option: "textContent" in document.createElement("option") ? "textContent" : "innerText",
+                textarea: "value"
+            },
+
+            attrMap: {
+                "class": "className",
+                "value": "value",
+                "innerText": "innerText",
+                "textContent": "textContent",
+                "checked": true,
+                "disabled": true,
+                "readonly": true,
+                "required": true
+            },
+            // elements whos default value we should set
+            defaultValue: ["input", "textarea"],
+            // a map of parent element to child elements
+            tagMap: {
+                "": "span",
+                table: "tbody",
+                tr: "td",
+                ol: "li",
+                ul: "li",
+                tbody: "tr",
+                thead: "tr",
+                tfoot: "tr",
+                select: "option",
+                optgroup: "option"
+            },
+            // a tag's parent element
+            reverseTagMap: {
+                tr: "tbody",
+                option: "select",
+                td: "tr",
+                th: "tr",
+                li: "ul"
+            },
+
+            getParentNode: function(el, defaultParentNode) {
+                return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
+            },
+            // set an attribute on an element
+            setAttr: function(el, attrName, val) {
+                var tagName = el.nodeName.toString().toLowerCase(),
+                    prop = elements.attrMap[attrName];
+                // if this is a special property
+                if (prop === true) {
+                    el[attrName] = true;
+                } else if (prop) {
+                    // set the value as true / false
+                    el[prop] = val;
+                    if (prop === "value" && can.inArray(tagName, elements.defaultValue) >= 0) {
+                        el.defaultValue = val;
+                    }
+                } else {
+                    el.setAttribute(attrName, val);
+                }
+            },
+            // gets the value of an attribute
+            getAttr: function(el, attrName) {
+                // Default to a blank string for IE7/8
+                return (elements.attrMap[attrName] && el[elements.attrMap[attrName]] ?
+                    el[elements.attrMap[attrName]] :
+                    el.getAttribute(attrName)) || '';
+            },
+            // removes the attribute
+            removeAttr: function(el, attrName) {
+                if (elements.attrMap[attrName] === true) {
+                    el[attrName] = false;
+                } else {
+                    el.removeAttribute(attrName);
+                }
+            },
+            contentText: function(text) {
+                if (typeof text == 'string') {
+                    return text;
+                }
+                // If has no value, return an empty string.
+                if (!text && text !== 0) {
+                    return '';
+                }
+                return "" + text;
+            }
+        };
+
+        return elements;
+    })();
+
+    // ## can/view/scanner.js
+    var __m13 = (function(can, elements) {
+
+        var newLine = /(\r|\n)+/g,
+            // Escapes characters starting with `\`.
+            clean = function(content) {
+                return content
+                    .split('\\').join("\\\\")
+                    .split("\n").join("\\n")
+                    .split('"').join('\\"')
+                    .split("\t").join("\\t");
+            },
+            // Returns a tagName to use as a temporary placeholder for live content
+            // looks forward ... could be slow, but we only do it when necessary
+            getTag = function(tagName, tokens, i) {
+                // if a tagName is provided, use that
+                if (tagName) {
+                    return tagName;
+                } else {
+                    // otherwise go searching for the next two tokens like "<",TAG
+                    while (i < tokens.length) {
+                        if (tokens[i] == "<" && elements.reverseTagMap[tokens[i + 1]]) {
+                            return elements.reverseTagMap[tokens[i + 1]];
+                        }
+                        i++;
+                    }
+                }
+                return '';
+            },
+            bracketNum = function(content) {
+                return (--content.split("{").length) - (--content.split("}").length);
+            },
+            myEval = function(script) {
+                eval(script);
+            },
+            attrReg = /([^\s]+)[\s]*=[\s]*$/,
+            // Commands for caching.
+            startTxt = 'var ___v1ew = [];',
+            finishTxt = "return ___v1ew.join('')",
+            put_cmd = "___v1ew.push(",
+            insert_cmd = put_cmd,
+            // Global controls (used by other functions to know where we are).
+            // Are we inside a tag?
+            htmlTag = null,
+            // Are we within a quote within a tag?
+            quote = null,
+            // What was the text before the current quote? (used to get the `attr` name)
+            beforeQuote = null,
+            // Whether a rescan is in progress
+            rescan = null,
+            // Used to mark where the element is.
+            status = function() {
+                // `t` - `1`.
+                // `h` - `0`.
+                // `q` - String `beforeQuote`.
+                return quote ? "'" + beforeQuote.match(attrReg)[1] + "'" : (htmlTag ? 1 : 0);
+            };
+
+        can.view.Scanner = Scanner = function(options) {
+            // Set options on self
+            can.extend(this, {
+                    text: {},
+                    tokens: []
+                }, options);
+
+            // Cache a token lookup
+            this.tokenReg = [];
+            this.tokenSimple = {
+                "<": "<",
+                ">": ">",
+                '"': '"',
+                "'": "'"
+            };
+            this.tokenComplex = [];
+            this.tokenMap = {};
+            for (var i = 0, token; token = this.tokens[i]; i++) {
+
+
+                // Save complex mappings (custom regexp)
+                if (token[2]) {
+                    this.tokenReg.push(token[2]);
+                    this.tokenComplex.push({
+                            abbr: token[1],
+                            re: new RegExp(token[2]),
+                            rescan: token[3]
+                        });
+                }
+                // Save simple mappings (string only, no regexp)
+                else {
+                    this.tokenReg.push(token[1]);
+                    this.tokenSimple[token[1]] = token[0];
+                }
+                this.tokenMap[token[0]] = token[1];
+            }
+
+            // Cache the token registry.
+            this.tokenReg = new RegExp("(" + this.tokenReg.slice(0).concat(["<", ">", '"', "'"]).join("|") + ")", "g");
+        };
+
+        Scanner.prototype = {
+
+            helpers: [
+
+                {
+                    name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+                    fn: function(content) {
+                        var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+                            parts = content.match(quickFunc);
+
+                        return "can.proxy(function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}, this);";
+                    }
+                }
+            ],
+
+            scan: function(source, name) {
+                var tokens = [],
+                    last = 0,
+                    simple = this.tokenSimple,
+                    complex = this.tokenComplex;
+
+                source = source.replace(newLine, "\n");
+                if (this.transform) {
+                    source = this.transform(source);
+                }
+                source.replace(this.tokenReg, function(whole, part) {
+                    // offset is the second to last argument
+                    var offset = arguments[arguments.length - 2];
+
+                    // if the next token starts after the last token ends
+                    // push what's in between
+                    if (offset > last) {
+                        tokens.push(source.substring(last, offset));
+                    }
+
+                    // push the simple token (if there is one)
+                    if (simple[whole]) {
+                        tokens.push(whole);
+                    }
+                    // otherwise lookup complex tokens
+                    else {
+                        for (var i = 0, token; token = complex[i]; i++) {
+                            if (token.re.test(whole)) {
+                                tokens.push(token.abbr);
+                                // Push a rescan function if one exists
+                                if (token.rescan) {
+                                    tokens.push(token.rescan(part));
+                                }
+                                break;
+                            }
+                        }
+                    }
+
+                    // update the position of the last part of the last token
+                    last = offset + part.length;
+                });
+
+                // if there's something at the end, add it
+                if (last < source.length) {
+                    tokens.push(source.substr(last));
+                }
+
+                var content = '',
+                    buff = [startTxt + (this.text.start || '')],
+                    // Helper `function` for putting stuff in the view concat.
+                    put = function(content, bonus) {
+                        buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');');
+                    },
+                    // A stack used to keep track of how we should end a bracket
+                    // `}`.  
+                    // Once we have a `<%= %>` with a `leftBracket`,
+                    // we store how the file should end here (either `))` or `;`).
+                    endStack = [],
+                    // The last token, used to remember which tag we are in.
+                    lastToken,
+                    // The corresponding magic tag.
+                    startTag = null,
+                    // Was there a magic tag inside an html tag?
+                    magicInTag = false,
+                    // The current tag name.
+                    tagName = '',
+                    // stack of tagNames
+                    tagNames = [],
+                    // Pop from tagNames?
+                    popTagName = false,
+                    // Declared here.
+                    bracketCount,
+                    i = 0,
+                    token,
+                    tmap = this.tokenMap;
+
+                // Reinitialize the tag state goodness.
+                htmlTag = quote = beforeQuote = null;
+
+                for (;
+                    (token = tokens[i++]) !== undefined;) {
+                    if (startTag === null) {
+                        switch (token) {
+                            case tmap.left:
+                            case tmap.escapeLeft:
+                            case tmap.returnLeft:
+                                magicInTag = htmlTag && 1;
+                            case tmap.commentLeft:
+                                // A new line -- just add whatever content within a clean.  
+                                // Reset everything.
+                                startTag = token;
+                                if (content.length) {
+                                    put(content);
+                                }
+                                content = '';
+                                break;
+                            case tmap.escapeFull:
+                                // This is a full line escape (a line that contains only whitespace and escaped logic)
+                                // Break it up into escape left and right
+                                magicInTag = htmlTag && 1;
+                                rescan = 1;
+                                startTag = tmap.escapeLeft;
+                                if (content.length) {
+                                    put(content);
+                                }
+                                rescan = tokens[i++];
+                                content = rescan.content || rescan;
+                                if (rescan.before) {
+                                    put(rescan.before);
+                                }
+                                tokens.splice(i, 0, tmap.right);
+                                break;
+                            case tmap.commentFull:
+                                // Ignore full line comments.
+                                break;
+                            case tmap.templateLeft:
+                                content += tmap.left;
+                                break;
+                            case '<':
+                                // Make sure we are not in a comment.
+                                if (tokens[i].indexOf("!--") !== 0) {
+                                    htmlTag = 1;
+                                    magicInTag = 0;
+                                }
+                                content += token;
+                                break;
+                            case '>':
+                                htmlTag = 0;
+                                // content.substr(-1) doesn't work in IE7/8
+                                var emptyElement = content.substr(content.length - 1) == "/" || content.substr(content.length - 2) == "--";
+                                // if there was a magic tag
+                                // or it's an element that has text content between its tags, 
+                                // but content is not other tags add a hookup
+                                // TODO: we should only add `can.EJS.pending()` if there's a magic tag 
+                                // within the html tags.
+                                if (magicInTag || !popTagName && elements.tagToContentPropMap[tagNames[tagNames.length - 1]]) {
+                                    // make sure / of /> is on the left of pending
+                                    if (emptyElement) {
+                                        put(content.substr(0, content.length - 1), ",can.view.pending(),\"/>\"");
+                                    } else {
+                                        put(content, ",can.view.pending(),\">\"");
+                                    }
+                                    content = '';
+                                    magicInTag = 0;
+                                } else {
+                                    content += token;
+                                }
+                                // if it's a tag like <input/>
+                                if (emptyElement || popTagName) {
+                                    // remove the current tag in the stack
+                                    tagNames.pop();
+                                    // set the current tag to the previous parent
+                                    tagName = tagNames[tagNames.length - 1];
+                                    // Don't pop next time
+                                    popTagName = false;
+                                }
+                                break;
+                            case "'":
+                            case '"':
+                                // If we are in an html tag, finding matching quotes.
+                                if (htmlTag) {
+                                    // We have a quote and it matches.
+                                    if (quote && quote === token) {
+                                        // We are exiting the quote.
+                                        quote = null;
+                                        // Otherwise we are creating a quote.
+                                        // TODO: does this handle `\`?
+                                    } else if (quote === null) {
+                                        quote = token;
+                                        beforeQuote = lastToken;
+                                    }
+                                }
+                            default:
+                                // Track the current tag
+                                if (lastToken === '<') {
+                                    tagName = token.split(/\s/)[0];
+                                    if (tagName.indexOf("/") === 0 && tagNames[tagNames.length - 1] === tagName.substr(1)) {
+                                        // set tagName to the last tagName
+                                        // if there are no more tagNames, we'll rely on getTag.
+                                        tagName = tagNames[tagNames.length - 1];
+                                        popTagName = true;
+                                    } else {
+                                        tagNames.push(tagName);
+                                    }
+                                }
+                                content += token;
+                                break;
+                        }
+                    } else {
+                        // We have a start tag.
+                        switch (token) {
+                            case tmap.right:
+                            case tmap.returnRight:
+                                switch (startTag) {
+                                    case tmap.left:
+                                        // Get the number of `{ minus }`
+                                        bracketCount = bracketNum(content);
+
+                                        // We are ending a block.
+                                        if (bracketCount == 1) {
+
+                                            // We are starting on.
+                                            buff.push(insert_cmd, "can.view.txt(0,'" + getTag(tagName, tokens, i) + "'," + status() + ",this,function(){", startTxt, content);
+
+                                            endStack.push({
+                                                    before: "",
+                                                    after: finishTxt + "}));\n"
+                                                });
+                                        } else {
+
+                                            // How are we ending this statement?
+                                            last = // If the stack has value and we are ending a block...
+                                            endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
+                                            endStack.pop() : // Or use the default ending.
+                                            {
+                                                after: ";"
+                                            };
+
+                                            // If we are ending a returning block, 
+                                            // add the finish text which returns the result of the
+                                            // block.
+                                            if (last.before) {
+                                                buff.push(last.before);
+                                            }
+                                            // Add the remaining content.
+                                            buff.push(content, ";", last.after);
+                                        }
+                                        break;
+                                    case tmap.escapeLeft:
+                                    case tmap.returnLeft:
+                                        // We have an extra `{` -> `block`.
+                                        // Get the number of `{ minus }`.
+                                        bracketCount = bracketNum(content);
+                                        // If we have more `{`, it means there is a block.
+                                        if (bracketCount) {
+                                            // When we return to the same # of `{` vs `}` end with a `doubleParent`.
+                                            endStack.push({
+                                                    before: finishTxt,
+                                                    after: "}));"
+                                                });
+                                        }
+
+                                        var escaped = startTag === tmap.escapeLeft ? 1 : 0,
+                                            commands = {
+                                                insert: insert_cmd,
+                                                tagName: getTag(tagName, tokens, i),
+                                                status: status()
+                                            };
+
+                                        for (var ii = 0; ii < this.helpers.length; ii++) {
+                                            // Match the helper based on helper
+                                            // regex name value
+                                            var helper = this.helpers[ii];
+                                            if (helper.name.test(content)) {
+                                                content = helper.fn(content, commands);
+
+                                                // dont escape partials
+                                                if (helper.name.source == /^>[\s]*\w*/.source) {
+                                                    escaped = 0;
+                                                }
+                                                break;
+                                            }
+                                        }
+
+                                        // Handle special cases
+                                        if (typeof content == 'object') {
+                                            if (content.raw) {
+                                                buff.push(content.raw);
+                                            }
+                                        } else {
+                                            // If we have `<%== a(function(){ %>` then we want
+                                            // `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
+                                            buff.push(insert_cmd, "can.view.txt(" + escaped + ",'" + tagName + "'," + status() + ",this,function(){ " + (this.text.escape || '') + "return ", content,
+                                                // If we have a block.
+                                                bracketCount ?
+                                                // Start with startTxt `"var _v1ew = [];"`.
+                                                startTxt :
+                                                // If not, add `doubleParent` to close push and text.
+                                                "}));");
+                                        }
+
+                                        if (rescan && rescan.after && rescan.after.length) {
+                                            put(rescan.after.length);
+                                            rescan = null;
+                                        }
+                                        break;
+                                }
+                                startTag = null;
+                                content = '';
+                                break;
+                            case tmap.templateLeft:
+                                content += tmap.left;
+                                break;
+                            default:
+                                content += token;
+                                break;
+                        }
+                    }
+                    lastToken = token;
+                }
+
+                // Put it together...
+                if (content.length) {
+                    // Should be `content.dump` in Ruby.
+                    put(content);
+                }
+                buff.push(";");
+
+                var template = buff.join(''),
+                    out = {
+                        out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}"
+                    };
+                // Use `eval` instead of creating a function, because it is easier to debug.
+                myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
+
+                return out;
+            }
+        };
+
+        return Scanner;
+    })(__m11, __m14);
+
+    // ## can/view/node_lists.js
+    var __m17 = (function(can) {
+
+        // text node expando test
+        var canExpando = true;
+        try {
+            document.createTextNode('')._ = 0;
+        } catch (ex) {
+            canExpando = false;
+        }
+
+        // a mapping of element ids to nodeList ids
+        var nodeMap = {},
+            // a mapping of ids to text nodes
+            textNodeMap = {},
+            // a mapping of nodeList ids to nodeList
+            nodeListMap = {},
+            expando = "ejs_" + Math.random(),
+            _id = 0,
+            id = function(node) {
+                if (canExpando || node.nodeType !== 3) {
+                    if (node[expando]) {
+                        return node[expando];
+                    } else {
+                        return node[expando] = (node.nodeName ? "element_" : "obj_") + (++_id);
+                    }
+                } else {
+                    for (var textNodeID in textNodeMap) {
+                        if (textNodeMap[textNodeID] === node) {
+                            return textNodeID;
+                        }
+                    }
+
+                    textNodeMap["text_" + (++_id)] = node;
+                    return "text_" + _id;
+                }
+            },
+            // removes a nodeListId from a node's nodeListIds
+            removeNodeListId = function(node, nodeListId) {
+                var nodeListIds = nodeMap[id(node)];
+                if (nodeListIds) {
+                    var index = can.inArray(nodeListId, nodeListIds);
+
+                    if (index >= 0) {
+                        nodeListIds.splice(index, 1);
+                    }
+                    if (!nodeListIds.length) {
+                        delete nodeMap[id(node)];
+                    }
+                }
+            },
+            addNodeListId = function(node, nodeListId) {
+                var nodeListIds = nodeMap[id(node)];
+                if (!nodeListIds) {
+                    nodeListIds = nodeMap[id(node)] = [];
+                }
+                nodeListIds.push(nodeListId);
+            };
+
+        var nodeLists = {
+            id: id,
+            // replaces the contents of one node list with the nodes in another list
+            replace: function(oldNodeList, newNodes) {
+                // for each node in the node list
+                oldNodeList = can.makeArray(oldNodeList);
+
+                // try every set
+                //can.each( oldNodeList, function(node){
+                var node = oldNodeList[0]
+                // for each nodeList the node is in
+                can.each(can.makeArray(nodeMap[id(node)]), function(nodeListId) {
+
+                    // if startNode to endNode is 
+                    // within list, replace that list
+                    // I think the problem is not the WHOLE part is being 
+                    // matched
+                    var nodeList = nodeListMap[nodeListId],
+                        startIndex = can.inArray(node, nodeList),
+                        endIndex = can.inArray(oldNodeList[oldNodeList.length - 1], nodeList);
+
+
+                    // remove this nodeListId from each node
+                    if (startIndex >= 0 && endIndex >= 0) {
+                        for (var i = startIndex; i <= endIndex; i++) {
+                            var n = nodeList[i];
+                            removeNodeListId(n, nodeListId);
+                        }
+                        // swap in new nodes into the nodeLIst
+                        nodeList.splice.apply(nodeList, [startIndex, endIndex - startIndex + 1].concat(newNodes));
+
+                        // tell these new nodes they belong to the nodeList
+                        can.each(newNodes, function(node) {
+                            addNodeListId(node, nodeListId);
+                        });
+                    } else {
+                        nodeLists.unregister(nodeList);
+                    }
+                });
+                //});
+            },
+            // registers a list of nodes
+            register: function(nodeList) {
+                var nLId = id(nodeList);
+                nodeListMap[nLId] = nodeList;
+
+                can.each(nodeList, function(node) {
+                    addNodeListId(node, nLId);
+                });
+
+            },
+            // removes mappings
+            unregister: function(nodeList) {
+                var nLId = id(nodeList);
+                can.each(nodeList, function(node) {
+                    removeNodeListId(node, nLId);
+                });
+                delete nodeListMap[nLId];
+            },
+            nodeMap: nodeMap,
+            nodeListMap: nodeListMap
+        }
+        var ids = function(nodeList) {
+            return nodeList.map(function(n) {
+                return id(n) + ":" + (n.innerHTML || n.nodeValue)
+            })
+        }
+        return nodeLists;
+
+    })(__m3);
+
+    // ## can/view/live.js
+    var __m16 = (function(can, elements, view, nodeLists) {
+        // ## live.js
+        // The live module provides live binding for computes
+        // and can.Observe.List.
+        // Currently, it's API is designed for `can/view/render`, but
+        // it could easily be used for other purposes.
+
+        // ### Helper methods
+        // #### setup
+        // `setup(HTMLElement, bind(data), unbind(data)) -> data`
+        // Calls bind right away, but will call unbind
+        // if the element is "destroyed" (removed from the DOM).
+        var setup = function(el, bind, unbind) {
+            var teardown = function() {
+                unbind(data)
+                can.unbind.call(el, 'destroyed', teardown);
+            },
+                data = {
+                    teardownCheck: function(parent) {
+                        if (!parent) {
+                            teardown();
+                        }
+                    }
+                }
+
+            can.bind.call(el, 'destroyed', teardown);
+            bind(data)
+            return data;
+        },
+            // #### listen
+            // Calls setup, but presets bind and unbind to 
+            // operate on a compute
+            listen = function(el, compute, change) {
+                return setup(el, function() {
+                    compute.bind("change", change);
+                }, function(data) {
+                    compute.unbind("change", change);
+                    if (data.nodeList) {
+                        nodeLists.unregister(data.nodeList);
+                    }
+                });
+            },
+            // #### getAttributeParts
+            // Breaks up a string like foo='bar' into ["foo","'bar'""]
+            getAttributeParts = function(newVal) {
+                return (newVal || "").replace(/['"]/g, '').split('=')
+            }
+            // #### insertElementsAfter
+            // Appends elements after the last item in oldElements.
+        insertElementsAfter = function(oldElements, newFrag) {
+            var last = oldElements[oldElements.length - 1];
+
+            // Insert it in the `document` or `documentFragment`
+            if (last.nextSibling) {
+                last.parentNode.insertBefore(newFrag, last.nextSibling);
+            } else {
+                last.parentNode.appendChild(newFrag);
+            }
+        };
+
+        var live = {
+            nodeLists: nodeLists,
+            list: function(el, list, func, context, parentNode) {
+                // A mapping of the index to an array
+                // of elements that represent the item.
+                // Each array is registered so child or parent
+                // live structures can update the elements
+                var nodesMap = [],
+
+                    add = function(ev, items, index) {
+
+                        // Collect new html and mappings
+                        var frag = document.createDocumentFragment(),
+                            newMappings = [];
+                        can.each(items, function(item) {
+                            var itemHTML = func.call(context, item),
+                                itemFrag = can.view.frag(itemHTML, parentNode);
+
+                            newMappings.push(can.makeArray(itemFrag.childNodes));
+                            frag.appendChild(itemFrag);
+                        })
+
+                        // Inserting at the end of the list
+                        if (!nodesMap[index]) {
+                            insertElementsAfter(
+                                index == 0 ? [text] :
+                                nodesMap[index - 1], frag)
+                        } else {
+                            var el = nodesMap[index][0];
+                            el.parentNode.insertBefore(frag, el)
+                        }
+                        // register each item
+                        can.each(newMappings, function(nodeList) {
+                            nodeLists.register(nodeList)
+                        });
+                        [].splice.apply(nodesMap, [index, 0].concat(newMappings));
+                    },
+                    remove = function(ev, items, index) {
+                        var removedMappings = nodesMap.splice(index, items.length),
+                            itemsToRemove = [];
+
+                        can.each(removedMappings, function(nodeList) {
+                            // add items that we will remove all at once
+                            [].push.apply(itemsToRemove, nodeList)
+                            // Update any parent lists to remove these items
+                            nodeLists.replace(nodeList, []);
+                            // unregister the list
+                            nodeLists.unregister(nodeList);
+
+                        });
+                        can.remove(can.$(itemsToRemove));
+                    },
+                    parentNode = elements.getParentNode(el, parentNode),
+                    text = document.createTextNode("");
+
+                // Setup binding and teardown to add and remove events
+                setup(parentNode, function() {
+                    list.bind("add", add).bind("remove", remove)
+                }, function() {
+                    list.unbind("add", add).unbind("remove", remove);
+                    can.each(nodesMap, function(nodeList) {
+                        nodeLists.unregister(nodeList);
+                    })
+                })
+
+                insertElementsAfter([el], text);
+                can.remove(can.$(el));
+                add({}, list, 0);
+
+            },
+            html: function(el, compute, parentNode) {
+                var parentNode = elements.getParentNode(el, parentNode),
+
+                    data = listen(parentNode, compute, function(ev, newVal, oldVal) {
+                        var attached = nodes[0].parentNode;
+                        // update the nodes in the DOM with the new rendered value
+                        if (attached) {
+                            makeAndPut(newVal);
+                        }
+                        data.teardownCheck(nodes[0].parentNode);
+                    });
+
+                var nodes,
+                    makeAndPut = function(val) {
+                        // create the fragment, but don't hook it up
+                        // we need to insert it into the document first
+                        var frag = can.view.frag(val, parentNode),
+                            // keep a reference to each node
+                            newNodes = can.makeArray(frag.childNodes);
+                        // Insert it in the `document` or `documentFragment`
+                        insertElementsAfter(nodes || [el], frag)
+                        // nodes hasn't been set yet
+                        if (!nodes) {
+                            can.remove(can.$(el));
+                            nodes = newNodes;
+                            // set the teardown nodeList
+                            data.nodeList = nodes;
+                            nodeLists.register(nodes);
+                        } else {
+                            // Update node Array's to point to new nodes
+                            // and then remove the old nodes.
+                            // It has to be in this order for Mootools
+                            // and IE because somehow, after an element
+                            // is removed from the DOM, it loses its
+                            // expando values.
+                            var nodesToRemove = can.makeArray(nodes);
+                            nodeLists.replace(nodes, newNodes);
+                            can.remove(can.$(nodesToRemove));
+                        }
+                    };
+                makeAndPut(compute(), [el]);
+
+            },
+            text: function(el, compute, parentNode) {
+                var parent = elements.getParentNode(el, parentNode);
+
+                // setup listening right away so we don't have to re-calculate value
+                var data = listen(el.parentNode !== parent ? el.parentNode : parent, compute, function(ev, newVal, oldVal) {
+                    // Sometimes this is 'unknown' in IE and will throw an exception if it is
+                    if (typeof node.nodeValue != 'unknown') {
+                        node.nodeValue = "" + newVal;
+                    }
+                    data.teardownCheck(node.parentNode);
+                });
+
+                var node = document.createTextNode(compute());
+
+                if (el.parentNode !== parent) {
+                    parent = el.parentNode;
+                    parent.insertBefore(node, el);
+                    parent.removeChild(el);
+                } else {
+                    parent.insertBefore(node, el);
+                    parent.removeChild(el);
+                }
+            },
+            attributes: function(el, compute, currentValue) {
+                var setAttrs = function(newVal) {
+                    var parts = getAttributeParts(newVal),
+                        newAttrName = parts.shift();
+
+                    // Remove if we have a change and used to have an `attrName`.
+                    if ((newAttrName != attrName) && attrName) {
+                        elements.removeAttr(el, attrName);
+                    }
+                    // Set if we have a new `attrName`.
+                    if (newAttrName) {
+                        elements.setAttr(el, newAttrName, parts.join('='));
+                        attrName = newAttrName;
+                    }
+                }
+
+                listen(el, compute, function(ev, newVal) {
+                    setAttrs(newVal)
+                })
+                // current value has been set
+                if (arguments.length >= 3) {
+                    var attrName = getAttributeParts(currentValue)[0]
+                } else {
+                    setAttrs(compute())
+                }
+            },
+            attributePlaceholder: '__!!__',
+            attributeReplace: /__!!__/g,
+            attribute: function(el, attributeName, compute) {
+                listen(el, compute, function(ev, newVal) {
+                    elements.setAttr(el, attributeName, hook.render());
+                })
+
+                var wrapped = can.$(el),
+                    hooks;
+
+                // Get the list of hookups or create one for this element.
+                // Hooks is a map of attribute names to hookup `data`s.
+                // Each hookup data has:
+                // `render` - A `function` to render the value of the attribute.
+                // `funcs` - A list of hookup `function`s on that attribute.
+                // `batchNum` - The last event `batchNum`, used for performance.
+                hooks = can.data(wrapped, 'hooks');
+                if (!hooks) {
+                    can.data(wrapped, 'hooks', hooks = {});
+                }
+
+                // Get the attribute value.
+                var attr = elements.getAttr(el, attributeName),
+                    // Split the attribute value by the template.
+                    // Only split out the first __!!__ so if we have multiple hookups in the same attribute, 
+                    // they will be put in the right spot on first render
+                    parts = attr.split(live.attributePlaceholder),
+                    goodParts = [],
+                    hook;
+                goodParts.push(parts.shift(),
+                    parts.join(live.attributePlaceholder));
+
+                // If we already had a hookup for this attribute...
+                if (hooks[attributeName]) {
+                    // Just add to that attribute's list of `function`s.
+                    hooks[attributeName].computes.push(compute);
+                } else {
+                    // Create the hookup data.
+                    hooks[attributeName] = {
+                        render: function() {
+                            var i = 0,
+                                // attr doesn't have a value in IE
+                                newAttr = attr ? attr.replace(live.attributeReplace, function() {
+                                    return elements.contentText(hook.computes[i++]());
+                                }) : elements.contentText(hook.computes[i++]());
+                            return newAttr;
+                        },
+                        computes: [compute],
+                        batchNum: undefined
+                    };
+                }
+
+                // Save the hook for slightly faster performance.
+                hook = hooks[attributeName];
+
+                // Insert the value in parts.
+                goodParts.splice(1, 0, compute());
+
+                // Set the attribute.
+                elements.setAttr(el, attributeName, goodParts.join(""));
+
+            }
+        }
+        return live;
+
+    })(__m3, __m14, __m11, __m17);
+
+    // ## can/view/render.js
+    var __m15 = (function(can, elements, live) {
+
+        var pendingHookups = [],
+            tagChildren = function(tagName) {
+                var newTag = elements.tagMap[tagName] || "span";
+                if (newTag === "span") {
+                    //innerHTML in IE doesn't honor leading whitespace after empty elements
+                    return "@@!!@@";
+                }
+                return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">";
+            },
+            contentText = function(input, tag) {
+
+                // If it's a string, return.
+                if (typeof input == 'string') {
+                    return input;
+                }
+                // If has no value, return an empty string.
+                if (!input && input !== 0) {
+                    return '';
+                }
+
+                // If it's an object, and it has a hookup method.
+                var hook = (input.hookup &&
+
+                    // Make a function call the hookup method.
+
+                    function(el, id) {
+                        input.hookup.call(input, el, id);
+                    }) ||
+
+                // Or if it's a `function`, just use the input.
+                (typeof input == 'function' && input);
+
+                // Finally, if there is a `function` to hookup on some dom,
+                // add it to pending hookups.
+                if (hook) {
+                    if (tag) {
+                        return "<" + tag + " " + can.view.hook(hook) + "></" + tag + ">"
+                    } else {
+                        pendingHookups.push(hook);
+                    }
+
+                    return '';
+                }
+
+                // Finally, if all else is `false`, `toString()` it.
+                return "" + input;
+            },
+            // Returns escaped/sanatized content for anything other than a live-binding
+            contentEscape = function(txt) {
+                return (typeof txt == 'string' || typeof txt == 'number') ?
+                    can.esc(txt) :
+                    contentText(txt);
+            };
+
+        var current;
+
+        can.extend(can.view, {
+                live: live,
+                setupLists: function() {
+
+                    var old = can.view.lists,
+                        data;
+
+                    can.view.lists = function(list, renderer) {
+                        data = {
+                            list: list,
+                            renderer: renderer
+                        }
+                    }
+                    return function() {
+                        can.view.lists = old;
+                        return data;
+                    }
+                },
+                pending: function() {
+                    // TODO, make this only run for the right tagName
+                    var hooks = pendingHookups.slice(0);
+                    lastHookups = hooks;
+                    pendingHookups = [];
+                    return can.view.hook(function(el) {
+                        can.each(hooks, function(fn) {
+                            fn(el);
+                        });
+                    });
+                },
+
+
+                txt: function(escape, tagName, status, self, func) {
+                    var listTeardown = can.view.setupLists(),
+                        emptyHandler = function() {},
+                        unbind = function() {
+                            compute.unbind("change", emptyHandler)
+                        };
+
+                    var compute = can.compute(func, self, false);
+                    // bind to get and temporarily cache the value
+                    compute.bind("change", emptyHandler);
+                    // call the "wrapping" function and get the binding information
+                    var tag = (elements.tagMap[tagName] || "span"),
+                        listData = listTeardown(),
+                        value = compute();
+
+
+                    if (listData) {
+                        return "<" + tag + can.view.hook(function(el, parentNode) {
+                            live.list(el, listData.list, listData.renderer, self, parentNode);
+                        }) + "></" + tag + ">";
+                    }
+
+                    // If we had no observes just return the value returned by func.
+                    if (!compute.hasDependencies) {
+                        unbind();
+                        return (escape || status !== 0 ? contentEscape : contentText)(value, status === 0 && tag);
+                    }
+
+                    // the property (instead of innerHTML elements) to adjust. For
+                    // example options should use textContent
+                    var contentProp = elements.tagToContentPropMap[tagName];
+
+
+                    // The magic tag is outside or between tags.
+                    if (status === 0 && !contentProp) {
+                        // Return an element tag with a hookup in place of the content
+                        return "<" + tag + can.view.hook(
+                            escape ?
+                            // If we are escaping, replace the parentNode with 
+                            // a text node who's value is `func`'s return value.
+
+                            function(el, parentNode) {
+                                live.text(el, compute, parentNode);
+                                unbind();
+                            } :
+                            // If we are not escaping, replace the parentNode with a
+                            // documentFragment created as with `func`'s return value.
+
+                            function(el, parentNode) {
+                                live.html(el, compute, parentNode);
+                                unbind();
+                                //children have to be properly nested HTML for buildFragment to work properly
+                            }) + ">" + tagChildren(tag) + "</" + tag + ">";
+                        // In a tag, but not in an attribute
+                    } else if (status === 1) {
+                        // remember the old attr name
+                        pendingHookups.push(function(el) {
+                            live.attributes(el, compute, compute());
+                            unbind();
+                        });
+                        return compute();
+                    } else { // In an attribute...
+                        var attributeName = status === 0 ? contentProp : status;
+                        // if the magic tag is inside the element, like `<option><% TAG %></option>`,
+                        // we add this hookup to the last element (ex: `option`'s) hookups.
+                        // Otherwise, the magic tag is in an attribute, just add to the current element's
+                        // hookups.
+                        (status === 0 ? lastHookups : pendingHookups).push(function(el) {
+                            live.attribute(el, attributeName, compute);
+                            unbind();
+                        });
+                        return live.attributePlaceholder;
+                    }
+                }
+            });
+
+        return can;
+    })(__m11, __m14, __m16, __m2);
+
+    // ## can/view/ejs/ejs.js
+    var __m12 = (function(can) {
+        // ## ejs.js
+        // `can.EJS`  
+        // _Embedded JavaScript Templates._
+
+        // Helper methods.
+        var extend = can.extend,
+            EJS = function(options) {
+                // Supports calling EJS without the constructor
+                // This returns a function that renders the template.
+                if (this.constructor != EJS) {
+                    var ejs = new EJS(options);
+                    return function(data, helpers) {
+                        return ejs.render(data, helpers);
+                    };
+                }
+                // If we get a `function` directly, it probably is coming from
+                // a `steal`-packaged view.
+                if (typeof options == "function") {
+                    this.template = {
+                        fn: options
+                    };
+                    return;
+                }
+                // Set options on self.
+                extend(this, options);
+                this.template = this.scanner.scan(this.text, this.name);
+            };
+
+        can.EJS = EJS;
+
+
+        EJS.prototype.
+
+        render = function(object, extraHelpers) {
+            object = object || {};
+            return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
+        };
+
+        extend(EJS.prototype, {
+
+                scanner: new can.view.Scanner({
+
+                        tokens: [
+                            ["templateLeft", "<%%"], // Template
+                            ["templateRight", "%>"], // Right Template
+                            ["returnLeft", "<%=="], // Return Unescaped
+                            ["escapeLeft", "<%="], // Return Escaped
+                            ["commentLeft", "<%#"], // Comment
+                            ["left", "<%"], // Run --- this is hack for now
+                            ["right", "%>"], // Right -> All have same FOR Mustache ...
+                            ["returnRight", "%>"]
+                        ],
+
+
+                        transform: function(source) {
+                            return source.replace(/<%([\s\S]+?)%>/gm, function(whole, part) {
+                                var brackets = [],
+                                    foundBracketPair,
+                                    i;
+
+                                // Look for brackets (for removing self-contained blocks)
+                                part.replace(/[{}]/gm, function(bracket, offset) {
+                                    brackets.push([bracket, offset]);
+                                });
+
+                                // Remove bracket pairs from the list of replacements
+                                do {
+                                    foundBracketPair = false;
+                                    for (i = brackets.length - 2; i >= 0; i--) {
+                                        if (brackets[i][0] == '{' && brackets[i + 1][0] == '}') {
+                                            brackets.splice(i, 2);
+                                            foundBracketPair = true;
+                                            break;
+                                        }
+                                    }
+                                } while (foundBracketPair);
+
+                                // Unmatched brackets found, inject EJS tags
+                                if (brackets.length >= 2) {
+                                    var result = ['<%'],
+                                        bracket,
+                                        last = 0;
+                                    for (i = 0; bracket = brackets[i]; i++) {
+                                        result.push(part.substring(last, last = bracket[1]));
+                                        if ((bracket[0] == '{' && i < brackets.length - 1) || (bracket[0] == '}' && i > 0)) {
+                                            result.push(bracket[0] == '{' ? '{ %><% ' : ' %><% }');
+                                        } else {
+                                            result.push(bracket[0]);
+                                        }
+                                        ++last;
+                                    }
+                                    result.push(part.substring(last), '%>');
+                                    return result.join('');
+                                }
+                                // Otherwise return the original
+                                else {
+                                    return '<%' + part + '%>';
+                                }
+                            });
+                        }
+                    })
+            });
+
+        EJS.Helpers = function(data, extras) {
+            this._data = data;
+            this._extras = extras;
+            extend(this, extras);
+        };
+
+
+        EJS.Helpers.prototype = {
+            // TODO Deprecated!!
+            list: function(list, cb) {
+
+                can.each(list, function(item, i) {
+                    cb(item, i, list)
+                })
+            },
+            each: function(list, cb) {
+                // Normal arrays don't get live updated
+                if (can.isArray(list)) {
+                    this.list(list, cb);
+                } else {
+                    can.view.lists(list, cb);
+                }
+            }
+        };
+
+        // Options for `steal`'s build.
+        can.view.register({
+                suffix: "ejs",
+                // returns a `function` that renders the view.
+                script: function(id, src) {
+                    return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
+                            text: src,
+                            name: id
+                        }).template.out + " })";
+                },
+                renderer: function(id, text) {
+                    return EJS({
+                            text: text,
+                            name: id
+                        });
+                }
+            });
+
+        return can;
+    })(__m3, __m11, __m2, __m9, __m13, __m15);
+
+    // ## can/control/control.js
+    var __m18 = (function(can) {
+        // ## control.js
+        // `can.Control`  
+        // _Controller_
+
+        // Binds an element, returns a function that unbinds.
+        var bind = function(el, ev, callback) {
+
+            can.bind.call(el, ev, callback);
+
+            return function() {
+                can.unbind.call(el, ev, callback);
+            };
+        },
+            isFunction = can.isFunction,
+            extend = can.extend,
+            each = can.each,
+            slice = [].slice,
+            paramReplacer = /\{([^\}]+)\}/g,
+            special = can.getObject("$.event.special", [can]) || {},
+
+            // Binds an element, returns a function that unbinds.
+            delegate = function(el, selector, ev, callback) {
+                can.delegate.call(el, selector, ev, callback);
+                return function() {
+                    can.undelegate.call(el, selector, ev, callback);
+                };
+            },
+
+            // Calls bind or unbind depending if there is a selector.
+            binder = function(el, ev, callback, selector) {
+                return selector ?
+                    delegate(el, can.trim(selector), ev, callback) :
+                    bind(el, ev, callback);
+            },
+
+            basicProcessor;
+
+        var Control = can.Control = can.Construct(
+
+            {
+                // Setup pre-processes which methods are event listeners.
+
+                setup: function() {
+
+                    // Allow contollers to inherit "defaults" from super-classes as it 
+                    // done in `can.Construct`
+                    can.Construct.setup.apply(this, arguments);
+
+                    // If you didn't provide a name, or are `control`, don't do anything.
+                    if (can.Control) {
+
+                        // Cache the underscored names.
+                        var control = this,
+                            funcName;
+
+                        // Calculate and cache actions.
+                        control.actions = {};
+                        for (funcName in control.prototype) {
+                            if (control._isAction(funcName)) {
+                                control.actions[funcName] = control._action(funcName);
+                            }
+                        }
+                    }
+                },
+
+                // Moves `this` to the first argument, wraps it with `jQuery` if it's an element
+                _shifter: function(context, name) {
+
+                    var method = typeof name == "string" ? context[name] : name;
+
+                    if (!isFunction(method)) {
+                        method = context[method];
+                    }
+
+                    return function() {
+                        context.called = name;
+                        return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0)));
+                    };
+                },
+
+                // Return `true` if is an action.
+
+                _isAction: function(methodName) {
+
+                    var val = this.prototype[methodName],
+                        type = typeof val;
+                    // if not the constructor
+                    return (methodName !== 'constructor') &&
+                    // and is a function or links to a function
+                    (type == "function" || (type == "string" && isFunction(this.prototype[val]))) &&
+                    // and is in special, a processor, or has a funny character
+                    !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName));
+                },
+                // Takes a method name and the options passed to a control
+                // and tries to return the data necessary to pass to a processor
+                // (something that binds things).
+
+                _action: function(methodName, options) {
+
+                    // If we don't have options (a `control` instance), we'll run this 
+                    // later.  
+                    paramReplacer.lastIndex = 0;
+                    if (options || !paramReplacer.test(methodName)) {
+                        // If we have options, run sub to replace templates `{}` with a
+                        // value from the options or the window
+                        var convertedName = options ? can.sub(methodName, [options, window]) : methodName;
+                        if (!convertedName) {
+                            return null;
+                        }
+                        // If a `{}` template resolves to an object, `convertedName` will be
+                        // an array
+                        var arr = can.isArray(convertedName),
+
+                            // Get the name
+                            name = arr ? convertedName[1] : convertedName,
+
+                            // Grab the event off the end
+                            parts = name.split(/\s+/g),
+                            event = parts.pop();
+
+                        return {
+                            processor: processors[event] || basicProcessor,
+                            parts: [name, parts.join(" "), event],
+                            delegate: arr ? convertedName[0] : undefined
+                        };
+                    }
+                },
+                // An object of `{eventName : function}` pairs that Control uses to 
+                // hook up events auto-magically.
+
+                processors: {},
+                // A object of name-value pairs that act as default values for a 
+                // control instance
+                defaults: {}
+
+            }, {
+
+                // Sets `this.element`, saves the control in `data, binds event
+                // handlers.
+
+                setup: function(element, options) {
+
+                    var cls = this.constructor,
+                        pluginname = cls.pluginName || cls._fullName,
+                        arr;
+
+                    // Want the raw element here.
+                    this.element = can.$(element)
+
+                    if (pluginname && pluginname !== 'can_control') {
+                        // Set element and `className` on element.
+                        this.element.addClass(pluginname);
+                    }
+
+                    (arr = can.data(this.element, "controls")) || can.data(this.element, "controls", arr = []);
+                    arr.push(this);
+
+                    // Option merging.
+
+                    this.options = extend({}, cls.defaults, options);
+
+                    // Bind all event handlers.
+                    this.on();
+
+                    // Gets passed into `init`.
+
+                    return [this.element, this.options];
+                },
+
+                on: function(el, selector, eventName, func) {
+                    if (!el) {
+
+                        // Adds bindings.
+                        this.off();
+
+                        // Go through the cached list of actions and use the processor 
+                        // to bind
+                        var cls = this.constructor,
+                            bindings = this._bindings,
+                            actions = cls.actions,
+                            element = this.element,
+                            destroyCB = can.Control._shifter(this, "destroy"),
+                            funcName, ready;
+
+                        for (funcName in actions) {
+                            // Only push if we have the action and no option is `undefined`
+                            if (actions.hasOwnProperty(funcName) &&
+                                (ready = actions[funcName] || cls._action(funcName, this.options))) {
+                                bindings.push(ready.processor(ready.delegate || element,
+                                        ready.parts[2], ready.parts[1], funcName, this));
+                            }
+                        }
+
+
+                        // Setup to be destroyed...  
+                        // don't bind because we don't want to remove it.
+                        can.bind.call(element, "destroyed", destroyCB);
+                        bindings.push(function(el) {
+                            can.unbind.call(el, "destroyed", destroyCB);
+                        });
+                        return bindings.length;
+                    }
+
+                    if (typeof el == 'string') {
+                        func = eventName;
+                        eventName = selector;
+                        selector = el;
+                        el = this.element;
+                    }
+
+                    if (func === undefined) {
+                        func = eventName;
+                        eventName = selector;
+                        selector = null;
+                    }
+
+                    if (typeof func == 'string') {
+                        func = can.Control._shifter(this, func);
+                    }
+
+                    this._bindings.push(binder(el, eventName, func, selector));
+
+                    return this._bindings.length;
+                },
+                // Unbinds all event handlers on the controller.
+
+                off: function() {
+                    var el = this.element[0]
+                    each(this._bindings || [], function(value) {
+                        value(el);
+                    });
+                    // Adds bindings.
+                    this._bindings = [];
+                },
+                // Prepares a `control` for garbage collection
+
+                destroy: function() {
+                    //Control already destroyed
+                    if (this.element === null) {
+
+                        return;
+                    }
+                    var Class = this.constructor,
+                        pluginName = Class.pluginName || Class._fullName,
+                        controls;
+
+                    // Unbind bindings.
+                    this.off();
+
+                    if (pluginName && pluginName !== 'can_control') {
+                        // Remove the `className`.
+                        this.element.removeClass(pluginName);
+                    }
+
+                    // Remove from `data`.
+                    controls = can.data(this.element, "controls");
+                    controls.splice(can.inArray(this, controls), 1);
+
+                    can.trigger(this, "destroyed"); // In case we want to know if the `control` is removed.
+
+                    this.element = null;
+                }
+            });
+
+        var processors = can.Control.processors,
+            // Processors do the binding.
+            // They return a function that unbinds when called.  
+            // The basic processor that binds events.
+            basicProcessor = function(el, event, selector, methodName, control) {
+                return binder(el, event, can.Control._shifter(control, methodName), selector);
+            };
+
+        // Set common events to be processed as a `basicProcessor`
+        each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
+                "keypress", "mousedown", "mousemove", "mouseout", "mouseover",
+                "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
+                "focusout", "mouseenter", "mouseleave",
+                // #104 - Add touch events as default processors
+                // TOOD feature detect?
+                "touchstart", "touchmove", "touchcancel", "touchend", "touchleave"
+            ], function(v) {
+                processors[v] = basicProcessor;
+            });
+
+        return Control;
+    })(__m3, __m1);
+
+    // ## can/util/string/deparam/deparam.js
+    var __m20 = (function(can) {
+
+        // ## deparam.js  
+        // `can.deparam`  
+        // _Takes a string of name value pairs and returns a Object literal that represents those params._
+        var digitTest = /^\d+$/,
+            keyBreaker = /([^\[\]]+)|(\[\])/g,
+            paramTest = /([^?#]*)(#.*)?$/,
+            prep = function(str) {
+                return decodeURIComponent(str.replace(/\+/g, " "));
+            };
+
+
+        can.extend(can, {
+
+                deparam: function(params) {
+
+                    var data = {},
+                        pairs, lastPart;
+
+                    if (params && paramTest.test(params)) {
+
+                        pairs = params.split('&'),
+
+                        can.each(pairs, function(pair) {
+
+                            var parts = pair.split('='),
+                                key = prep(parts.shift()),
+                                value = prep(parts.join("=")),
+                                current = data;
+
+                            if (key) {
+                                parts = key.match(keyBreaker);
+
+                                for (var j = 0, l = parts.length - 1; j < l; j++) {
+                                    if (!current[parts[j]]) {
+                                        // If what we are pointing to looks like an `array`
+                                        current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] == "[]" ? [] : {};
+                                    }
+                                    current = current[parts[j]];
+                                }
+                                lastPart = parts.pop();
+                                if (lastPart == "[]") {
+                                    current.push(value);
+                                } else {
+                                    current[lastPart] = value;
+                                }
+                            }
+                        });
+                    }
+                    return data;
+                }
+            });
+        return can;
+    })(__m3, __m2);
+
+    // ## can/route/route.js
+    var __m19 = (function(can) {
+
+        // ## route.js  
+        // `can.route`  
+        // _Helps manage browser history (and client state) by synchronizing the 
+        // `window.location.hash` with a `can.Observe`._  
+        // Helper methods used for matching routes.
+        var
+        // `RegExp` used to match route variables of the type ':name'.
+        // Any word character or a period is matched.
+        matcher = /\:([\w\.]+)/g,
+            // Regular expression for identifying &amp;key=value lists.
+            paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
+            // Converts a JS Object into a list of parameters that can be 
+            // inserted into an html element tag.
+            makeProps = function(props) {
+                var tags = [];
+                can.each(props, function(val, name) {
+                    tags.push((name === 'className' ? 'class' : name) + '="' +
+                        (name === "href" ? val : can.esc(val)) + '"');
+                });
+                return tags.join(" ");
+            },
+            // Checks if a route matches the data provided. If any route variable
+            // is not present in the data, the route does not match. If all route
+            // variables are present in the data, the number of matches is returned 
+            // to allow discerning between general and more specific routes. 
+            matchesData = function(route, data) {
+                var count = 0,
+                    i = 0,
+                    defaults = {};
+                // look at default values, if they match ...
+                for (var name in route.defaults) {
+                    if (route.defaults[name] === data[name]) {
+                        // mark as matched
+                        defaults[name] = 1;
+                        count++;
+                    }
+                }
+                for (; i < route.names.length; i++) {
+                    if (!data.hasOwnProperty(route.names[i])) {
+                        return -1;
+                    }
+                    if (!defaults[route.names[i]]) {
+                        count++;
+                    }
+
+                }
+
+                return count;
+            },
+            onready = !0,
+            location = window.location,
+            wrapQuote = function(str) {
+                return (str + '').replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1");
+            },
+            each = can.each,
+            extend = can.extend;
+
+        can.route = function(url, defaults) {
+            defaults = defaults || {};
+            // Extract the variable names and replace with `RegExp` that will match
+            // an atual URL with values.
+            var names = [],
+                test = url.replace(matcher, function(whole, name, i) {
+                    names.push(name);
+                    var next = "\\" + (url.substr(i + whole.length, 1) || can.route._querySeparator);
+                    // a name without a default value HAS to have a value
+                    // a name that has a default value can be empty
+                    // The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
+                    return "([^" + next + "]" + (defaults[name] ? "*" : "+") + ")";
+                });
+
+            // Add route in a form that can be easily figured out.
+            can.route.routes[url] = {
+                // A regular expression that will match the route when variable values 
+                // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
+                // will match for any value of `:page` and `:type` (word chars or period).
+                test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"),
+                // The original URL, same as the index for this entry in routes.
+                route: url,
+                // An `array` of all the variable names in this route.
+                names: names,
+                // Default values provided for the variables.
+                defaults: defaults,
+                // The number of parts in the URL separated by `/`.
+                length: url.split('/').length
+            };
+            return can.route;
+        };
+
+
+        extend(can.route, {
+
+                _querySeparator: '&',
+                _paramsMatcher: paramsMatcher,
+
+
+                param: function(data, _setRoute) {
+                    // Check if the provided data keys match the names in any routes;
+                    // Get the one with the most matches.
+                    var route,
+                        // Need to have at least 1 match.
+                        matches = 0,
+                        matchCount,
+                        routeName = data.route,
+                        propCount = 0;
+
+                    delete data.route;
+
+                    each(data, function() {
+                        propCount++;
+                    });
+                    // Otherwise find route.
+                    each(can.route.routes, function(temp, name) {
+                        // best route is the first with all defaults matching
+
+
+                        matchCount = matchesData(temp, data);
+                        if (matchCount > matches) {
+                            route = temp;
+                            matches = matchCount;
+                        }
+                        if (matchCount >= propCount) {
+                            return false;
+                        }
+                    });
+                    // If we have a route name in our `can.route` data, and it's
+                    // just as good as what currently matches, use that
+                    if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) {
+                        route = can.route.routes[routeName];
+                    }
+                    // If this is match...
+                    if (route) {
+                        var cpy = extend({}, data),
+                            // Create the url by replacing the var names with the provided data.
+                            // If the default value is found an empty string is inserted.
+                            res = route.route.replace(matcher, function(whole, name) {
+                                delete cpy[name];
+                                return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]);
+                            }),
+                            after;
+                        // Remove matching default values
+                        each(route.defaults, function(val, name) {
+                            if (cpy[name] === val) {
+                                delete cpy[name];
+                            }
+                        });
+
+                        // The remaining elements of data are added as 
+                        // `&amp;` separated parameters to the url.
+                        after = can.param(cpy);
+                        // if we are paraming for setting the hash
+                        // we also want to make sure the route value is updated
+                        if (_setRoute) {
+                            can.route.attr('route', route.route);
+                        }
+                        return res + (after ? can.route._querySeparator + after : "");
+                    }
+                    // If no route was found, there is no hash URL, only paramters.
+                    return can.isEmptyObject(data) ? "" : can.route._querySeparator + can.param(data);
+                },
+
+                deparam: function(url) {
+                    // See if the url matches any routes by testing it against the `route.test` `RegExp`.
+                    // By comparing the URL length the most specialized route that matches is used.
+                    var route = {
+                        length: -1
+                    };
+                    each(can.route.routes, function(temp, name) {
+                        if (temp.test.test(url) && temp.length > route.length) {
+                            route = temp;
+                        }
+                    });
+                    // If a route was matched.
+                    if (route.length > -1) {
+
+                        var // Since `RegExp` backreferences are used in `route.test` (parens)
+                        // the parts will contain the full matched string and each variable (back-referenced) value.
+                        parts = url.match(route.test),
+                            // Start will contain the full matched string; parts contain the variable values.
+                            start = parts.shift(),
+                            // The remainder will be the `&amp;key=value` list at the end of the URL.
+                            remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)),
+                            // If there is a remainder and it contains a `&amp;key=value` list deparam it.
+                            obj = (remainder && can.route._paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {};
+
+                        // Add the default values for this route.
+                        obj = extend(true, {}, route.defaults, obj);
+                        // Overwrite each of the default values in `obj` with those in 
+                        // parts if that part is not empty.
+                        each(parts, function(part, i) {
+                            if (part && part !== can.route._querySeparator) {
+                                obj[route.names[i]] = decodeURIComponent(part);
+                            }
+                        });
+                        obj.route = route.route;
+                        return obj;
+                    }
+                    // If no route was matched, it is parsed as a `&amp;key=value` list.
+                    if (url.charAt(0) !== can.route._querySeparator) {
+                        url = can.route._querySeparator + url;
+                    }
+                    return can.route._paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {};
+                },
+
+                data: new can.Observe({}),
+
+                routes: {},
+
+                ready: function(val) {
+                    if (val === false) {
+                        onready = val;
+                    }
+                    if (val === true || onready === true) {
+                        can.route._setup();
+                        setState();
+                    }
+                    return can.route;
+                },
+
+                url: function(options, merge) {
+                    if (merge) {
+                        options = extend({}, curParams, options)
+                    }
+                    return "#!" + can.route.param(options);
+                },
+
+                link: function(name, options, props, merge) {
+                    return "<a " + makeProps(
+                        extend({
+                                href: can.route.url(options, merge)
+                            }, props)) + ">" + name + "</a>";
+                },
+
+                current: function(options) {
+                    return location.hash == "#!" + can.route.param(options)
+                },
+                _setup: function() {
+                    // If the hash changes, update the `can.route.data`.
+                    can.bind.call(window, 'hashchange', setState);
+                },
+                _getHash: function() {
+                    return location.href.split(/#!?/)[1] || "";
+                },
+                _setHash: function(serialized) {
+                    var path = (can.route.param(serialized, true));
+                    location.hash = "#!" + path;
+                    return path;
+                }
+            });
+
+
+        // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
+        // instead act on the `can.route.data` observe.
+        each(['bind', 'unbind', 'delegate', 'undelegate', 'attr', 'removeAttr'], function(name) {
+            can.route[name] = function() {
+                // `delegate` and `undelegate` require
+                // the `can/observe/delegate` plugin
+                if (!can.route.data[name]) {
+                    return;
+                }
+
+                return can.route.data[name].apply(can.route.data, arguments);
+            }
+        })
+
+        var // A ~~throttled~~ debounced function called multiple times will only fire once the
+        // timer runs down. Each call resets the timer.
+        timer,
+            // Intermediate storage for `can.route.data`.
+            curParams,
+            // Deparameterizes the portion of the hash of interest and assign the
+            // values to the `can.route.data` removing existing values no longer in the hash.
+            // setState is called typically by hashchange which fires asynchronously
+            // So it's possible that someone started changing the data before the 
+            // hashchange event fired.  For this reason, it will not set the route data
+            // if the data is changing or the hash already matches the hash that was set.
+            setState = can.route.setState = function() {
+                var hash = can.route._getHash();
+                curParams = can.route.deparam(hash);
+
+                // if the hash data is currently changing, or
+                // the hash is what we set it to anyway, do NOT change the hash
+                if (!changingData || hash !== lastHash) {
+                    can.route.attr(curParams, true);
+                }
+            },
+            // The last hash caused by a data change
+            lastHash,
+            // Are data changes pending that haven't yet updated the hash
+            changingData;
+
+        // If the `can.route.data` changes, update the hash.
+        // Using `.serialize()` retrieves the raw data contained in the `observable`.
+        // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
+        // This might be able to use batchNum and avoid this.
+        can.route.bind("change", function(ev, attr) {
+            // indicate that data is changing
+            changingData = 1;
+            clearTimeout(timer);
+            timer = setTimeout(function() {
+                // indicate that the hash is set to look like the data
+                changingData = 0;
+                var serialized = can.route.data.serialize();
+
+                lastHash = can.route._setHash(serialized);
+            }, 1);
+        });
+        // `onready` event...
+        can.bind.call(document, "ready", can.route.ready);
+
+        // Libraries other than jQuery don't execute the document `ready` listener
+        // if we are already DOM ready
+        if ((document.readyState === 'complete' || document.readyState === "interactive") && onready) {
+            can.route.ready();
+        }
+
+        // extend route to have a similar property 
+        // that is often checked in mustache to determine
+        // an object's observability
+        can.route.constructor.canMakeObserve = can.Observe.canMakeObserve;
+
+        return can.route;
+    })(__m3, __m7, __m20);
+
+    // ## can/control/route/route.js
+    var __m21 = (function(can) {
+
+        // ## control/route.js  
+        // _Controller route integration._
+
+        can.Control.processors.route = function(el, event, selector, funcName, controller) {
+            selector = selector || "";
+            can.route(selector);
+            var batchNum,
+                check = function(ev, attr, how) {
+                    if (can.route.attr('route') === (selector) &&
+                        (ev.batchNum === undefined || ev.batchNum !== batchNum)) {
+
+                        batchNum = ev.batchNum;
+
+                        var d = can.route.attr();
+                        delete d.route;
+                        if (can.isFunction(controller[funcName])) {
+                            controller[funcName](d);
+                        } else {
+                            controller[controller[funcName]](d);
+                        }
+
+                    }
+                };
+            can.route.bind('change', check);
+            return function() {
+                can.route.unbind('change', check);
+            };
+        };
+
+        return can;
+    })(__m3, __m19, __m18);
+
+    // ## can/util/object/object.js
+    var __m23 = (function(can) {
+
+        var isArray = can.isArray,
+            // essentially returns an object that has all the must have comparisons ...
+            // must haves, do not return true when provided undefined
+            cleanSet = function(obj, compares) {
+                var copy = can.extend({}, obj);
+                for (var prop in copy) {
+                    var compare = compares[prop] === undefined ? compares["*"] : compares[prop];
+                    if (same(copy[prop], undefined, compare)) {
+                        delete copy[prop]
+                    }
+                }
+                return copy;
+            },
+            propCount = function(obj) {
+                var count = 0;
+                for (var prop in obj) count++;
+                return count;
+            };
+
+        can.Object = {};
+
+        var same = can.Object.same = function(a, b, compares, aParent, bParent, deep) {
+            var aType = typeof a,
+                aArray = isArray(a),
+                comparesType = typeof compares,
+                compare;
+
+            if (comparesType == 'string' || compares === null) {
+                compares = compareMethods[compares];
+                comparesType = 'function'
+            }
+            if (comparesType == 'function') {
+                return compares(a, b, aParent, bParent)
+            }
+            compares = compares || {};
+
+            if (a instanceof Date) {
+                return a === b;
+            }
+            if (deep === -1) {
+                return aType === 'object' || a === b;
+            }
+            if (aType !== typeof b || aArray !== isArray(b)) {
+                return false;
+            }
+            if (a === b) {
+                return true;
+            }
+            if (aArray) {
+                if (a.length !== b.length) {
+                    return false;
+                }
+                for (var i = 0; i < a.length; i++) {
+                    compare = compares[i] === undefined ? compares["*"] : compares[i]
+                    if (!same(a[i], b[i], a, b, compare)) {
+                        return false;
+                    }
+                };
+                return true;
+            } else if (aType === "object" || aType === 'function') {
+                var bCopy = can.extend({}, b);
+                for (var prop in a) {
+                    compare = compares[prop] === undefined ? compares["*"] : compares[prop];
+                    if (!same(a[prop], b[prop], compare, a, b, deep === false ? -1 : undefined)) {
+                        return false;
+                    }
+                    delete bCopy[prop];
+                }
+                // go through bCopy props ... if there is no compare .. return false
+                for (prop in bCopy) {
+                    if (compares[prop] === undefined || !same(undefined, b[prop], compares[prop], a, b, deep === false ? -1 : undefined)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+            return false;
+        };
+
+        can.Object.subsets = function(checkSet, sets, compares) {
+            var len = sets.length,
+                subsets = [],
+                checkPropCount = propCount(checkSet),
+                setLength;
+
+            for (var i = 0; i < len; i++) {
+                //check this subset
+                var set = sets[i];
+                if (can.Object.subset(checkSet, set, compares)) {
+                    subsets.push(set)
+                }
+            }
+            return subsets;
+        };
+
+        can.Object.subset = function(subset, set, compares) {
+            // go through set {type: 'folder'} and make sure every property
+            // is in subset {type: 'folder', parentId :5}
+            // then make sure that set has fewer properties
+            // make sure we are only checking 'important' properties
+            // in subset (ones that have to have a value)
+
+            var setPropCount = 0,
+                compares = compares || {};
+
+            for (var prop in set) {
+
+                if (!same(subset[prop], set[prop], compares[prop], subset, set)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        var compareMethods = {
+            "null": function() {
+                return true;
+            },
+            i: function(a, b) {
+                return ("" + a).toLowerCase() == ("" + b).toLowerCase()
+            }
+        }
+
+        return can.Object;
+
+    })(__m3);
+
+    // ## can/observe/backup/backup.js
+    var __m22 = (function(can) {
+        var flatProps = function(a) {
+            var obj = {};
+            for (var prop in a) {
+                if (typeof a[prop] !== 'object' || a[prop] === null || a[prop] instanceof Date) {
+                    obj[prop] = a[prop]
+                }
+            }
+            return obj;
+        };
+
+        can.extend(can.Observe.prototype, {
+
+
+                backup: function() {
+                    this._backupStore = this._attrs();
+                    return this;
+                },
+
+
+                isDirty: function(checkAssociations) {
+                    return this._backupStore && !can.Object.same(this._attrs(),
+                        this._backupStore,
+                        undefined,
+                        undefined,
+                        undefined, !! checkAssociations);
+                },
+
+
+                restore: function(restoreAssociations) {
+                    var props = restoreAssociations ? this._backupStore : flatProps(this._backupStore)
+
+                    if (this.isDirty(restoreAssociations)) {
+                        this._attrs(props);
+                    }
+
+                    return this;
+                }
+
+            })
+
+        return can.Observe;
+    })(__m3, __m7, __m23);
+
+    window['can'] = __m5;
+})();
\ No newline at end of file