Commits

Aljoša Mohorović committed fe7598f

initial browser history support (back/fw nav).
added new jquery version and canjs framework.

Comments (0)

Files changed (73)

 build
 dist
 *.egg-info
+env

_jitviewer/app.py

             loops = loops[:CUTOFF]
 
         qt_workaround = ('Qt/4.7.2' in flask.request.user_agent.string)
-        return flask.render_template('index.html', loops=loops,
+        return flask.render_template("index.html", loops=loops,
                                      filename=self.filename,
                                      qt_workaround=qt_workaround,
                                      extra_data=extra_data)
     app.route('/loop')(server.loop)
     if run_app:
         def run():
-            app.run(use_reloader=False, host='0.0.0.0', port=port)
+            app.run(use_reloader=True, host='0.0.0.0', port=port)
 
         if server_mode:
             run()

_jitviewer/display.py

         self.lines = []
         html = []
         for v in loop.inputargs:
-            html.append(cssclass(v, v, onmouseover='highlight_var(this)', onmouseout='disable_var(this)'))
+            #html.append(cssclass(v, v, onmouseover='highlight_var(this)', onmouseout='disable_var(this)'))
+            html.append(cssclass(v, v, data={'name': v}))
         self.inputargs = " ".join(html)
         self.firstlineno = code.co_firstlineno
         for i, line in enumerate(source.split("\n")):

_jitviewer/parser.py

 
 def cssclass(cls, s, **kwds):
     cls = re.sub("[^\w]", "_", cls)
-    attrs = ['%s="%s"' % (name, value) for name, value in kwds.iteritems()]
+    attrs = []
+    if 'data' in kwds:
+        data = kwds.pop('data')
+        attrs += ['data-%s="%s"' % (name, value) for name, value in data.iteritems()]
+    attrs += ['%s="%s"' % (name, value) for name, value in kwds.iteritems()]
     return '<span class="%s" %s>%s</span>' % (cls, ' '.join(attrs),
                                               cgi.escape(s))
 
         return Html(s)
 
     def wrap_html(self, v):
-        return cssclass(v, v, onmouseover='highlight_var(this)', onmouseout='disable_var(this)')
+        #return cssclass(v, v, onmouseover='highlight_var(this)', onmouseout='disable_var(this)')
+        return cssclass(v, v, data={'name': v})
 
     for bin_op, name in [('==', 'int_eq'),
                          ('!=', 'int_ne'),
                                      field, self.wrap_html(self.args[1]))
 
     def repr_jump(self):
-        return ("<a href='' onclick='show_loop(\"%s\");return false'>" % self.descr
+        return ("<a href='#' data-name='%s'>" % self.descr
                 + self.default_repr() + "</a>")
 
     def default_repr(self):

_jitviewer/static/app.js

+MainDisplay = can.Control({
+    init: function(element, options){
+        this.highlight_class = "variable_highlight";
+    },
+    "a[data-name] click": function(el, ev) {
+        ev.preventDefault();
+        var name = el.data('name');
+        if(name) {
+            can.route.attr('name', name);
+        }
+    },
+    ".operations a.bridgelink click": function(el, ev) {
+        ev.preventDefault();
+        var name = el.data('name');
+        if(name) {
+            can.route.attr('name', name);
+        }
+    },
+    ".operations .single-operation a click": function(el, ev) {
+        ev.preventDefault();
+        var name = el.data('name');
+        if(name) {
+            can.route.attr('name', name);
+        }
+    },
+    ".operations .single-operation span mouseenter": function(el, ev) {
+        var name = el.data('name');
+        if(name) {
+            var s = '.operations .single-operation span[data-name="'+name+'"]';
+            this.element.find(s).addClass(this.highlight_class);
+        }
+    },
+    ".operations .single-operation span mouseleave": function(el, ev) {
+        var name = el.data('name');
+        if(name) {
+            var s = '.operations .single-operation span[data-name="'+name+'"]';
+            this.element.find(s).removeClass(this.highlight_class);
+        }
+    }
+})
+JITViewer = can.Control({
+    init: function(element, options){
+        this.state_asm = false;
+        this.state_bytecodepos = false;
+        this.state_op = true;
+        this.current_line = null;
+        this.filter_active = false;
+
+        this.main_display = new MainDisplay(this.element.find("#main"), {})
+        $("#inp-bar").focus();
+    },
+    'route': function() {
+        //console.log('route index');
+    },
+    'items/:name route': function(data) {
+        this.show_loop(data.name);
+    },
+    "#inp-bar focusin": function(el, ev){
+        this.filter_active = true;
+    },
+    "#inp-bar focusout": function(el, ev){
+        this.filter_active = false;
+    },
+    "#inp-bar keyup": function(el, ev){
+        var v = el.val();
+        $(".loopitem").each(function (i, l) {
+            var name = $(l).attr('name');
+            if(name.search(v) != -1){
+                $(l).show();
+            } else {
+                $(l).hide();
+            }
+        });
+    },
+    toggle_asm: function(force_state) {
+        if(typeof force_state !== 'undefined'){
+            this.state_asm = force_state ? false : true;
+        }
+
+        var el = $("#asmtoggler");
+        var asm = $(".asm");
+        if(this.state_asm) {
+            this.state_asm = false;
+            el.html("Show assembler [a]");
+            asm.hide();
+        } else {
+            this.state_asm = true;
+            el.html("Hide assembler [a]");
+            asm.show();
+        }
+    },
+    "#asmtoggler click": function(el, ev) {
+        ev.preventDefault();
+        this.toggle_asm();
+        return;
+    },
+    "#bytecodepos_toggler click": function(el, ev) {
+        ev.preventDefault();
+        this.toggle_bytecodepos();
+        return;
+    },
+    toggle_bytecodepos: function(force_state){
+        if(typeof force_state !== 'undefined'){
+            this.state_bytecodepos = force_state ? false : true;
+        }
+
+        var el = $("#bytecodepos_toggler");
+        var bytecodepos = $(".bytecodepos");
+        if(this.state_bytecodepos) {
+            this.state_bytecodepos = false;
+            el.html("Show bytecode position [b]");
+            bytecodepos.hide();
+        } else {
+            this.state_bytecodepos = true;
+            el.html("Hide bytecode position [b]");
+            bytecodepos.show();
+        }
+    },
+    " keydown": function(el, ev){
+        if(ev.keyCode == 13) {
+            ev.preventDefault();
+        }
+
+        if(this.filter_active == false){
+            if(ev.keyCode == 65){
+                this.toggle_asm();
+            } else if(ev.keyCode == 66){
+                this.toggle_bytecodepos();
+            } else if(ev.keyCode == 191){
+                ev.preventDefault();
+                $('#inp-bar').focus();
+            }
+        }
+    },
+    "#loops .loopitem a click": function(el, ev){
+        ev.preventDefault();
+        var name = el.data('name');
+        can.route.attr('name', name);
+        //this.show_loop(name);
+    },
+    "#callstack a click": function(el, ev){
+        ev.preventDefault();
+        var name = el.data('name');
+        can.route.attr('name', name);
+        //this.show_loop(name);
+    },
+    check_selfreferencing: function(){
+        var self = this;
+        var current_name = can.route.attr('name');
+        var names = [];
+
+        $.each(this.element.find('[data-name]'), function(idx, item){
+            var name = String($(item).data('name'));
+            if(name.indexOf('TargetToken') == -1) {
+                token_name = 'TargetToken('+name+')';
+            } else {
+                token_name = name;
+                name = token_name.replace('TargetToken(', '').replace(')', '');
+            }
+
+            if(current_name == name || current_name == token_name){
+                names.push(name);
+                names.push(token_name);
+            }
+        });
+        unique_names = [];
+        for(n in names){
+            var name = names[n];
+            if(unique_names.indexOf(name) == -1){
+                unique_names.push(name);
+            }
+        }
+        var elements = null;
+        if(current_name == name){
+            elements = self.element.find('.operations a[data-name="'+name+'"]');
+            elements.css('color', 'black').append(' [self]');
+        }
+        if(current_name == token_name){
+            elements = self.element.find('.operations a[data-name="'+token_name+'"]');
+            elements.css('color', 'black').append(' [self]');
+        }
+    },
+    show_loop: function(name, path){
+        /*
+        if(this.current_line) {
+            $("#loop-" + this.current_line).removeClass("selected");
+        }
+        $("#loop-" + name).addClass("selected");
+        $("#title-text").html($("#loop-" + name).attr('name'));
+        $("#title").show();
+        glob_bridge_state.name = name;
+        if (path) {
+            glob_bridge_state.path = path;
+        } else {
+            delete glob_bridge_state.path;
+        }
+        */
+        var self = this;
+        var data = {
+            'asm': this.state_asm,
+            'bytecodepos': this.state_bytecodepos,
+            'op': this.state_op,
+            'name': name,
+            'path': path
+        }
+
+        $.getJSON('/loop', data, function(arg) {
+            $('#main').html(arg.html).ready(function() {
+                var scrollto;
+                if (arg.scrollto == 0) {
+                    scrollto = 0;
+                } else {
+                    scrollto = arg.scrollto - 1;
+                }
+                $.scrollTo($('#line-' + scrollto), 200, {axis:'y'});
+            });
+            $('#callstack').html('')
+            for (var index in arg.callstack) {
+                var elem = arg.callstack[index];
+                $('#callstack').append('<div><a href="#" data-name="' + name + '" data-path="' + elem[0] + '">' + elem[1] + "</a></div>");
+            }
+
+            self.toggle_asm(false);
+            self.toggle_bytecodepos(false);
+
+            self.check_selfreferencing();
+        });
+    }
+})

_jitviewer/static/canjs/1.1.4/amd/can.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['can/util/library', 'can/control/route', 'can/model', 'can/view/ejs', 'can/route'], function (can) {
+	return can;
+});

_jitviewer/static/canjs/1.1.4/amd/can/construct.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['can/util/string'], 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,
+				_shortName: _shortName,
+
+				fullName: fullName,
+				_fullName: _fullName
+			});
+
+			// 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;
+});

_jitviewer/static/canjs/1.1.4/amd/can/construct/proxy.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['can/util/library', 'can/construct'], function (can, Construct) {
+	var isFunction = can.isFunction,
+		isArray = can.isArray,
+		makeArray = can.makeArray,
+
+		proxy = function (funcs) {
+
+			//args that should be curried
+			var args = makeArray(arguments),
+				self;
+
+			// get the functions to callback
+			funcs = args.shift();
+
+			// if there is only one function, make funcs into an array
+			if (!isArray(funcs)) {
+				funcs = [funcs];
+			}
+
+			// keep a reference to us in self
+			self = this;
+
+
+			return function class_cb() {
+				// add the arguments after the curried args
+				var cur = args.concat(makeArray(arguments)),
+					isString, length = funcs.length,
+					f = 0,
+					func;
+
+				// go through each function to call back
+				for (; f < length; f++) {
+					func = funcs[f];
+					if (!func) {
+						continue;
+					}
+
+					// set called with the name of the function on self (this is how this.view works)
+					isString = typeof func == "string";
+
+					// call the function
+					cur = (isString ? self[func] : func).apply(self, cur || []);
+
+					// pass the result to the next function (if there is a next function)
+					if (f < length - 1) {
+						cur = !isArray(cur) || cur._use_call ? [cur] : cur
+					}
+				}
+				return cur;
+			}
+		}
+		can.Construct.proxy = can.Construct.prototype.proxy = proxy;
+	// this corrects the case where can/control loads after can/construct/proxy, so static props don't have proxy
+	var correctedClasses = [can.Observe, can.Control, can.Model],
+		i = 0;
+	for (; i < correctedClasses.length; i++) {
+		if (correctedClasses[i]) {
+			correctedClasses[i].proxy = proxy;
+		}
+	}
+	return can;
+});

_jitviewer/static/canjs/1.1.4/amd/can/construct/super.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['can/util/library', 'can/construct'], function (can, Construct) {
+
+	// tests if we can get super in .toString()
+	var isFunction = can.isFunction,
+
+		fnTest = /xyz/.test(function () {
+			xyz;
+		}) ? /\b_super\b/ : /.*/;
+
+	// overwrites a single property so it can still call super
+	can.Construct._overwrite = function (addTo, base, name, val) {
+		// Check if we're overwriting an existing function
+		addTo[name] = isFunction(val) && isFunction(base[name]) && fnTest.test(val) ? (function (name, fn) {
+			return function () {
+				var tmp = this._super,
+					ret;
+
+				// Add a new ._super() method that is the same method
+				// but on the super-class
+				this._super = base[name];
+
+				// The method only need to be bound temporarily, so we
+				// remove it when we're done executing
+				ret = fn.apply(this, arguments);
+				this._super = tmp;
+				return ret;
+			};
+		})(name, val) : val;
+	}
+	// overwrites an object with methods, sets up _super
+	//   newProps - new properties
+	//   oldProps - where the old properties might be
+	//   addTo - what we are adding to
+	can.Construct._inherit = function (newProps, oldProps, addTo) {
+		addTo = addTo || newProps
+		for (var name in newProps) {
+			can.Construct._overwrite(addTo, oldProps, name, newProps[name]);
+		}
+	}
+
+	return can;
+});

_jitviewer/static/canjs/1.1.4/amd/can/control.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['can/util/library', 'can/construct'], 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();
+
+			// Get's 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 () {
+			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;
+});

_jitviewer/static/canjs/1.1.4/amd/can/control/plugin.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['jquery', 'can/util/library', 'can/control'], function ($, can) {
+	//used to determine if a control instance is one of controllers
+	//controllers can be strings or classes
+	var i, isAControllerOf = function (instance, controllers) {
+		for (i = 0; i < controllers.length; i++) {
+			if (typeof controllers[i] == 'string' ? instance.constructor._shortName == controllers[i] : instance instanceof controllers[i]) {
+				return true;
+			}
+		}
+		return false;
+	},
+		makeArray = can.makeArray,
+		old = can.Control.setup;
+
+	can.Control.setup = function () {
+		// if you didn't provide a name, or are control, don't do anything
+		if (this !== can.Control) {
+
+
+			var pluginName = this.pluginName || this._fullName;
+
+			// create jQuery plugin
+			if (pluginName !== 'can_control') {
+				this.plugin(pluginName);
+			}
+
+			old.apply(this, arguments);
+		}
+	};
+
+	$.fn.extend({
+
+
+		controls: function () {
+			var controllerNames = makeArray(arguments),
+				instances = [],
+				controls, c, cname;
+			//check if arguments
+			this.each(function () {
+
+				controls = can.$(this).data("controls");
+				if (!controls) {
+					return;
+				}
+				for (var i = 0; i < controls.length; i++) {
+					c = controls[i];
+					if (!controllerNames.length || isAControllerOf(c, controllerNames)) {
+						instances.push(c);
+					}
+				}
+			});
+			return instances;
+		},
+
+
+		control: function (control) {
+			return this.controls.apply(this, arguments)[0];
+		}
+	});
+
+	can.Control.plugin = function (pluginname) {
+		var control = this;
+
+		if (!$.fn[pluginname]) {
+			$.fn[pluginname] = function (options) {
+
+				var args = makeArray(arguments),
+					//if the arg is a method on this control
+					isMethod = typeof options == "string" && $.isFunction(control.prototype[options]),
+					meth = args[0],
+					returns;
+				this.each(function () {
+					//check if created
+					var plugin = can.$(this).control(control);
+
+					if (plugin) {
+						if (isMethod) {
+							// call a method on the control with the remaining args
+							returns = plugin[meth].apply(plugin, args.slice(1));
+						}
+						else {
+							// call the plugin's update method
+							plugin.update.apply(plugin, args);
+						}
+					}
+					else {
+						//create a new control instance
+						control.newInstance.apply(control, [this].concat(args));
+					}
+				});
+				return returns !== undefined ? returns : this;
+			};
+		}
+	}
+
+	can.Control.prototype.update = function (options) {
+		can.extend(this.options, options);
+		this.on();
+	};
+
+	return can;
+});

_jitviewer/static/canjs/1.1.4/amd/can/control/route.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['can/util/library', 'can/route', 'can/control'], 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;
+});

_jitviewer/static/canjs/1.1.4/amd/can/model.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['can/util/library', 'can/observe'], 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 deferred, args = [self.serialize()],
+				// 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",
+			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
+							this._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.
+				this._reqs = 0;
+				this._url = this._shortName + "/{" + this.id + "}"
+			},
+			_ajax: ajaxMaker,
+			_clean: function () {
+				this._reqs--;
+				if (!this._reqs) {
+					for (var id in this.store) {
+						if (!this.store[id]._bindings) {
+							delete this.store[id];
+						}
+					}
+				}
+				return arguments[0];
+			},
+
+			models: function (instancesRawData, oldList) {
+
+				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);
+						}
+					})
+				}
+				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 (this._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;
+					return can.Deferred().done(function (data) {
+						self.destroyed(data)
+					}).resolve(self);
+				}
+				return makeRequest(this, 'destroy', success, error, 'destroyed');
+			},
+
+			bind: function (eventName) {
+				if (!ignoreHookup.test(eventName)) {
+					if (!this._bindings) {
+						this.constructor.store[this.__get(this.constructor.id)] = this;
+						this._bindings = 0;
+					}
+					this._bindings++;
+				}
+
+				return can.Observe.prototype.bind.apply(this, arguments);
+			},
+
+			unbind: function (eventName) {
+				if (!ignoreHookup.test(eventName)) {
+					this._bindings--;
+					if (!this._bindings) {
+						delete this.constructor.store[getId(this)];
+					}
+				}
+				return can.Observe.prototype.unbind.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"
+	}, function (method, name) {
+		can.Model[name] = function (oldFind) {
+			return function (params, success, error) {
+				var def = pipe(oldFind.call(this, params), this, method);
+				def.then(success, error);
+				// 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);
+
+			// Call event on the instance
+			can.trigger(this, funcName);
+			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 () {
+			can.Observe.List.prototype.setup.apply(this, arguments);
+			// Send destroy events.
+			var self = this;
+			this.bind('change', function (ev, how) {
+				if (/\w+\.destroyed/.test(how)) {
+					var index = self.indexOf(ev.target);
+					if (index != -1) {
+						self.splice(index, 1);
+					}
+				}
+			})
+		}
+	})
+
+	return can.Model;
+});

_jitviewer/static/canjs/1.1.4/amd/can/observe.js

+/*!
+* CanJS - 1.1.4 (2013-02-05)
+* http://canjs.us/
+* Copyright (c) 2013 Bitovi
+* Licensed MIT
+*/
+define(['can/util/library', 'can/construct'], function (can) {
+	// ## 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.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 `val` and "bubbles" the event up.  
+		// `val` - 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 (val, prop, parent, Ob, List) {
+			Ob = Ob || Observe;
+			List = List || Observe.List;
+
+			// If it's an `array` make a list, otherwise a val.
+			if (val instanceof Observe) {
+				// We have an `observe` already...
+				// Make sure it is not listening to this already
+				unhookup([val], parent._cid);
+			} else if (can.isArray(val)) {
+				val = new List(val);
+			} else {
+				val = new Ob(val);
+			}
+
+			// Listen to all changes and `batchTrigger` upwards.
+			val.bind("change" + parent._cid, function () {
+				// `batchTrigger` the type on this...
+				var args = can.makeArray(arguments),
+					ev = args.shift();
+				args[0] = (prop === "*" ? [parent.indexOf(val), 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);
+			});
+
+			return val;
+		},
+
+		// 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;
+		},
+		$method = function (name) {
+			return function () {
+				return can[name].apply(this, arguments);
+			};
+		},
+		bind = $method('addEvent'),
+		unbind = $method('removeEvent'),
+		attrParts = function (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 = [];
+
+
+
+
+	var Observe = can.Observe = can.Construct({
+
+		// keep so it can be overwritten
+		bind: bind,
+		unbind: unbind,
+		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 {
+					batchEvents.push([
+					item,
+					{
+						type: event,
+						batchNum: batchNum
+					},
+					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;
+		},
+		_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))
+		},
+
+		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 (val === undefined) { // 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) {
+			// Convert the `attr` into parts (if nested).
+			var parts = attrParts(attr),
+				// The actual property to remove.
+				prop = parts.shift(),
+				// The current value.
+				current = this._data[prop];
+
+			// If we have more parts, call `removeAttr` on that part.
+			if (parts.length) {
+				return current.removeAttr(parts)
+			} 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) {
+			// 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) {
+			// Convert `attr` to attr parts (if it isn't already).
+			var parts = attrParts(attr),
+				// 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: bind,
+
+		unbind: unbind,
+
+		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)
+			}
+			Observe.stopBatch()
+			return this;
+		},
+
+
+		compute: function (prop) {
+			var self = this,
+				computer = function (val) {
+					return self.attr(prop, val);
+				};
+
+			return can.compute ? can.compute(computer) : computer;
+		}
+	});
+	// Helpers for `observable` lists.
+	var splice = [].splice,
+		list = Observe(
+
+		{
+			setup: function (instances, options) {
+				this.length = 0;
+				can.cid(this, ".observe")
+				this._init = 1;
+				this.push.apply(this, can.makeArray(instances || []));
+				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)
+				}
+			},
+			// 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)
+					}
+				}
+				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++) {