--- /dev/null
+var Backbone, op, AppView, _ref, _;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+/**
+ * @class Application view, automatically attaching to an existing element
+ * found at `appSelector`.
+ * @extends Backbone.View
+ */
+AppView = exports.AppView = Backbone.View.extend({
+ appSelector: '#content .inner'
+ /**
+ * @constructor
+ */,
+ constructor: (function(){
+ function AppView(options){
+ var that, _this = this;
+ options == null && (options = {});
+ if (typeof options === 'function') {
+ this.initialize = options;
+ options = {};
+ } else {
+ if (that = options.initialize) {
+ this.initialize = that;
+ }
+ }
+ if (that = options.appSelector) {
+ this.appSelector = that;
+ }
+ options.el || (options.el = jQuery(this.appSelector)[0]);
+ Backbone.View.call(this, options);
+ jQuery(function(){
+ return _this.render();
+ });
+ return this;
+ }
+ return AppView;
+ }())
+ /**
+ * Override to set up your app. This method may be passed
+ * as an option to the constructor.
+ */,
+ initialize: function(){}
+ /**
+ * Append subviews.
+ */,
+ render: function(){
+ var _ref;
+ if (this.view && !((_ref = this.view.$el.parent()) != null && _ref.length)) {
+ return this.$el.append(this.view.el);
+ }
+ },
+ getClassName: function(){
+ return (this.constructor.name || this.constructor.displayName) + "";
+ },
+ toString: function(){
+ return this.getClassName() + "()";
+ }
+});
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/app.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var Backbone, op, AppView, _ref, _;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+/**
+ * @class Application view, automatically attaching to an existing element
+ * found at `appSelector`.
+ * @extends Backbone.View
+ */
+AppView = exports.AppView = Backbone.View.extend({
+ appSelector: '#content .inner'
+ /**
+ * @constructor
+ */,
+ constructor: (function(){
+ function AppView(options){
+ var that, _this = this;
+ options == null && (options = {});
+ if (typeof options === 'function') {
+ this.initialize = options;
+ options = {};
+ } else {
+ if (that = options.initialize) {
+ this.initialize = that;
+ }
+ }
+ if (that = options.appSelector) {
+ this.appSelector = that;
+ }
+ options.el || (options.el = jQuery(this.appSelector)[0]);
+ Backbone.View.call(this, options);
+ jQuery(function(){
+ return _this.render();
+ });
+ return this;
+ }
+ return AppView;
+ }())
+ /**
+ * Override to set up your app. This method may be passed
+ * as an option to the constructor.
+ */,
+ initialize: function(){}
+ /**
+ * Append subviews.
+ */,
+ render: function(){
+ var _ref;
+ if (this.view && !((_ref = this.view.$el.parent()) != null && _ref.length)) {
+ return this.$el.append(this.view.el);
+ }
+ },
+ getClassName: function(){
+ return (this.constructor.name || this.constructor.displayName) + "";
+ },
+ toString: function(){
+ return this.getClassName() + "()";
+ }
+});
+
+});
--- /dev/null
+var Backbone, op, BaseBackboneMixin, Mixin, mixinBase, _ref, _, __slice = [].slice;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+BaseBackboneMixin = exports.BaseBackboneMixin = {
+ initialize: function(){
+ return this.__apply_bind__();
+ }
+ /**
+ * A list of method-names to bind on `initialize`; set this on a subclass to override.
+ * @type Array<String>
+ */,
+ __bind__: []
+ /**
+ * Applies the contents of `__bind__`.
+ */,
+ __apply_bind__: function(){
+ var names;
+ names = _(this.pluckSuperAndSelf('__bind__')).chain().flatten().compact().unique().value();
+ if (names.length) {
+ return _.bindAll.apply(_, [this].concat(__slice.call(names)));
+ }
+ }
+ /**
+ * Whether we're ready.
+ * @type Boolean
+ */,
+ ready: false
+ /**
+ * Triggers the 'ready' event if it has not yet been triggered.
+ * Subsequent listeners added on this event will be auto-triggered.
+ * @returns {this}
+ */,
+ triggerReady: function(lock, event){
+ lock == null && (lock = 'ready');
+ event == null && (event = 'ready');
+ if (this[lock]) {
+ return this;
+ }
+ this[lock] = true;
+ this.trigger(event, this);
+ return this;
+ }
+ /**
+ * Resets the 'ready' event to its non-triggered state, firing a
+ * 'ready-reset' event.
+ * @returns {this}
+ */,
+ resetReady: function(lock, event){
+ lock == null && (lock = 'ready');
+ event == null && (event = 'ready');
+ if (!this[lock]) {
+ return this;
+ }
+ this[lock] = false;
+ this.trigger(event + "-reset", this);
+ return this;
+ }
+ /**
+ * Wrap {@link Backbone.Event#on} registration to handle registrations
+ * on 'ready' after we've broadcast the event. Handler will always still
+ * be registered, however, in case the emitter is reset.
+ *
+ * @param {String} events Space-separated events for which to register.
+ * @param {Function} callback
+ * @param {Object} [context]
+ * @returns {this}
+ */,
+ on: function(events, callback, context){
+ context == null && (context = this);
+ if (!callback) {
+ return this;
+ }
+ Backbone.Events.on.apply(this, arguments);
+ if (this.ready && _.contains(events.split(/\s+/), 'ready')) {
+ callback.call(context, this);
+ }
+ return this;
+ },
+ makeHandlersForCallback: function(cb){
+ var _this = this;
+ return {
+ success: function(){
+ return cb.call(_this, [null].concat(arguments));
+ },
+ error: function(it){
+ return cb.call(_this, it);
+ }
+ };
+ }
+ /**
+ * Count of outstanding tasks.
+ * @type Number
+ */,
+ waitingOn: 0
+ /**
+ * Increment the waiting task counter.
+ * @returns {this}
+ */,
+ wait: function(){
+ var count;
+ count = this.waitingOn;
+ this.waitingOn += 1;
+ if (count === 0 && this.waitingOn > 0) {
+ this.trigger('start-waiting', this);
+ }
+ return this;
+ }
+ /**
+ * Decrement the waiting task counter.
+ * @returns {this}
+ */,
+ unwait: function(){
+ var count;
+ count = this.waitingOn;
+ this.waitingOn -= 1;
+ if (this.waitingOn === 0 && count > 0) {
+ this.trigger('stop-waiting', this);
+ }
+ return this;
+ }
+ /**
+ * @param {Function} fn Function to wrap.
+ * @returns {Function} A function wrapping the passed function with a call
+ * to `unwait()`, then delegating with current context and arguments.
+ */,
+ unwaitAnd: function(fn){
+ var self;
+ self = this;
+ return function(){
+ self.unwait();
+ return fn.apply(this, arguments);
+ };
+ },
+ getClassName: function(){
+ return (this.constructor.name || this.constructor.displayName) + "";
+ },
+ toString: function(){
+ return this.getClassName() + "()";
+ }
+};
+/**
+ * @class Base mixin class. Extend this to create a new mixin, attaching the
+ * donor methods as you would instance methods.
+ *
+ * To mingle your mixin with another class or object:
+ *
+ * class MyMixin extends Mixin
+ * foo: -> "foo!"
+ *
+ * # Mix into an object...
+ * o = MyMixin.mix { bar:1 }
+ *
+ * # Mix into a Coco class...
+ * class Bar
+ * MyMixin.mix this
+ * bar : 1
+ *
+ */
+exports.Mixin = Mixin = (function(){
+ /**
+ * Mixes this mixin into the target. If `target` is not a class, a new
+ * object will be returned which inherits from the mixin.
+ */
+ Mixin.displayName = 'Mixin';
+ var prototype = Mixin.prototype, constructor = Mixin;
+ Mixin.mix = function(target){
+ var MixinClass;
+ if (!target) {
+ return that;
+ }
+ MixinClass = Mixin;
+ if (this instanceof Mixin) {
+ MixinClass = this.constructor;
+ }
+ if (this instanceof Function) {
+ MixinClass = this;
+ }
+ if (typeof target === 'function') {
+ __import(target.prototype, MixinClass.prototype);
+ } else {
+ target = __import(_.clone(MixinClass.prototype), target);
+ }
+ (target.__mixins__ || (target.__mixins__ = [])).push(MixinClass);
+ return target;
+ };
+ /**
+ * Coco metaprogramming hook to propagate class properties and methods.
+ */
+ Mixin.extended = function(SubClass){
+ var SuperClass, k, v, _own = {}.hasOwnProperty;
+ SuperClass = this;
+ for (k in SuperClass) if (_own.call(SuperClass, k)) {
+ v = SuperClass[k];
+ if (!SubClass[k]) {
+ SubClass[k] = v;
+ }
+ }
+ return SubClass;
+ };
+ function Mixin(){}
+ return Mixin;
+}());
+/**
+ * Mixes BaseBackboneMixin into another object or prototype.
+ * @returns {Object} The merged prototype object.
+ */
+mixinBase = exports.mixinBase = function(){
+ var bodies;
+ bodies = __slice.call(arguments);
+ return _.extend.apply(_, [_.clone(BaseBackboneMixin)].concat(__slice.call(bodies)));
+};
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/base-mixin.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var Backbone, op, BaseBackboneMixin, Mixin, mixinBase, _ref, _, __slice = [].slice;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+BaseBackboneMixin = exports.BaseBackboneMixin = {
+ initialize: function(){
+ return this.__apply_bind__();
+ }
+ /**
+ * A list of method-names to bind on `initialize`; set this on a subclass to override.
+ * @type Array<String>
+ */,
+ __bind__: []
+ /**
+ * Applies the contents of `__bind__`.
+ */,
+ __apply_bind__: function(){
+ var names;
+ names = _(this.pluckSuperAndSelf('__bind__')).chain().flatten().compact().unique().value();
+ if (names.length) {
+ return _.bindAll.apply(_, [this].concat(__slice.call(names)));
+ }
+ }
+ /**
+ * Whether we're ready.
+ * @type Boolean
+ */,
+ ready: false
+ /**
+ * Triggers the 'ready' event if it has not yet been triggered.
+ * Subsequent listeners added on this event will be auto-triggered.
+ * @returns {this}
+ */,
+ triggerReady: function(lock, event){
+ lock == null && (lock = 'ready');
+ event == null && (event = 'ready');
+ if (this[lock]) {
+ return this;
+ }
+ this[lock] = true;
+ this.trigger(event, this);
+ return this;
+ }
+ /**
+ * Resets the 'ready' event to its non-triggered state, firing a
+ * 'ready-reset' event.
+ * @returns {this}
+ */,
+ resetReady: function(lock, event){
+ lock == null && (lock = 'ready');
+ event == null && (event = 'ready');
+ if (!this[lock]) {
+ return this;
+ }
+ this[lock] = false;
+ this.trigger(event + "-reset", this);
+ return this;
+ }
+ /**
+ * Wrap {@link Backbone.Event#on} registration to handle registrations
+ * on 'ready' after we've broadcast the event. Handler will always still
+ * be registered, however, in case the emitter is reset.
+ *
+ * @param {String} events Space-separated events for which to register.
+ * @param {Function} callback
+ * @param {Object} [context]
+ * @returns {this}
+ */,
+ on: function(events, callback, context){
+ context == null && (context = this);
+ if (!callback) {
+ return this;
+ }
+ Backbone.Events.on.apply(this, arguments);
+ if (this.ready && _.contains(events.split(/\s+/), 'ready')) {
+ callback.call(context, this);
+ }
+ return this;
+ },
+ makeHandlersForCallback: function(cb){
+ var _this = this;
+ return {
+ success: function(){
+ return cb.call(_this, [null].concat(arguments));
+ },
+ error: function(it){
+ return cb.call(_this, it);
+ }
+ };
+ }
+ /**
+ * Count of outstanding tasks.
+ * @type Number
+ */,
+ waitingOn: 0
+ /**
+ * Increment the waiting task counter.
+ * @returns {this}
+ */,
+ wait: function(){
+ var count;
+ count = this.waitingOn;
+ this.waitingOn += 1;
+ if (count === 0 && this.waitingOn > 0) {
+ this.trigger('start-waiting', this);
+ }
+ return this;
+ }
+ /**
+ * Decrement the waiting task counter.
+ * @returns {this}
+ */,
+ unwait: function(){
+ var count;
+ count = this.waitingOn;
+ this.waitingOn -= 1;
+ if (this.waitingOn === 0 && count > 0) {
+ this.trigger('stop-waiting', this);
+ }
+ return this;
+ }
+ /**
+ * @param {Function} fn Function to wrap.
+ * @returns {Function} A function wrapping the passed function with a call
+ * to `unwait()`, then delegating with current context and arguments.
+ */,
+ unwaitAnd: function(fn){
+ var self;
+ self = this;
+ return function(){
+ self.unwait();
+ return fn.apply(this, arguments);
+ };
+ },
+ getClassName: function(){
+ return (this.constructor.name || this.constructor.displayName) + "";
+ },
+ toString: function(){
+ return this.getClassName() + "()";
+ }
+};
+/**
+ * @class Base mixin class. Extend this to create a new mixin, attaching the
+ * donor methods as you would instance methods.
+ *
+ * To mingle your mixin with another class or object:
+ *
+ * class MyMixin extends Mixin
+ * foo: -> "foo!"
+ *
+ * # Mix into an object...
+ * o = MyMixin.mix { bar:1 }
+ *
+ * # Mix into a Coco class...
+ * class Bar
+ * MyMixin.mix this
+ * bar : 1
+ *
+ */
+exports.Mixin = Mixin = (function(){
+ /**
+ * Mixes this mixin into the target. If `target` is not a class, a new
+ * object will be returned which inherits from the mixin.
+ */
+ Mixin.displayName = 'Mixin';
+ var prototype = Mixin.prototype, constructor = Mixin;
+ Mixin.mix = function(target){
+ var MixinClass;
+ if (!target) {
+ return that;
+ }
+ MixinClass = Mixin;
+ if (this instanceof Mixin) {
+ MixinClass = this.constructor;
+ }
+ if (this instanceof Function) {
+ MixinClass = this;
+ }
+ if (typeof target === 'function') {
+ __import(target.prototype, MixinClass.prototype);
+ } else {
+ target = __import(_.clone(MixinClass.prototype), target);
+ }
+ (target.__mixins__ || (target.__mixins__ = [])).push(MixinClass);
+ return target;
+ };
+ /**
+ * Coco metaprogramming hook to propagate class properties and methods.
+ */
+ Mixin.extended = function(SubClass){
+ var SuperClass, k, v, _own = {}.hasOwnProperty;
+ SuperClass = this;
+ for (k in SuperClass) if (_own.call(SuperClass, k)) {
+ v = SuperClass[k];
+ if (!SubClass[k]) {
+ SubClass[k] = v;
+ }
+ }
+ return SubClass;
+ };
+ function Mixin(){}
+ return Mixin;
+}());
+/**
+ * Mixes BaseBackboneMixin into another object or prototype.
+ * @returns {Object} The merged prototype object.
+ */
+mixinBase = exports.mixinBase = function(){
+ var bodies;
+ bodies = __slice.call(arguments);
+ return _.extend.apply(_, [_.clone(BaseBackboneMixin)].concat(__slice.call(bodies)));
+};
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
--- /dev/null
+var Backbone, op, BaseBackboneMixin, mixinBase, BaseModel, BaseList, _ref, _, __slice = [].slice;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+_ref = require('kraken/base/base-mixin'), BaseBackboneMixin = _ref.BaseBackboneMixin, mixinBase = _ref.mixinBase;
+/**
+ * @class Base model, extending Backbone.Model, used by scaffold and others.
+ * @extends Backbone.Model
+ */
+BaseModel = exports.BaseModel = Backbone.Model.extend(mixinBase({
+ constructor: (function(){
+ function BaseModel(){
+ this.__class__ = this.constructor;
+ this.__superclass__ = this.constructor.__super__.constructor;
+ this.waitingOn = 0;
+ return Backbone.Model.apply(this, arguments);
+ }
+ return BaseModel;
+ }()),
+ url: function(){
+ return this.urlRoot + "/" + this.get('id') + ".json";
+ },
+ has: function(key){
+ return this.get(key) != null;
+ },
+ get: function(key){
+ return _.getNested(this.attributes, key);
+ }
+ /**
+ * Override to customize what data or assets the model requires,
+ * and how they should be loaded.
+ *
+ * By default, `load()` simply calls `loadModel()` via `loader()`.
+ *
+ * @see BaseModel#loader
+ * @see BaseModel#loadModel
+ * @returns {this}
+ */,
+ load: function(){
+ console.log(this + ".load()");
+ this.loader({
+ start: this.loadModel,
+ completeEvent: 'fetch-success'
+ });
+ return this;
+ }
+ /**
+ * Wraps the loading workflow boilerplate:
+ * - Squelches multiple loads from running at once
+ * - Squelches loads post-ready, unless forced
+ * - Triggers a start event
+ * - Triggers "ready" when complete
+ * - Wraps workflow with wait/unwait
+ * - Cleans up "loading" state
+ *
+ * @protected
+ * @param {Object} [opts={}] Options:
+ * @param {Function} opts.start Function that starts the loading process. Always called with `this` as the context.
+ * @param {String} [opts.startEvent='load'] Event to trigger before beginning the load.
+ * @param {String} [opts.completeEvent='load-success'] Event which signals loading has completed successfully.
+ * @param {String} [opts.errorEvent='load-error'] Event which signals loading has completed but failed.
+ * @param {Boolean} [opts.force=false] If true, reset ready state if we're ready before proceeding.
+ * @param {Boolean} [opts.readyIfError=false] If true, move fire the ready event when loading completes, even if it failed.
+ * @returns {this}
+ */,
+ loader: function(opts){
+ var _this = this;
+ opts == null && (opts = {});
+ opts = (__import({
+ force: false,
+ readyIfError: false,
+ startEvent: 'load',
+ completeEvent: 'load-success',
+ errorEvent: 'load-error'
+ }, opts));
+ if (opts.force) {
+ this.resetReady();
+ }
+ if (!opts.start) {
+ throw new Error('You must specify a `start` function to start loading!');
+ }
+ if (this.loading || this.ready) {
+ return this;
+ }
+ this.wait();
+ this.loading = true;
+ this.trigger(opts.startEvent, this);
+ this.once(opts.completeEvent, function(){
+ _this.loading = false;
+ _this.unwait();
+ if (opts.completeEvent !== 'load-success') {
+ _this.trigger('load-success', _this);
+ }
+ return _this.triggerReady();
+ });
+ this.once(opts.errorEvent, function(){
+ _this.loading = false;
+ _this.unwait();
+ if (opts.errorEvent !== 'load-error') {
+ _this.trigger('load-error', _this);
+ }
+ if (opts.readyIfError) {
+ return _this.triggerReady();
+ }
+ });
+ opts.start.call(this);
+ return this;
+ }
+ /**
+ * Runs `.fetch()`, triggering a `fetch` event at start, and
+ * `fetch-success` / `fetch-error` on completion.
+ *
+ * @protected
+ * @returns {this}
+ */,
+ loadModel: function(){
+ var _this = this;
+ this.wait();
+ this.trigger('fetch', this);
+ this.fetch({
+ success: function(){
+ _this.unwait();
+ return _this.trigger('fetch-success', _this);
+ },
+ error: function(){
+ _this.unwait();
+ return _this.trigger.apply(_this, ['fetch-error', _this].concat(__slice.call(arguments)));
+ }
+ });
+ return this;
+ },
+ serialize: function(v){
+ if (_.isBoolean(v)) {
+ v = Number(v);
+ } else if (_.isObject(v)) {
+ v = JSON.stringify(v);
+ }
+ return String(v);
+ }
+ /**
+ * Like `.toJSON()` in that it should return a plain object with no functions,
+ * but for the purpose of `.toKV()`, allowing you to customize the values
+ * included and keys used.
+ *
+ * @param {Object} [opts={}] Options:
+ * @param {Boolean} [opts.keepFunctions=false] If false, functions will be omitted from the result.
+ * @returns {Object}
+ */,
+ toKVPairs: function(opts){
+ var kvo, k, v;
+ opts == null && (opts = {});
+ opts = (__import({
+ keepFunctions: false
+ }, opts));
+ kvo = _.collapseObject(this.toJSON());
+ for (k in kvo) {
+ v = kvo[k];
+ if (opts.keepFunctions || typeof v !== 'function') {
+ kvo[k] = this.serialize(v);
+ }
+ }
+ return kvo;
+ }
+ /**
+ * Serialize the model into a `www-form-encoded` string suitable for use as
+ * a query string or a POST body.
+ * @returns {String}
+ */,
+ toKV: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return _.toKV(this.toKVPairs(), item_delim, kv_delim);
+ }
+ /**
+ * @returns {String} URL identifying this model.
+ */,
+ toURL: function(){
+ return "?" + this.toKV.apply(this, arguments);
+ },
+ toString: function(){
+ return this.getClassName() + "(cid=" + this.cid + ", id=" + this.id + ")";
+ }
+}));
+__import(BaseModel, {
+ /**
+ * Factory method which constructs an instance of this model from a string of KV-pairs.
+ * This is a class method inherited by models which extend {BaseModel}.
+ * @static
+ * @param {String|Object} o Serialized KV-pairs (or a plain object).
+ * @returns {BaseModel} An instance of this model.
+ */
+ fromKV: function(o, item_delim, kv_delim){
+ var Cls;
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ if (typeof o === 'string') {
+ o = _.fromKV(o, item_delim, kv_delim);
+ }
+ Cls = typeof this === 'function'
+ ? this
+ : this.constructor;
+ return new Cls(_.uncollapseObject(o));
+ }
+});
+/**
+ * @class Base collection, extending Backbone.Collection, used by scaffold and others.
+ * @extends Backbone.Collection
+ */
+BaseList = exports.BaseList = Backbone.Collection.extend(mixinBase({
+ constructor: (function(){
+ function BaseList(){
+ this.__class__ = this.constructor;
+ this.__superclass__ = this.constructor.__super__.constructor;
+ this.waitingOn = 0;
+ return Backbone.Collection.apply(this, arguments);
+ }
+ return BaseList;
+ }()),
+ getIds: function(){
+ return this.models.map(function(it){
+ return it.id || it.get('id') || it.cid;
+ });
+ },
+ toKVPairs: function(){
+ return _.collapseObject(this.toJSON());
+ },
+ toKV: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return _.toKV(this.toKVPairs(), item_delim, kv_delim);
+ },
+ toURL: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return "?" + this.toKV.apply(this, arguments);
+ },
+ toString: function(){
+ return this.getClassName() + "[" + this.length + "]";
+ },
+ toStringWithIds: function(){
+ var modelIds;
+ modelIds = this.models.map(function(it){
+ var _ref;
+ return "\"" + ((_ref = it.id) != null
+ ? _ref
+ : it.cid) + "\"";
+ }).join(', ');
+ return this.getClassName() + "[" + this.length + "](" + modelIds + ")";
+ }
+}));
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/base-model.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var Backbone, op, BaseBackboneMixin, mixinBase, BaseModel, BaseList, _ref, _, __slice = [].slice;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+_ref = require('kraken/base/base-mixin'), BaseBackboneMixin = _ref.BaseBackboneMixin, mixinBase = _ref.mixinBase;
+/**
+ * @class Base model, extending Backbone.Model, used by scaffold and others.
+ * @extends Backbone.Model
+ */
+BaseModel = exports.BaseModel = Backbone.Model.extend(mixinBase({
+ constructor: (function(){
+ function BaseModel(){
+ this.__class__ = this.constructor;
+ this.__superclass__ = this.constructor.__super__.constructor;
+ this.waitingOn = 0;
+ return Backbone.Model.apply(this, arguments);
+ }
+ return BaseModel;
+ }()),
+ url: function(){
+ return this.urlRoot + "/" + this.get('id') + ".json";
+ },
+ has: function(key){
+ return this.get(key) != null;
+ },
+ get: function(key){
+ return _.getNested(this.attributes, key);
+ }
+ /**
+ * Override to customize what data or assets the model requires,
+ * and how they should be loaded.
+ *
+ * By default, `load()` simply calls `loadModel()` via `loader()`.
+ *
+ * @see BaseModel#loader
+ * @see BaseModel#loadModel
+ * @returns {this}
+ */,
+ load: function(){
+ console.log(this + ".load()");
+ this.loader({
+ start: this.loadModel,
+ completeEvent: 'fetch-success'
+ });
+ return this;
+ }
+ /**
+ * Wraps the loading workflow boilerplate:
+ * - Squelches multiple loads from running at once
+ * - Squelches loads post-ready, unless forced
+ * - Triggers a start event
+ * - Triggers "ready" when complete
+ * - Wraps workflow with wait/unwait
+ * - Cleans up "loading" state
+ *
+ * @protected
+ * @param {Object} [opts={}] Options:
+ * @param {Function} opts.start Function that starts the loading process. Always called with `this` as the context.
+ * @param {String} [opts.startEvent='load'] Event to trigger before beginning the load.
+ * @param {String} [opts.completeEvent='load-success'] Event which signals loading has completed successfully.
+ * @param {String} [opts.errorEvent='load-error'] Event which signals loading has completed but failed.
+ * @param {Boolean} [opts.force=false] If true, reset ready state if we're ready before proceeding.
+ * @param {Boolean} [opts.readyIfError=false] If true, move fire the ready event when loading completes, even if it failed.
+ * @returns {this}
+ */,
+ loader: function(opts){
+ var _this = this;
+ opts == null && (opts = {});
+ opts = (__import({
+ force: false,
+ readyIfError: false,
+ startEvent: 'load',
+ completeEvent: 'load-success',
+ errorEvent: 'load-error'
+ }, opts));
+ if (opts.force) {
+ this.resetReady();
+ }
+ if (!opts.start) {
+ throw new Error('You must specify a `start` function to start loading!');
+ }
+ if (this.loading || this.ready) {
+ return this;
+ }
+ this.wait();
+ this.loading = true;
+ this.trigger(opts.startEvent, this);
+ this.once(opts.completeEvent, function(){
+ _this.loading = false;
+ _this.unwait();
+ if (opts.completeEvent !== 'load-success') {
+ _this.trigger('load-success', _this);
+ }
+ return _this.triggerReady();
+ });
+ this.once(opts.errorEvent, function(){
+ _this.loading = false;
+ _this.unwait();
+ if (opts.errorEvent !== 'load-error') {
+ _this.trigger('load-error', _this);
+ }
+ if (opts.readyIfError) {
+ return _this.triggerReady();
+ }
+ });
+ opts.start.call(this);
+ return this;
+ }
+ /**
+ * Runs `.fetch()`, triggering a `fetch` event at start, and
+ * `fetch-success` / `fetch-error` on completion.
+ *
+ * @protected
+ * @returns {this}
+ */,
+ loadModel: function(){
+ var _this = this;
+ this.wait();
+ this.trigger('fetch', this);
+ this.fetch({
+ success: function(){
+ _this.unwait();
+ return _this.trigger('fetch-success', _this);
+ },
+ error: function(){
+ _this.unwait();
+ return _this.trigger.apply(_this, ['fetch-error', _this].concat(__slice.call(arguments)));
+ }
+ });
+ return this;
+ },
+ serialize: function(v){
+ if (_.isBoolean(v)) {
+ v = Number(v);
+ } else if (_.isObject(v)) {
+ v = JSON.stringify(v);
+ }
+ return String(v);
+ }
+ /**
+ * Like `.toJSON()` in that it should return a plain object with no functions,
+ * but for the purpose of `.toKV()`, allowing you to customize the values
+ * included and keys used.
+ *
+ * @param {Object} [opts={}] Options:
+ * @param {Boolean} [opts.keepFunctions=false] If false, functions will be omitted from the result.
+ * @returns {Object}
+ */,
+ toKVPairs: function(opts){
+ var kvo, k, v;
+ opts == null && (opts = {});
+ opts = (__import({
+ keepFunctions: false
+ }, opts));
+ kvo = _.collapseObject(this.toJSON());
+ for (k in kvo) {
+ v = kvo[k];
+ if (opts.keepFunctions || typeof v !== 'function') {
+ kvo[k] = this.serialize(v);
+ }
+ }
+ return kvo;
+ }
+ /**
+ * Serialize the model into a `www-form-encoded` string suitable for use as
+ * a query string or a POST body.
+ * @returns {String}
+ */,
+ toKV: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return _.toKV(this.toKVPairs(), item_delim, kv_delim);
+ }
+ /**
+ * @returns {String} URL identifying this model.
+ */,
+ toURL: function(){
+ return "?" + this.toKV.apply(this, arguments);
+ },
+ toString: function(){
+ return this.getClassName() + "(cid=" + this.cid + ", id=" + this.id + ")";
+ }
+}));
+__import(BaseModel, {
+ /**
+ * Factory method which constructs an instance of this model from a string of KV-pairs.
+ * This is a class method inherited by models which extend {BaseModel}.
+ * @static
+ * @param {String|Object} o Serialized KV-pairs (or a plain object).
+ * @returns {BaseModel} An instance of this model.
+ */
+ fromKV: function(o, item_delim, kv_delim){
+ var Cls;
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ if (typeof o === 'string') {
+ o = _.fromKV(o, item_delim, kv_delim);
+ }
+ Cls = typeof this === 'function'
+ ? this
+ : this.constructor;
+ return new Cls(_.uncollapseObject(o));
+ }
+});
+/**
+ * @class Base collection, extending Backbone.Collection, used by scaffold and others.
+ * @extends Backbone.Collection
+ */
+BaseList = exports.BaseList = Backbone.Collection.extend(mixinBase({
+ constructor: (function(){
+ function BaseList(){
+ this.__class__ = this.constructor;
+ this.__superclass__ = this.constructor.__super__.constructor;
+ this.waitingOn = 0;
+ return Backbone.Collection.apply(this, arguments);
+ }
+ return BaseList;
+ }()),
+ getIds: function(){
+ return this.models.map(function(it){
+ return it.id || it.get('id') || it.cid;
+ });
+ },
+ toKVPairs: function(){
+ return _.collapseObject(this.toJSON());
+ },
+ toKV: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return _.toKV(this.toKVPairs(), item_delim, kv_delim);
+ },
+ toURL: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return "?" + this.toKV.apply(this, arguments);
+ },
+ toString: function(){
+ return this.getClassName() + "[" + this.length + "]";
+ },
+ toStringWithIds: function(){
+ var modelIds;
+ modelIds = this.models.map(function(it){
+ var _ref;
+ return "\"" + ((_ref = it.id) != null
+ ? _ref
+ : it.cid) + "\"";
+ }).join(', ');
+ return this.getClassName() + "[" + this.length + "](" + modelIds + ")";
+ }
+}));
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
--- /dev/null
+var Backbone, op, BaseBackboneMixin, mixinBase, BaseModel, DataBinding, BaseView, ViewList, _ref, _, __slice = [].slice;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+_ref = require('kraken/base/base-mixin'), BaseBackboneMixin = _ref.BaseBackboneMixin, mixinBase = _ref.mixinBase;
+BaseModel = require('kraken/base/base-mixin').BaseModel;
+DataBinding = require('kraken/base/data-binding').DataBinding;
+/**
+ * @class Base view, extending Backbone.View, used by scaffold and others.
+ * @extends Backbone.View
+ */
+BaseView = exports.BaseView = Backbone.View.extend(mixinBase({
+ tagName: 'section',
+ model: BaseModel
+ /**
+ * Method-name called by `onReturnKeypress` when used as an event-handler.
+ * @type String
+ */,
+ callOnReturnKeypress: null
+ /**
+ * Parent view of this view.
+ * @type BaseView
+ */,
+ parent: null
+ /**
+ * Array of [view, selector]-pairs.
+ * @type Array<[BaseView, String]>
+ */,
+ subviews: []
+ /**
+ * Whether this view has been added to the DOM.
+ * @type Boolean
+ */,
+ isAttached: false,
+ constructor: (function(){
+ function BaseView(){
+ this.__class__ = this.constructor;
+ this.__superclass__ = this.constructor.__super__.constructor;
+ this.waitingOn = 0;
+ this.subviews = new ViewList;
+ this.onReturnKeypress = _.debounce(this.onReturnKeypress.bind(this), 50);
+ Backbone.View.apply(this, arguments);
+ return this.trigger('create', this);
+ }
+ return BaseView;
+ }()),
+ initialize: function(){
+ this.__apply_bind__();
+ this.setModel(this.model);
+ this.build();
+ return this.$el.on('form submit', function(it){
+ return it.preventDefault();
+ });
+ },
+ setModel: function(model){
+ var data;
+ if (this.model) {
+ this.model.off('change', this.render, this);
+ this.model.off('destroy', this.remove, this);
+ delete this.model.view;
+ data = this.$el.data();
+ delete data.model;
+ delete data.view;
+ }
+ if (this.model = model) {
+ this.model.view = this;
+ this.$el.data({
+ model: this.model,
+ view: this
+ });
+ this.model.on('change', this.render, this);
+ this.model.on('destroy', this.remove, this);
+ this.trigger('change:model', this, model);
+ }
+ return model;
+ },
+ setParent: function(parent){
+ var old_parent, _ref;
+ _ref = [this.parent, parent], old_parent = _ref[0], this.parent = _ref[1];
+ this.trigger('parent', this, parent, old_parent);
+ return this;
+ },
+ unsetParent: function(){
+ var old_parent, _ref;
+ _ref = [this.parent, null], old_parent = _ref[0], this.parent = _ref[1];
+ this.trigger('unparent', this, old_parent);
+ return this;
+ },
+ addSubview: function(view){
+ this.removeSubview(view);
+ this.subviews.push(view);
+ view.setParent(this);
+ return view;
+ },
+ removeSubview: function(view){
+ if (this.hasSubview(view)) {
+ view.remove();
+ this.subviews.remove(view);
+ view.unsetParent();
+ }
+ return view;
+ },
+ hasSubview: function(view){
+ return this.subviews.contains(view);
+ },
+ invokeSubviews: function(){
+ var _ref;
+ return (_ref = this.subviews).invoke.apply(_ref, arguments);
+ },
+ removeAllSubviews: function(){
+ this.subviews.forEach(this.removeSubview, this);
+ return this;
+ },
+ attach: function(el){
+ var _this = this;
+ this.$el.appendTo(el);
+ if (this.isAttached) {
+ return this;
+ }
+ this.isAttached = true;
+ _.delay(function(){
+ _this.delegateEvents();
+ return _this.trigger('attach', _this);
+ }, 50);
+ return this;
+ },
+ remove: function(){
+ this.$el.remove();
+ if (!this.isAttached) {
+ return this;
+ }
+ this.isAttached = false;
+ this.trigger('unattach', this);
+ return this;
+ },
+ clear: function(){
+ this.remove();
+ this.model.destroy();
+ this.trigger('clear', this);
+ return this;
+ },
+ hide: function(){
+ this.$el.hide();
+ this.trigger('hide', this);
+ return this;
+ },
+ show: function(){
+ this.$el.show();
+ this.trigger('show', this);
+ return this;
+ }
+ /**
+ * Attach each subview to its bind-point.
+ * @returns {this}
+ */,
+ attachSubviews: function(){
+ var bps, view, bp, _i, _ref, _len;
+ bps = this.getOwnSubviewBindPoints();
+ if (this.subviews.length && !bps.length) {
+ console.warn(this + ".attachSubviews(): no subview bind-points found!");
+ return this;
+ }
+ for (_i = 0, _len = (_ref = this.subviews).length; _i < _len; ++_i) {
+ view = _ref[_i];
+ if (bp = this.findSubviewBindPoint(view, bps)) {
+ view.attach(bp);
+ } else {
+ console.warn(this + ".attachSubviews(): Unable to find bind-point for " + view + "!");
+ }
+ }
+ return this;
+ }
+ /**
+ * Finds all subview bind-points under this view's element, but not under
+ * the view element of any subview.
+ * @returns {jQuery|undefined}
+ */,
+ getOwnSubviewBindPoints: function(){
+ return this.$('[data-subview]').not(this.$('[data-subview] [data-subview]'));
+ }
+ /**
+ * Find the matching subview bind-point for the given view.
+ */,
+ findSubviewBindPoint: function(view, bind_points){
+ var bp;
+ bind_points || (bind_points = this.getOwnSubviewBindPoints());
+ if (view.id) {
+ bp = bind_points.filter("[data-subview$=':" + view.id + "']");
+ if (bp.length) {
+ return bp.eq(0);
+ }
+ }
+ bp = bind_points.filter("[data-subview='" + view.getClassName() + "']");
+ if (bp.length) {
+ return bp.eq(0);
+ }
+ },
+ toTemplateLocals: function(){
+ return this.model.toJSON();
+ },
+ $template: function(){
+ return $(this.template((__import({
+ _: _,
+ op: op,
+ model: this.model,
+ view: this
+ }, this.toTemplateLocals()))));
+ },
+ build: function(){
+ var outer;
+ if (!this.template) {
+ return this;
+ }
+ outer = this.$template();
+ this.$el.html(outer.html()).attr({
+ id: outer.attr('id'),
+ 'class': outer.attr('class')
+ });
+ this.attachSubviews();
+ this.isBuilt = true;
+ return this;
+ },
+ render: function(){
+ this.wait();
+ if (this.isBuilt) {
+ this.update();
+ } else {
+ this.build();
+ }
+ this.renderSubviews();
+ this.trigger('render', this);
+ this.unwait();
+ return this;
+ },
+ renderSubviews: function(){
+ this.attachSubviews();
+ this.subviews.invoke('render');
+ return this;
+ },
+ update: function(){
+ var locals;
+ new DataBinding(this).update(locals = this.toTemplateLocals());
+ this.trigger('update', this, locals);
+ return this;
+ }
+ /* * * * Events * * * */,
+ bubbleEventDown: function(evt){
+ this.invokeSubviews.apply(this, ['trigger'].concat(__slice.call(arguments)));
+ return this;
+ },
+ redispatch: function(evt){
+ var args;
+ args = __slice.call(arguments, 1);
+ this.trigger.apply(this, [evt, this].concat(__slice.call(args)));
+ return this;
+ },
+ onlyOnReturn: function(fn){
+ var args, _this = this;
+ args = __slice.call(arguments, 1);
+ fn = _.debounce(fn.bind(this), 50);
+ return function(evt){
+ if (evt.keyCode === 13) {
+ return fn.apply(_this, args);
+ }
+ };
+ }
+ /**
+ * Call a delegate on keypress == the return key.
+ * @returns {Function} Keypress event handler.
+ */,
+ onReturnKeypress: function(evt){
+ var fn;
+ if (this.callOnReturnKeypress) {
+ fn = this[this.callOnReturnKeypress];
+ }
+ if (fn && evt.keyCode === 13) {
+ return fn.call(this);
+ }
+ },
+ toString: function(){
+ return this.getClassName() + "(model=" + this.model + ")";
+ }
+}));
+['get', 'set', 'unset', 'toJSON', 'toKV', 'toURL'].forEach(function(methodname){
+ return BaseView.prototype[methodname] = function(){
+ return this.model[methodname].apply(this.model, arguments);
+ };
+});
+exports.ViewList = ViewList = (function(superclass){
+ ViewList.displayName = 'ViewList';
+ var prototype = __extend(ViewList, superclass).prototype, constructor = ViewList;
+ function ViewList(views){
+ views == null && (views = []);
+ superclass.apply(this, arguments);
+ }
+ prototype.extend = function(views){
+ var _this = this;
+ _.each(views, function(it){
+ return _this.push(it);
+ });
+ return this;
+ };
+ prototype.findByModel = function(model){
+ return this.find(function(it){
+ return it.model === model;
+ });
+ };
+ prototype.toString = function(){
+ var contents;
+ contents = this.length ? "\"" + this.join('","') + "\"" : '';
+ return "ViewList[" + this.length + "](" + contents + ")";
+ };
+ return ViewList;
+}(Array));
+['each', 'contains', 'invoke', 'pluck', 'find', 'remove', 'compact', 'flatten', 'without', 'union', 'intersection', 'difference', 'unique', 'uniq'].forEach(function(methodname){
+ return ViewList.prototype[methodname] = function(){
+ var _ref;
+ return (_ref = _[methodname]).call.apply(_ref, [_, this].concat(__slice.call(arguments)));
+ };
+});
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+function __extend(sub, sup){
+ function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+ (sub.prototype = new fun).constructor = sub;
+ if (typeof sup.extended == 'function') sup.extended(sub);
+ return sub;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/base-view.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var Backbone, op, BaseBackboneMixin, mixinBase, BaseModel, DataBinding, BaseView, ViewList, _ref, _, __slice = [].slice;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+_ref = require('kraken/base/base-mixin'), BaseBackboneMixin = _ref.BaseBackboneMixin, mixinBase = _ref.mixinBase;
+BaseModel = require('kraken/base/base-mixin').BaseModel;
+DataBinding = require('kraken/base/data-binding').DataBinding;
+/**
+ * @class Base view, extending Backbone.View, used by scaffold and others.
+ * @extends Backbone.View
+ */
+BaseView = exports.BaseView = Backbone.View.extend(mixinBase({
+ tagName: 'section',
+ model: BaseModel
+ /**
+ * Method-name called by `onReturnKeypress` when used as an event-handler.
+ * @type String
+ */,
+ callOnReturnKeypress: null
+ /**
+ * Parent view of this view.
+ * @type BaseView
+ */,
+ parent: null
+ /**
+ * Array of [view, selector]-pairs.
+ * @type Array<[BaseView, String]>
+ */,
+ subviews: []
+ /**
+ * Whether this view has been added to the DOM.
+ * @type Boolean
+ */,
+ isAttached: false,
+ constructor: (function(){
+ function BaseView(){
+ this.__class__ = this.constructor;
+ this.__superclass__ = this.constructor.__super__.constructor;
+ this.waitingOn = 0;
+ this.subviews = new ViewList;
+ this.onReturnKeypress = _.debounce(this.onReturnKeypress.bind(this), 50);
+ Backbone.View.apply(this, arguments);
+ return this.trigger('create', this);
+ }
+ return BaseView;
+ }()),
+ initialize: function(){
+ this.__apply_bind__();
+ this.setModel(this.model);
+ this.build();
+ return this.$el.on('form submit', function(it){
+ return it.preventDefault();
+ });
+ },
+ setModel: function(model){
+ var data;
+ if (this.model) {
+ this.model.off('change', this.render, this);
+ this.model.off('destroy', this.remove, this);
+ delete this.model.view;
+ data = this.$el.data();
+ delete data.model;
+ delete data.view;
+ }
+ if (this.model = model) {
+ this.model.view = this;
+ this.$el.data({
+ model: this.model,
+ view: this
+ });
+ this.model.on('change', this.render, this);
+ this.model.on('destroy', this.remove, this);
+ this.trigger('change:model', this, model);
+ }
+ return model;
+ },
+ setParent: function(parent){
+ var old_parent, _ref;
+ _ref = [this.parent, parent], old_parent = _ref[0], this.parent = _ref[1];
+ this.trigger('parent', this, parent, old_parent);
+ return this;
+ },
+ unsetParent: function(){
+ var old_parent, _ref;
+ _ref = [this.parent, null], old_parent = _ref[0], this.parent = _ref[1];
+ this.trigger('unparent', this, old_parent);
+ return this;
+ },
+ addSubview: function(view){
+ this.removeSubview(view);
+ this.subviews.push(view);
+ view.setParent(this);
+ return view;
+ },
+ removeSubview: function(view){
+ if (this.hasSubview(view)) {
+ view.remove();
+ this.subviews.remove(view);
+ view.unsetParent();
+ }
+ return view;
+ },
+ hasSubview: function(view){
+ return this.subviews.contains(view);
+ },
+ invokeSubviews: function(){
+ var _ref;
+ return (_ref = this.subviews).invoke.apply(_ref, arguments);
+ },
+ removeAllSubviews: function(){
+ this.subviews.forEach(this.removeSubview, this);
+ return this;
+ },
+ attach: function(el){
+ var _this = this;
+ this.$el.appendTo(el);
+ if (this.isAttached) {
+ return this;
+ }
+ this.isAttached = true;
+ _.delay(function(){
+ _this.delegateEvents();
+ return _this.trigger('attach', _this);
+ }, 50);
+ return this;
+ },
+ remove: function(){
+ this.$el.remove();
+ if (!this.isAttached) {
+ return this;
+ }
+ this.isAttached = false;
+ this.trigger('unattach', this);
+ return this;
+ },
+ clear: function(){
+ this.remove();
+ this.model.destroy();
+ this.trigger('clear', this);
+ return this;
+ },
+ hide: function(){
+ this.$el.hide();
+ this.trigger('hide', this);
+ return this;
+ },
+ show: function(){
+ this.$el.show();
+ this.trigger('show', this);
+ return this;
+ }
+ /**
+ * Attach each subview to its bind-point.
+ * @returns {this}
+ */,
+ attachSubviews: function(){
+ var bps, view, bp, _i, _ref, _len;
+ bps = this.getOwnSubviewBindPoints();
+ if (this.subviews.length && !bps.length) {
+ console.warn(this + ".attachSubviews(): no subview bind-points found!");
+ return this;
+ }
+ for (_i = 0, _len = (_ref = this.subviews).length; _i < _len; ++_i) {
+ view = _ref[_i];
+ if (bp = this.findSubviewBindPoint(view, bps)) {
+ view.attach(bp);
+ } else {
+ console.warn(this + ".attachSubviews(): Unable to find bind-point for " + view + "!");
+ }
+ }
+ return this;
+ }
+ /**
+ * Finds all subview bind-points under this view's element, but not under
+ * the view element of any subview.
+ * @returns {jQuery|undefined}
+ */,
+ getOwnSubviewBindPoints: function(){
+ return this.$('[data-subview]').not(this.$('[data-subview] [data-subview]'));
+ }
+ /**
+ * Find the matching subview bind-point for the given view.
+ */,
+ findSubviewBindPoint: function(view, bind_points){
+ var bp;
+ bind_points || (bind_points = this.getOwnSubviewBindPoints());
+ if (view.id) {
+ bp = bind_points.filter("[data-subview$=':" + view.id + "']");
+ if (bp.length) {
+ return bp.eq(0);
+ }
+ }
+ bp = bind_points.filter("[data-subview='" + view.getClassName() + "']");
+ if (bp.length) {
+ return bp.eq(0);
+ }
+ },
+ toTemplateLocals: function(){
+ return this.model.toJSON();
+ },
+ $template: function(){
+ return $(this.template((__import({
+ _: _,
+ op: op,
+ model: this.model,
+ view: this
+ }, this.toTemplateLocals()))));
+ },
+ build: function(){
+ var outer;
+ if (!this.template) {
+ return this;
+ }
+ outer = this.$template();
+ this.$el.html(outer.html()).attr({
+ id: outer.attr('id'),
+ 'class': outer.attr('class')
+ });
+ this.attachSubviews();
+ this.isBuilt = true;
+ return this;
+ },
+ render: function(){
+ this.wait();
+ if (this.isBuilt) {
+ this.update();
+ } else {
+ this.build();
+ }
+ this.renderSubviews();
+ this.trigger('render', this);
+ this.unwait();
+ return this;
+ },
+ renderSubviews: function(){
+ this.attachSubviews();
+ this.subviews.invoke('render');
+ return this;
+ },
+ update: function(){
+ var locals;
+ new DataBinding(this).update(locals = this.toTemplateLocals());
+ this.trigger('update', this, locals);
+ return this;
+ }
+ /* * * * Events * * * */,
+ bubbleEventDown: function(evt){
+ this.invokeSubviews.apply(this, ['trigger'].concat(__slice.call(arguments)));
+ return this;
+ },
+ redispatch: function(evt){
+ var args;
+ args = __slice.call(arguments, 1);
+ this.trigger.apply(this, [evt, this].concat(__slice.call(args)));
+ return this;
+ },
+ onlyOnReturn: function(fn){
+ var args, _this = this;
+ args = __slice.call(arguments, 1);
+ fn = _.debounce(fn.bind(this), 50);
+ return function(evt){
+ if (evt.keyCode === 13) {
+ return fn.apply(_this, args);
+ }
+ };
+ }
+ /**
+ * Call a delegate on keypress == the return key.
+ * @returns {Function} Keypress event handler.
+ */,
+ onReturnKeypress: function(evt){
+ var fn;
+ if (this.callOnReturnKeypress) {
+ fn = this[this.callOnReturnKeypress];
+ }
+ if (fn && evt.keyCode === 13) {
+ return fn.call(this);
+ }
+ },
+ toString: function(){
+ return this.getClassName() + "(model=" + this.model + ")";
+ }
+}));
+['get', 'set', 'unset', 'toJSON', 'toKV', 'toURL'].forEach(function(methodname){
+ return BaseView.prototype[methodname] = function(){
+ return this.model[methodname].apply(this.model, arguments);
+ };
+});
+exports.ViewList = ViewList = (function(superclass){
+ ViewList.displayName = 'ViewList';
+ var prototype = __extend(ViewList, superclass).prototype, constructor = ViewList;
+ function ViewList(views){
+ views == null && (views = []);
+ superclass.apply(this, arguments);
+ }
+ prototype.extend = function(views){
+ var _this = this;
+ _.each(views, function(it){
+ return _this.push(it);
+ });
+ return this;
+ };
+ prototype.findByModel = function(model){
+ return this.find(function(it){
+ return it.model === model;
+ });
+ };
+ prototype.toString = function(){
+ var contents;
+ contents = this.length ? "\"" + this.join('","') + "\"" : '';
+ return "ViewList[" + this.length + "](" + contents + ")";
+ };
+ return ViewList;
+}(Array));
+['each', 'contains', 'invoke', 'pluck', 'find', 'remove', 'compact', 'flatten', 'without', 'union', 'intersection', 'difference', 'unique', 'uniq'].forEach(function(methodname){
+ return ViewList.prototype[methodname] = function(){
+ var _ref;
+ return (_ref = _[methodname]).call.apply(_ref, [_, this].concat(__slice.call(arguments)));
+ };
+});
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+function __extend(sub, sup){
+ function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+ (sub.prototype = new fun).constructor = sub;
+ if (typeof sup.extended == 'function') sup.extended(sub);
+ return sub;
+}
+
+});
--- /dev/null
+var EventEmitter, op, Base, k, _ref, _, _i, _len, __slice = [].slice;
+EventEmitter = require('events').EventEmitter;
+EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
+EventEmitter.prototype.trigger = EventEmitter.prototype.emit;
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+/**
+ * @class Eventful base class.
+ * @extends EventEmitter
+ */
+Base = (function(superclass){
+ /**
+ * After the super chain has exhausted (but not necessarily at the end
+ * of init -- it depends on when you super()), Base will publish a 'new'
+ * event on the instance's class, allowing anyone to subscribe to
+ * notifications about new objects.
+ * @constructor
+ */
+ Base.displayName = 'Base';
+ var prototype = __extend(Base, superclass).prototype, constructor = Base;
+ function Base(){
+ this.__class__ = this.constructor;
+ this.__superclass__ = this.constructor.superclass;
+ this.__apply_bind__();
+ superclass.call(this);
+ this.__class__.emit('new', this);
+ }
+ /**
+ * A list of method-names to bind on `initialize`; set this on a subclass to override.
+ * @type Array<String>
+ */
+ prototype.__bind__ = [];
+ /**
+ * Applies the contents of `__bind__`.
+ */
+ prototype.__apply_bind__ = function(){
+ var names;
+ names = _(this.pluckSuperAndSelf('__bind__')).chain().flatten().compact().unique().value();
+ if (names.length) {
+ return _.bindAll.apply(_, [this].concat(__slice.call(names)));
+ }
+ };
+ prototype.getClassName = function(){
+ return (this.constructor.name || this.constructor.displayName) + "";
+ };
+ prototype.toString = function(){
+ return this.getClassName() + "()";
+ };
+ Base.extended = function(Subclass){
+ var k, v, _own = {}.hasOwnProperty;
+ for (k in this) if (_own.call(this, k)) {
+ v = this[k];
+ if (typeof v === 'function') {
+ Subclass[k] = v;
+ }
+ }
+ Subclass.__super__ = this.prototype;
+ return Subclass;
+ };
+ return Base;
+}(EventEmitter));
+for (_i = 0, _len = (_ref = ['getSuperClasses', 'pluckSuper', 'pluckSuperAndSelf']).length; _i < _len; ++_i) {
+ k = _ref[_i];
+ Base[k] = Base.prototype[k] = _.methodize(_[k]);
+}
+__import(Base, EventEmitter.prototype);
+module.exports = Base;
+function __extend(sub, sup){
+ function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+ (sub.prototype = new fun).constructor = sub;
+ if (typeof sup.extended == 'function') sup.extended(sub);
+ return sub;
+}
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/base.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var EventEmitter, op, Base, k, _ref, _, _i, _len, __slice = [].slice;
+EventEmitter = require('events').EventEmitter;
+EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
+EventEmitter.prototype.trigger = EventEmitter.prototype.emit;
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+/**
+ * @class Eventful base class.
+ * @extends EventEmitter
+ */
+Base = (function(superclass){
+ /**
+ * After the super chain has exhausted (but not necessarily at the end
+ * of init -- it depends on when you super()), Base will publish a 'new'
+ * event on the instance's class, allowing anyone to subscribe to
+ * notifications about new objects.
+ * @constructor
+ */
+ Base.displayName = 'Base';
+ var prototype = __extend(Base, superclass).prototype, constructor = Base;
+ function Base(){
+ this.__class__ = this.constructor;
+ this.__superclass__ = this.constructor.superclass;
+ this.__apply_bind__();
+ superclass.call(this);
+ this.__class__.emit('new', this);
+ }
+ /**
+ * A list of method-names to bind on `initialize`; set this on a subclass to override.
+ * @type Array<String>
+ */
+ prototype.__bind__ = [];
+ /**
+ * Applies the contents of `__bind__`.
+ */
+ prototype.__apply_bind__ = function(){
+ var names;
+ names = _(this.pluckSuperAndSelf('__bind__')).chain().flatten().compact().unique().value();
+ if (names.length) {
+ return _.bindAll.apply(_, [this].concat(__slice.call(names)));
+ }
+ };
+ prototype.getClassName = function(){
+ return (this.constructor.name || this.constructor.displayName) + "";
+ };
+ prototype.toString = function(){
+ return this.getClassName() + "()";
+ };
+ Base.extended = function(Subclass){
+ var k, v, _own = {}.hasOwnProperty;
+ for (k in this) if (_own.call(this, k)) {
+ v = this[k];
+ if (typeof v === 'function') {
+ Subclass[k] = v;
+ }
+ }
+ Subclass.__super__ = this.prototype;
+ return Subclass;
+ };
+ return Base;
+}(EventEmitter));
+for (_i = 0, _len = (_ref = ['getSuperClasses', 'pluckSuper', 'pluckSuperAndSelf']).length; _i < _len; ++_i) {
+ k = _ref[_i];
+ Base[k] = Base.prototype[k] = _.methodize(_[k]);
+}
+__import(Base, EventEmitter.prototype);
+module.exports = Base;
+function __extend(sub, sup){
+ function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+ (sub.prototype = new fun).constructor = sub;
+ if (typeof sup.extended == 'function') sup.extended(sub);
+ return sub;
+}
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
--- /dev/null
+var op, BaseModel, BaseList, Cascade, CascadingModel, _ref, _;
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+_ref = require('kraken/base/base-model'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
+Cascade = require('kraken/util/cascade');
+/**
+ * @class A model that implements cascading lookups for its attributes.
+ */
+CascadingModel = exports.CascadingModel = BaseModel.extend({
+ /**
+ * The lookup cascade.
+ * @type Cascade
+ */
+ cascade: null,
+ constructor: (function(){
+ function CascadingModel(attributes, opts){
+ attributes == null && (attributes = {});
+ this.cascade = new Cascade(attributes);
+ return BaseModel.call(this, attributes, opts);
+ }
+ return CascadingModel;
+ }()),
+ initialize: function(){
+ return BaseModel.prototype.initialize.apply(this, arguments);
+ }
+ /**
+ * Recursively look up a (potenitally nested) attribute in the lookup chain.
+ * @param {String} key Attribute key (potenitally nested using dot-delimited subkeys).
+ * @returns {*}
+ */,
+ get: function(key){
+ return this.cascade.get(key);
+ },
+ toJSON: function(opts){
+ opts == null && (opts = {});
+ opts = (__import({
+ collapseCascade: false
+ }, opts));
+ if (opts.collapseCascade) {
+ return this.cascade.collapse();
+ } else {
+ return BaseModel.prototype.toJSON.apply(this, arguments);
+ }
+ }
+});
+['addLookup', 'removeLookup', 'popLookup', 'shiftLookup', 'unshiftLookup', 'isOwnProperty', 'isOwnValue', 'isInheritedValue', 'isModifiedValue'].forEach(function(methodname){
+ return CascadingModel.prototype[methodname] = function(){
+ return this.cascade[methodname].apply(this.cascade, arguments);
+ };
+});
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/cascading-model.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var op, BaseModel, BaseList, Cascade, CascadingModel, _ref, _;
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+_ref = require('kraken/base/base-model'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
+Cascade = require('kraken/util/cascade');
+/**
+ * @class A model that implements cascading lookups for its attributes.
+ */
+CascadingModel = exports.CascadingModel = BaseModel.extend({
+ /**
+ * The lookup cascade.
+ * @type Cascade
+ */
+ cascade: null,
+ constructor: (function(){
+ function CascadingModel(attributes, opts){
+ attributes == null && (attributes = {});
+ this.cascade = new Cascade(attributes);
+ return BaseModel.call(this, attributes, opts);
+ }
+ return CascadingModel;
+ }()),
+ initialize: function(){
+ return BaseModel.prototype.initialize.apply(this, arguments);
+ }
+ /**
+ * Recursively look up a (potenitally nested) attribute in the lookup chain.
+ * @param {String} key Attribute key (potenitally nested using dot-delimited subkeys).
+ * @returns {*}
+ */,
+ get: function(key){
+ return this.cascade.get(key);
+ },
+ toJSON: function(opts){
+ opts == null && (opts = {});
+ opts = (__import({
+ collapseCascade: false
+ }, opts));
+ if (opts.collapseCascade) {
+ return this.cascade.collapse();
+ } else {
+ return BaseModel.prototype.toJSON.apply(this, arguments);
+ }
+ }
+});
+['addLookup', 'removeLookup', 'popLookup', 'shiftLookup', 'unshiftLookup', 'isOwnProperty', 'isOwnValue', 'isInheritedValue', 'isModifiedValue'].forEach(function(methodname){
+ return CascadingModel.prototype[methodname] = function(){
+ return this.cascade[methodname].apply(this.cascade, arguments);
+ };
+});
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
--- /dev/null
+var Backbone, op, DataBinding, _ref, _;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+exports.DataBinding = DataBinding = (function(){
+ DataBinding.displayName = 'DataBinding';
+ var prototype = DataBinding.prototype, constructor = DataBinding;
+ prototype.data = null;
+ prototype.context = null;
+ prototype.el = null;
+ prototype.$el = null;
+ prototype.bindPoints = null;
+ function DataBinding(el, context){
+ this.context = context != null ? context : el;
+ if (el instanceof Backbone.View) {
+ el = el.$el;
+ }
+ this.$el = $(el);
+ this.el = this.$el.get(0);
+ this.bindPoints = this.$('[data-bind], [name]').not(this.$('[data-subview]').find('[data-bind], [name]'));
+ }
+ prototype.$ = function(sel){
+ return this.$el.find(sel);
+ };
+ prototype.serialize = function(it){
+ return it;
+ };
+ prototype.update = function(data){
+ var key, val, _ref;
+ this.data = data;
+ for (key in _ref = _.collapseObject(this.data)) {
+ val = _ref[key];
+ this.updateBinding(key, val);
+ }
+ return this;
+ };
+ prototype.updateBinding = function(key, val){
+ var bp;
+ if (bp = this.findDataBindPoint(key)) {
+ if (_.isFunction(val)) {
+ val.call(this.context, val, key, bp, this.data);
+ } else if (bp.is('input:checkbox')) {
+ bp.attr('checked', !!val);
+ } else if (bp.is('input, textarea')) {
+ bp.val(this.serialize(val));
+ } else {
+ if (op.toBool(bp.data('data-bind-escape'))) {
+ bp.text(this.serialize(val));
+ } else {
+ bp.html(this.serialize(val));
+ }
+ }
+ } else {
+ false && console.warn(this + ".updateBinding(): Unable to find data bind-point for " + key + "=" + val + "!");
+ }
+ return this;
+ };
+ prototype.findDataBindPoint = function(key){
+ var bp;
+ bp = this.bindPoints.filter("[name='" + key + "'], [data-bind='" + key + "']");
+ if (bp.length) {
+ return bp.eq(0);
+ }
+ };
+ return DataBinding;
+}());
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/data-binding.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var Backbone, op, DataBinding, _ref, _;
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+exports.DataBinding = DataBinding = (function(){
+ DataBinding.displayName = 'DataBinding';
+ var prototype = DataBinding.prototype, constructor = DataBinding;
+ prototype.data = null;
+ prototype.context = null;
+ prototype.el = null;
+ prototype.$el = null;
+ prototype.bindPoints = null;
+ function DataBinding(el, context){
+ this.context = context != null ? context : el;
+ if (el instanceof Backbone.View) {
+ el = el.$el;
+ }
+ this.$el = $(el);
+ this.el = this.$el.get(0);
+ this.bindPoints = this.$('[data-bind], [name]').not(this.$('[data-subview]').find('[data-bind], [name]'));
+ }
+ prototype.$ = function(sel){
+ return this.$el.find(sel);
+ };
+ prototype.serialize = function(it){
+ return it;
+ };
+ prototype.update = function(data){
+ var key, val, _ref;
+ this.data = data;
+ for (key in _ref = _.collapseObject(this.data)) {
+ val = _ref[key];
+ this.updateBinding(key, val);
+ }
+ return this;
+ };
+ prototype.updateBinding = function(key, val){
+ var bp;
+ if (bp = this.findDataBindPoint(key)) {
+ if (_.isFunction(val)) {
+ val.call(this.context, val, key, bp, this.data);
+ } else if (bp.is('input:checkbox')) {
+ bp.attr('checked', !!val);
+ } else if (bp.is('input, textarea')) {
+ bp.val(this.serialize(val));
+ } else {
+ if (op.toBool(bp.data('data-bind-escape'))) {
+ bp.text(this.serialize(val));
+ } else {
+ bp.html(this.serialize(val));
+ }
+ }
+ } else {
+ false && console.warn(this + ".updateBinding(): Unable to find data bind-point for " + key + "=" + val + "!");
+ }
+ return this;
+ };
+ prototype.findDataBindPoint = function(key){
+ var bp;
+ bp = this.bindPoints.filter("[name='" + key + "'], [data-bind='" + key + "']");
+ if (bp.length) {
+ return bp.eq(0);
+ }
+ };
+ return DataBinding;
+}());
+
+});
--- /dev/null
+var mixins, models, views, cache, cascading, data_binding;
+exports.Base = require('kraken/base/base');
+mixins = require('kraken/base/base-mixin');
+models = require('kraken/base/base-model');
+views = require('kraken/base/base-view');
+cache = require('kraken/base/model-cache');
+cascading = require('kraken/base/cascading-model');
+data_binding = require('kraken/base/data-binding');
+__import(__import(__import(__import(__import(__import(exports, mixins), models), views), cache), cascading), data_binding);
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/index.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var mixins, models, views, cache, cascading, data_binding;
+exports.Base = require('kraken/base/base');
+mixins = require('kraken/base/base-mixin');
+models = require('kraken/base/base-model');
+views = require('kraken/base/base-view');
+cache = require('kraken/base/model-cache');
+cascading = require('kraken/base/cascading-model');
+data_binding = require('kraken/base/data-binding');
+__import(__import(__import(__import(__import(__import(exports, mixins), models), views), cache), cascading), data_binding);
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
--- /dev/null
+var Seq, ReadyEmitter, ModelCache, _;
+_ = require('underscore');
+Seq = require('seq');
+ReadyEmitter = require('kraken/util/event').ReadyEmitter;
+/**
+ * @class Caches models and provides static lookups by ID.
+ */
+exports.ModelCache = ModelCache = (function(superclass){
+ ModelCache.displayName = 'ModelCache';
+ var prototype = __extend(ModelCache, superclass).prototype, constructor = ModelCache;
+ /**
+ * @see ReadyEmitter#readyEventName
+ * @private
+ * @constant
+ * @type String
+ */
+ prototype.readyEventName = 'cache-ready';
+ /**
+ * Default options.
+ * @private
+ * @constant
+ * @type Object
+ */
+ prototype.DEFAULT_OPTIONS = {
+ ready: true,
+ cache: null,
+ create: null,
+ ModelType: null
+ };
+ /**
+ * @private
+ * @type Object
+ */
+ prototype.options = null;
+ /**
+ * Type we're caching (presumably extending `Backbone.Model`), used to create new
+ * instances unless a `create` function was provided in options.
+ * @private
+ * @type Class<Backbone.Model>
+ */
+ prototype.ModelType = null;
+ /**
+ * Collection holding the cached Models.
+ * @private
+ * @type Backbone.Collection
+ */
+ prototype.cache = null;
+ /**
+ * @constructor
+ * @param {Class<Backbone.Model>} [ModelType] Type of cached object (presumably extending
+ * `Backbone.Model`), used to create new instances unless `options.create`
+ * is provided.
+ * @param {Object} [options] Options:
+ * @param {Boolean} [options.ready=true] Starting `ready` state. If false,
+ * the cache will queue lookup calls until `triggerReady()` is called.
+ * @param {Class<Backbone.Collection>} [options.cache=new Backbone.Collection]
+ * The backing data-structure for the cache. If omitted, we'll use a new
+ * `Backbone.Collection`, but really, anything with a `get(id)` method for
+ * model lookup will work here.
+ * @param {Function} [options.create] A function called when a new Model
+ * object is needed, being passed the new model ID.
+ * @param {Class<Backbone.Model>} [options.ModelType] Type of cached object
+ * (presumably extending `Backbone.Model`), used to create new instances
+ * unless `options.create` is provided.
+ */;
+ function ModelCache(ModelType, options){
+ var that, _ref;
+ if (!_.isFunction(ModelType)) {
+ _ref = [ModelType || {}, null], options = _ref[0], ModelType = _ref[1];
+ }
+ this.options = (_ref = {}, __import(_ref, this.DEFAULT_OPTIONS), __import(_ref, options));
+ this.cache = this.options.cache || new Backbone.Collection;
+ this.ModelType = ModelType || this.options.ModelType;
+ if (that = this.options.create) {
+ this.createModel = that;
+ }
+ this.ready = !!this.options.ready;
+ if (this.ModelType) {
+ this.decorate(this.ModelType);
+ }
+ }
+ /**
+ * Called when a new Model object is needed, being passed the new model ID.
+ * Uses the supplied `ModelType`; overriden by `options.create` if provided.
+ *
+ * @param {String} id The model ID to create.
+ * @returns {Model} Created model.
+ */
+ prototype.createModel = function(id){
+ return new this.ModelType({
+ id: id
+ });
+ };
+ /**
+ * Registers a model with the cache. If a model by this ID already exists
+ * in the cache, it will be removed and this one will take its place.
+ *
+ * Fires an `add` event.
+ *
+ * @param {Model} model The model.
+ * @returns {Model} The model.
+ */
+ prototype.register = function(model){
+ if (this.cache.contains(model)) {
+ this.cache.remove(model, {
+ silent: true
+ });
+ }
+ this.cache.add(model);
+ this.trigger('add', this, model);
+ return model;
+ };
+ /**
+ * Synchronously check if a model is in the cache, returning it if so.
+ *
+ * @param {String} id The model ID to get.
+ * @returns {Model}
+ */
+ prototype.get = function(id){
+ return this.cache.get(id);
+ };
+ /**
+ * Asynchronously look up any number of models, requesting them from the
+ * server if not already known to the cache.
+ *
+ * @param {String|Array<String>} ids List of model IDs to lookup.
+ * @param {Function} cb Callback of the form `(err, models)`,
+ * where `err` will be null on success and `models` will be an Array
+ * of model objects.
+ * @param {Object} [cxt=this] Callback context.
+ * @returns {this}
+ */
+ prototype.lookupAll = function(ids, cb, cxt){
+ var _this = this;
+ cxt == null && (cxt = this);
+ if (!_.isArray(ids)) {
+ ids = [ids];
+ }
+ if (!this.ready) {
+ this.on('cache-ready', function(){
+ _this.off('cache-ready', arguments.callee);
+ return _this.lookupAll(ids, cb, cxt);
+ });
+ return this;
+ }
+ Seq(ids).parMap_(function(next, id){
+ var that;
+ if (that = _this.cache.get(id)) {
+ return next.ok(that);
+ }
+ return _this.register(_this.createModel(id)).on('ready', function(it){
+ return next.ok(it);
+ }).load();
+ }).unflatten().seq(function(models){
+ return cb.call(cxt, null, models);
+ })['catch'](function(err){
+ return cb.call(cxt, err);
+ });
+ return this;
+ };
+ /**
+ * Looks up a model, requesting it from the server if it is not already
+ * known to the cache.
+ *
+ * @param {String|Array<String>} id Model ID to lookup.
+ * @param {Function} cb Callback of the form `(err, model)`,
+ * where `err` will be null on success and `model` will be the
+ * model object.
+ * @param {Object} [cxt=this] Callback context.
+ * @returns {this}
+ */
+ prototype.lookup = function(id, cb, cxt){
+ cxt == null && (cxt = this);
+ return this.lookupAll([id], function(err, models){
+ if (err) {
+ return cb.call(cxt, err);
+ } else {
+ return cb.call(cxt, null, models[0]);
+ }
+ });
+ };
+ /**
+ * Decorate an object with the cache methods:
+ * - register
+ * - get
+ * - lookup
+ * - lookupAll
+ *
+ * This is automatically called on `ModelType` if supplied.
+ *
+ * @param {Object} obj Object to decorate.
+ * @returns {obj} The supplied object.
+ */
+ prototype.decorate = function(obj){
+ var m, _i, _ref, _len;
+ obj.__cache__ = this;
+ for (_i = 0, _len = (_ref = ['register', 'get', 'lookup', 'lookupAll']).length; _i < _len; ++_i) {
+ m = _ref[_i];
+ obj[m] = this[m].bind(this);
+ }
+ return obj;
+ };
+ prototype.toString = function(){
+ return (this.constructor.displayName || this.constructor.name) + "(cache=" + this.cache + ")";
+ };
+ return ModelCache;
+}(ReadyEmitter));
+function __extend(sub, sup){
+ function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+ (sub.prototype = new fun).constructor = sub;
+ if (typeof sup.extended == 'function') sup.extended(sub);
+ return sub;
+}
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/model-cache.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var Seq, ReadyEmitter, ModelCache, _;
+_ = require('underscore');
+Seq = require('seq');
+ReadyEmitter = require('kraken/util/event').ReadyEmitter;
+/**
+ * @class Caches models and provides static lookups by ID.
+ */
+exports.ModelCache = ModelCache = (function(superclass){
+ ModelCache.displayName = 'ModelCache';
+ var prototype = __extend(ModelCache, superclass).prototype, constructor = ModelCache;
+ /**
+ * @see ReadyEmitter#readyEventName
+ * @private
+ * @constant
+ * @type String
+ */
+ prototype.readyEventName = 'cache-ready';
+ /**
+ * Default options.
+ * @private
+ * @constant
+ * @type Object
+ */
+ prototype.DEFAULT_OPTIONS = {
+ ready: true,
+ cache: null,
+ create: null,
+ ModelType: null
+ };
+ /**
+ * @private
+ * @type Object
+ */
+ prototype.options = null;
+ /**
+ * Type we're caching (presumably extending `Backbone.Model`), used to create new
+ * instances unless a `create` function was provided in options.
+ * @private
+ * @type Class<Backbone.Model>
+ */
+ prototype.ModelType = null;
+ /**
+ * Collection holding the cached Models.
+ * @private
+ * @type Backbone.Collection
+ */
+ prototype.cache = null;
+ /**
+ * @constructor
+ * @param {Class<Backbone.Model>} [ModelType] Type of cached object (presumably extending
+ * `Backbone.Model`), used to create new instances unless `options.create`
+ * is provided.
+ * @param {Object} [options] Options:
+ * @param {Boolean} [options.ready=true] Starting `ready` state. If false,
+ * the cache will queue lookup calls until `triggerReady()` is called.
+ * @param {Class<Backbone.Collection>} [options.cache=new Backbone.Collection]
+ * The backing data-structure for the cache. If omitted, we'll use a new
+ * `Backbone.Collection`, but really, anything with a `get(id)` method for
+ * model lookup will work here.
+ * @param {Function} [options.create] A function called when a new Model
+ * object is needed, being passed the new model ID.
+ * @param {Class<Backbone.Model>} [options.ModelType] Type of cached object
+ * (presumably extending `Backbone.Model`), used to create new instances
+ * unless `options.create` is provided.
+ */;
+ function ModelCache(ModelType, options){
+ var that, _ref;
+ if (!_.isFunction(ModelType)) {
+ _ref = [ModelType || {}, null], options = _ref[0], ModelType = _ref[1];
+ }
+ this.options = (_ref = {}, __import(_ref, this.DEFAULT_OPTIONS), __import(_ref, options));
+ this.cache = this.options.cache || new Backbone.Collection;
+ this.ModelType = ModelType || this.options.ModelType;
+ if (that = this.options.create) {
+ this.createModel = that;
+ }
+ this.ready = !!this.options.ready;
+ if (this.ModelType) {
+ this.decorate(this.ModelType);
+ }
+ }
+ /**
+ * Called when a new Model object is needed, being passed the new model ID.
+ * Uses the supplied `ModelType`; overriden by `options.create` if provided.
+ *
+ * @param {String} id The model ID to create.
+ * @returns {Model} Created model.
+ */
+ prototype.createModel = function(id){
+ return new this.ModelType({
+ id: id
+ });
+ };
+ /**
+ * Registers a model with the cache. If a model by this ID already exists
+ * in the cache, it will be removed and this one will take its place.
+ *
+ * Fires an `add` event.
+ *
+ * @param {Model} model The model.
+ * @returns {Model} The model.
+ */
+ prototype.register = function(model){
+ if (this.cache.contains(model)) {
+ this.cache.remove(model, {
+ silent: true
+ });
+ }
+ this.cache.add(model);
+ this.trigger('add', this, model);
+ return model;
+ };
+ /**
+ * Synchronously check if a model is in the cache, returning it if so.
+ *
+ * @param {String} id The model ID to get.
+ * @returns {Model}
+ */
+ prototype.get = function(id){
+ return this.cache.get(id);
+ };
+ /**
+ * Asynchronously look up any number of models, requesting them from the
+ * server if not already known to the cache.
+ *
+ * @param {String|Array<String>} ids List of model IDs to lookup.
+ * @param {Function} cb Callback of the form `(err, models)`,
+ * where `err` will be null on success and `models` will be an Array
+ * of model objects.
+ * @param {Object} [cxt=this] Callback context.
+ * @returns {this}
+ */
+ prototype.lookupAll = function(ids, cb, cxt){
+ var _this = this;
+ cxt == null && (cxt = this);
+ if (!_.isArray(ids)) {
+ ids = [ids];
+ }
+ if (!this.ready) {
+ this.on('cache-ready', function(){
+ _this.off('cache-ready', arguments.callee);
+ return _this.lookupAll(ids, cb, cxt);
+ });
+ return this;
+ }
+ Seq(ids).parMap_(function(next, id){
+ var that;
+ if (that = _this.cache.get(id)) {
+ return next.ok(that);
+ }
+ return _this.register(_this.createModel(id)).on('ready', function(it){
+ return next.ok(it);
+ }).load();
+ }).unflatten().seq(function(models){
+ return cb.call(cxt, null, models);
+ })['catch'](function(err){
+ return cb.call(cxt, err);
+ });
+ return this;
+ };
+ /**
+ * Looks up a model, requesting it from the server if it is not already
+ * known to the cache.
+ *
+ * @param {String|Array<String>} id Model ID to lookup.
+ * @param {Function} cb Callback of the form `(err, model)`,
+ * where `err` will be null on success and `model` will be the
+ * model object.
+ * @param {Object} [cxt=this] Callback context.
+ * @returns {this}
+ */
+ prototype.lookup = function(id, cb, cxt){
+ cxt == null && (cxt = this);
+ return this.lookupAll([id], function(err, models){
+ if (err) {
+ return cb.call(cxt, err);
+ } else {
+ return cb.call(cxt, null, models[0]);
+ }
+ });
+ };
+ /**
+ * Decorate an object with the cache methods:
+ * - register
+ * - get
+ * - lookup
+ * - lookupAll
+ *
+ * This is automatically called on `ModelType` if supplied.
+ *
+ * @param {Object} obj Object to decorate.
+ * @returns {obj} The supplied object.
+ */
+ prototype.decorate = function(obj){
+ var m, _i, _ref, _len;
+ obj.__cache__ = this;
+ for (_i = 0, _len = (_ref = ['register', 'get', 'lookup', 'lookupAll']).length; _i < _len; ++_i) {
+ m = _ref[_i];
+ obj[m] = this[m].bind(this);
+ }
+ return obj;
+ };
+ prototype.toString = function(){
+ return (this.constructor.displayName || this.constructor.name) + "(cache=" + this.cache + ")";
+ };
+ return ModelCache;
+}(ReadyEmitter));
+function __extend(sub, sup){
+ function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+ (sub.prototype = new fun).constructor = sub;
+ if (typeof sup.extended == 'function') sup.extended(sub);
+ return sub;
+}
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
--- /dev/null
+var models, views;
+models = require('kraken/base/scaffold/scaffold-model');
+views = require('kraken/base/scaffold/scaffold-view');
+__import(__import(exports, models), views);
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/scaffold/index.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var models, views;
+models = require('kraken/base/scaffold/scaffold-model');
+views = require('kraken/base/scaffold/scaffold-view');
+__import(__import(exports, models), views);
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
--- /dev/null
+var op, BaseModel, BaseList, Field, FieldList, _, _ref, __slice = [].slice;
+_ = require('kraken/util/underscore');
+op = require('kraken/util/op');
+_ref = require('kraken/base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
+Field = exports.Field = BaseModel.extend({
+ valueAttribute: 'value',
+ defaults: function(){
+ return {
+ name: '',
+ type: 'String',
+ 'default': null,
+ desc: '',
+ include: 'diff',
+ tags: [],
+ examples: []
+ };
+ },
+ constructor: (function(){
+ function Field(){
+ return BaseModel.apply(this, arguments);
+ }
+ return Field;
+ }()),
+ initialize: function(){
+ _.bindAll.apply(_, [this].concat(__slice.call(_.functions(this).filter(function(it){
+ return _.startsWith(it, 'parse');
+ }))));
+ this.set('id', this.id = _.camelize(this.get('name')));
+ if (!this.has('value')) {
+ this.set('value', this.get('default'), {
+ silent: true
+ });
+ }
+ return Field.__super__.initialize.apply(this, arguments);
+ }
+ /* * * Value Accessors * * */,
+ getValue: function(def){
+ return this.getParser()(this.get(this.valueAttribute, def));
+ },
+ setValue: function(v, options){
+ var def, val;
+ def = this.get('default');
+ if (!v && def == null) {
+ val = null;
+ } else {
+ val = this.getParser()(v);
+ }
+ return this.set(this.valueAttribute, val, options);
+ },
+ clearValue: function(){
+ return this.set(this.valueAttribute, this.get('default'));
+ },
+ isDefault: function(){
+ return this.get(this.valueAttribute) === this.get('default');
+ }
+ /* * * Serializers * * */,
+ serializeValue: function(){
+ return this.serialize(this.getValue());
+ },
+ toJSON: function(){
+ var _ref;
+ return __import({
+ id: this.id
+ }, (_ref = _.clone(this.attributes), _ref.value = this.getValue(), _ref.def = this.get('default'), _ref));
+ },
+ toKVPairs: function(){
+ var _ref;
+ return _ref = {}, _ref[this.id + ""] = this.serializeValue(), _ref;
+ },
+ toString: function(){
+ return "(" + this.id + ": " + this.serializeValue() + ")";
+ }
+});
+FieldList = exports.FieldList = BaseList.extend({
+ model: Field,
+ constructor: (function(){
+ function FieldList(){
+ return BaseList.apply(this, arguments);
+ }
+ return FieldList;
+ }())
+ /**
+ * Collects a map of fields to their values, excluding those set to `null` or their default.
+ * @returns {Object}
+ */,
+ values: function(opts){
+ opts == null && (opts = {});
+ opts = __import({
+ keepDefaults: true,
+ serialize: false
+ }, opts);
+ return _.synthesize(opts.keepDefaults
+ ? this.models
+ : this.models.filter(function(it){
+ return !it.isDefault();
+ }), function(it){
+ return [
+ it.get('name'), opts.serialize
+ ? it.serializeValue()
+ : it.getValue()
+ ];
+ });
+ },
+ toJSON: function(){
+ return this.values({
+ keepDefaults: true,
+ serialize: false
+ });
+ },
+ toKVPairs: function(){
+ return _.collapseObject(this.values({
+ keepDefaults: true,
+ serialize: true
+ }));
+ },
+ toKV: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return _.toKV(this.toKVPairs(), item_delim, kv_delim);
+ },
+ toURL: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return "?" + this.toKV.apply(this, arguments);
+ }
+});
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/scaffold/scaffold-model.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var op, BaseModel, BaseList, Field, FieldList, _, _ref, __slice = [].slice;
+_ = require('kraken/util/underscore');
+op = require('kraken/util/op');
+_ref = require('kraken/base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
+Field = exports.Field = BaseModel.extend({
+ valueAttribute: 'value',
+ defaults: function(){
+ return {
+ name: '',
+ type: 'String',
+ 'default': null,
+ desc: '',
+ include: 'diff',
+ tags: [],
+ examples: []
+ };
+ },
+ constructor: (function(){
+ function Field(){
+ return BaseModel.apply(this, arguments);
+ }
+ return Field;
+ }()),
+ initialize: function(){
+ _.bindAll.apply(_, [this].concat(__slice.call(_.functions(this).filter(function(it){
+ return _.startsWith(it, 'parse');
+ }))));
+ this.set('id', this.id = _.camelize(this.get('name')));
+ if (!this.has('value')) {
+ this.set('value', this.get('default'), {
+ silent: true
+ });
+ }
+ return Field.__super__.initialize.apply(this, arguments);
+ }
+ /* * * Value Accessors * * */,
+ getValue: function(def){
+ return this.getParser()(this.get(this.valueAttribute, def));
+ },
+ setValue: function(v, options){
+ var def, val;
+ def = this.get('default');
+ if (!v && def == null) {
+ val = null;
+ } else {
+ val = this.getParser()(v);
+ }
+ return this.set(this.valueAttribute, val, options);
+ },
+ clearValue: function(){
+ return this.set(this.valueAttribute, this.get('default'));
+ },
+ isDefault: function(){
+ return this.get(this.valueAttribute) === this.get('default');
+ }
+ /* * * Serializers * * */,
+ serializeValue: function(){
+ return this.serialize(this.getValue());
+ },
+ toJSON: function(){
+ var _ref;
+ return __import({
+ id: this.id
+ }, (_ref = _.clone(this.attributes), _ref.value = this.getValue(), _ref.def = this.get('default'), _ref));
+ },
+ toKVPairs: function(){
+ var _ref;
+ return _ref = {}, _ref[this.id + ""] = this.serializeValue(), _ref;
+ },
+ toString: function(){
+ return "(" + this.id + ": " + this.serializeValue() + ")";
+ }
+});
+FieldList = exports.FieldList = BaseList.extend({
+ model: Field,
+ constructor: (function(){
+ function FieldList(){
+ return BaseList.apply(this, arguments);
+ }
+ return FieldList;
+ }())
+ /**
+ * Collects a map of fields to their values, excluding those set to `null` or their default.
+ * @returns {Object}
+ */,
+ values: function(opts){
+ opts == null && (opts = {});
+ opts = __import({
+ keepDefaults: true,
+ serialize: false
+ }, opts);
+ return _.synthesize(opts.keepDefaults
+ ? this.models
+ : this.models.filter(function(it){
+ return !it.isDefault();
+ }), function(it){
+ return [
+ it.get('name'), opts.serialize
+ ? it.serializeValue()
+ : it.getValue()
+ ];
+ });
+ },
+ toJSON: function(){
+ return this.values({
+ keepDefaults: true,
+ serialize: false
+ });
+ },
+ toKVPairs: function(){
+ return _.collapseObject(this.values({
+ keepDefaults: true,
+ serialize: true
+ }));
+ },
+ toKV: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return _.toKV(this.toKVPairs(), item_delim, kv_delim);
+ },
+ toURL: function(item_delim, kv_delim){
+ item_delim == null && (item_delim = '&');
+ kv_delim == null && (kv_delim = '=');
+ return "?" + this.toKV.apply(this, arguments);
+ }
+});
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
--- /dev/null
+var op, BaseView, Field, FieldList, FieldView, Scaffold, _, _ref;
+_ = require('kraken/util/underscore');
+op = require('kraken/util/op');
+BaseView = require('kraken/base').BaseView;
+_ref = require('kraken/base/scaffold/scaffold-model'), Field = _ref.Field, FieldList = _ref.FieldList;
+FieldView = exports.FieldView = BaseView.extend({
+ tagName: 'div',
+ className: 'field',
+ type: 'string',
+ events: {
+ 'blur .value': 'onChange',
+ 'submit .value': 'onChange'
+ },
+ constructor: (function(){
+ function FieldView(){
+ return BaseView.apply(this, arguments);
+ }
+ return FieldView;
+ }()),
+ initialize: function(){
+ BaseView.prototype.initialize.apply(this, arguments);
+ return this.type = this.model.get('type').toLowerCase() || 'string';
+ },
+ onChange: function(){
+ var val, current;
+ if (this.type === 'boolean') {
+ val = !!this.$('.value').attr('checked');
+ } else {
+ val = this.model.getParser()(this.$('.value').val());
+ }
+ current = this.model.getValue();
+ if (_.isEqual(val, current)) {
+ return;
+ }
+ this.model.setValue(val, {
+ silent: true
+ });
+ return this.trigger('change', this);
+ },
+ toTemplateLocals: function(){
+ var json, v;
+ json = FieldView.__super__.toTemplateLocals.apply(this, arguments);
+ json.id || (json.id = _.camelize(json.name));
+ json.value == null && (json.value = '');
+ if (v = json.value && (_.isArray(v) || _.isPlainObject(v))) {
+ json.value = JSON.stringify(v);
+ }
+ return json;
+ }
+ /**
+ * A ghetto default template, typically overridden by superclass.
+ */,
+ template: function(locals){
+ return $("<label class=\"name\" for=\"" + locals.id + "\">" + locals.name + "</label>\n<input class=\"value\" type=\"text\" id=\"" + locals.id + "\" name=\"" + locals.id + "\" value=\"" + locals.value + "\">");
+ },
+ render: function(){
+ if (this.model.get('ignore')) {
+ return this.remove();
+ }
+ return FieldView.__super__.render.apply(this, arguments);
+ }
+});
+Scaffold = exports.Scaffold = BaseView.extend({
+ __bind__: ['addField', 'resetFields'],
+ tagName: 'form',
+ className: 'scaffold',
+ collectionType: FieldList,
+ subviewType: FieldView,
+ constructor: (function(){
+ function Scaffold(){
+ return BaseView.apply(this, arguments);
+ }
+ return Scaffold;
+ }()),
+ initialize: function(){
+ var CollectionType;
+ CollectionType = this.collectionType;
+ this.model = this.collection || (this.collection = new CollectionType);
+ BaseView.prototype.initialize.apply(this, arguments);
+ this.collection.on('add', this.addField, this);
+ return this.collection.on('reset', this.resetFields, this);
+ },
+ addField: function(field){
+ var SubviewType, view;
+ if (field.view) {
+ this.removeSubview(field.view);
+ }
+ field.off('change:value', this.onChange, this);
+ field.on('change:value', this.onChange, this);
+ SubviewType = this.subviewType;
+ view = this.addSubview(new SubviewType({
+ model: field
+ }));
+ view.on('change', this.onChange.bind(this, field));
+ this.render();
+ return view;
+ },
+ resetFields: function(){
+ this.removeAllSubviews();
+ this.collection.each(this.addField);
+ return this;
+ },
+ onChange: function(field){
+ var key, value;
+ key = field.get('name');
+ value = field.getValue();
+ this.trigger("change:" + key, this, value, key, field);
+ this.trigger("change", this, value, key, field);
+ return this;
+ }
+});
+['get', 'at', 'pluck', 'invoke', 'values', 'toJSON', 'toKVPairs', 'toKV', 'toURL'].forEach(function(methodname){
+ return Scaffold.prototype[methodname] = function(){
+ return this.collection[methodname].apply(this.collection, arguments);
+ };
+});
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/base/scaffold/scaffold-view.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var op, BaseView, Field, FieldList, FieldView, Scaffold, _, _ref;
+_ = require('kraken/util/underscore');
+op = require('kraken/util/op');
+BaseView = require('kraken/base').BaseView;
+_ref = require('kraken/base/scaffold/scaffold-model'), Field = _ref.Field, FieldList = _ref.FieldList;
+FieldView = exports.FieldView = BaseView.extend({
+ tagName: 'div',
+ className: 'field',
+ type: 'string',
+ events: {
+ 'blur .value': 'onChange',
+ 'submit .value': 'onChange'
+ },
+ constructor: (function(){
+ function FieldView(){
+ return BaseView.apply(this, arguments);
+ }
+ return FieldView;
+ }()),
+ initialize: function(){
+ BaseView.prototype.initialize.apply(this, arguments);
+ return this.type = this.model.get('type').toLowerCase() || 'string';
+ },
+ onChange: function(){
+ var val, current;
+ if (this.type === 'boolean') {
+ val = !!this.$('.value').attr('checked');
+ } else {
+ val = this.model.getParser()(this.$('.value').val());
+ }
+ current = this.model.getValue();
+ if (_.isEqual(val, current)) {
+ return;
+ }
+ this.model.setValue(val, {
+ silent: true
+ });
+ return this.trigger('change', this);
+ },
+ toTemplateLocals: function(){
+ var json, v;
+ json = FieldView.__super__.toTemplateLocals.apply(this, arguments);
+ json.id || (json.id = _.camelize(json.name));
+ json.value == null && (json.value = '');
+ if (v = json.value && (_.isArray(v) || _.isPlainObject(v))) {
+ json.value = JSON.stringify(v);
+ }
+ return json;
+ }
+ /**
+ * A ghetto default template, typically overridden by superclass.
+ */,
+ template: function(locals){
+ return $("<label class=\"name\" for=\"" + locals.id + "\">" + locals.name + "</label>\n<input class=\"value\" type=\"text\" id=\"" + locals.id + "\" name=\"" + locals.id + "\" value=\"" + locals.value + "\">");
+ },
+ render: function(){
+ if (this.model.get('ignore')) {
+ return this.remove();
+ }
+ return FieldView.__super__.render.apply(this, arguments);
+ }
+});
+Scaffold = exports.Scaffold = BaseView.extend({
+ __bind__: ['addField', 'resetFields'],
+ tagName: 'form',
+ className: 'scaffold',
+ collectionType: FieldList,
+ subviewType: FieldView,
+ constructor: (function(){
+ function Scaffold(){
+ return BaseView.apply(this, arguments);
+ }
+ return Scaffold;
+ }()),
+ initialize: function(){
+ var CollectionType;
+ CollectionType = this.collectionType;
+ this.model = this.collection || (this.collection = new CollectionType);
+ BaseView.prototype.initialize.apply(this, arguments);
+ this.collection.on('add', this.addField, this);
+ return this.collection.on('reset', this.resetFields, this);
+ },
+ addField: function(field){
+ var SubviewType, view;
+ if (field.view) {
+ this.removeSubview(field.view);
+ }
+ field.off('change:value', this.onChange, this);
+ field.on('change:value', this.onChange, this);
+ SubviewType = this.subviewType;
+ view = this.addSubview(new SubviewType({
+ model: field
+ }));
+ view.on('change', this.onChange.bind(this, field));
+ this.render();
+ return view;
+ },
+ resetFields: function(){
+ this.removeAllSubviews();
+ this.collection.each(this.addField);
+ return this;
+ },
+ onChange: function(field){
+ var key, value;
+ key = field.get('name');
+ value = field.getValue();
+ this.trigger("change:" + key, this, value, key, field);
+ this.trigger("change", this, value, key, field);
+ return this;
+ }
+});
+['get', 'at', 'pluck', 'invoke', 'values', 'toJSON', 'toKVPairs', 'toKV', 'toURL'].forEach(function(methodname){
+ return Scaffold.prototype[methodname] = function(){
+ return this.collection[methodname].apply(this.collection, arguments);
+ };
+});
+
+});
--- /dev/null
+var moment, Backbone, op, ReadyEmitter, Parsers, ParserMixin, KNOWN_CHART_TYPES, ChartType, _ref, _, __slice = [].slice;
+moment = require('moment');
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+ReadyEmitter = require('kraken/util/event').ReadyEmitter;
+_ref = require('kraken/util/parser'), Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin;
+/**
+ * Map of known libraries by name.
+ * @type Object
+ */
+KNOWN_CHART_TYPES = exports.KNOWN_CHART_TYPES = {};
+/**
+ * @class Abstraction of a chart-type or charting library, encapsulating its
+ * logic and options. In addition, a `ChartType` also mediates the
+ * transformation of the domain-specific data types (the model and its view)
+ * with its specific needs.
+ *
+ * `ChartType`s mix in `ParserMixin`: when implementing a `ChartType`, you can
+ * add or supplement parsers merely by subclassing and overriding the
+ * corresponding `parseXXX` method (such as `parseArray` or `parseDate`).
+ *
+ * @extends EventEmitter
+ * @borrows ParserMixin
+ */
+exports.ChartType = ChartType = (function(superclass){
+ /**
+ * Register a new chart type.
+ */
+ ChartType.displayName = 'ChartType';
+ var prototype = __extend(ChartType, superclass).prototype, constructor = ChartType;
+ ChartType.register = function(Subclass){
+ return KNOWN_CHART_TYPES[Subclass.prototype.typeName] = Subclass;
+ };
+ /**
+ * Look up a `ChartType` by `typeName`.
+ */
+ ChartType.lookup = function(name){
+ if (name instanceof Backbone.Model) {
+ name = name.get('chartType');
+ }
+ return KNOWN_CHART_TYPES[name];
+ };
+ /**
+ * Look up a chart type by name, returning a new instance
+ * with the given model (and, optionally, view).
+ * @returns {ChartType}
+ */
+ ChartType.create = function(model, view){
+ var Type;
+ if (!(Type = this.lookup(model))) {
+ return null;
+ }
+ return new Type(model, view);
+ };
+ /*
+ * These are "class properties": each is set on the prototype at the class-level,
+ * and the reference is therefore shared by all instances. It is expected you
+ * will not modify this on the instance-level.
+ */
+ /**
+ * URL for the Chart Spec JSON. Loaded once, the first time an instance of
+ * that class is created.
+ * @type String
+ * @readonly
+ */
+ prototype.SPEC_URL = null;
+ /**
+ * Chart-type name.
+ * @type String
+ * @readonly
+ */
+ prototype.typeName = null;
+ /**
+ * Map of option name to ChartOption objects.
+ * @type { name:ChartOption, ... }
+ * @readonly
+ */
+ prototype.options = null;
+ /**
+ * Ordered ChartOption objects.
+ *
+ * This is a "class-property": it is set on the prototype at the class-level,
+ * and the reference is shared by all instances. It is expected you will not
+ * modify that instance.
+ *
+ * @type ChartOption[]
+ * @readonly
+ */
+ prototype.options_ordered = null;
+ /**
+ * Hash of role-names to the selector which, when applied to the view,
+ * returns the correct element.
+ * @type Object
+ */
+ prototype.roles = {
+ viewport: '.viewport'
+ };
+ /**
+ * Whether the ChartType has loaded all its data and is ready.
+ * @type Boolean
+ */
+ prototype.ready = false;
+ /**
+ * Model to be rendered as a chart.
+ * @type Backbone.Model
+ */
+ prototype.model = null;
+ /**
+ * View to render the chart into.
+ * @type Backbone.View
+ */
+ prototype.view = null;
+ /**
+ * Last chart rendered by this ChartType.
+ * @private
+ */
+ prototype.chart = null;
+ /**
+ * @constructor
+ */;
+ function ChartType(model, view){
+ this.model = model;
+ this.view = view;
+ this.roles || (this.roles = {});
+ _.bindAll.apply(_, [this].concat(__slice.call(this.__bind__)));
+ if (!this.ready) {
+ this.loadSpec();
+ }
+ }
+ prototype.withModel = function(model){
+ this.model = model;
+ return this;
+ };
+ prototype.withView = function(view){
+ this.view = view;
+ return this;
+ };
+ /**
+ * Load the corresponding chart specification, which includes
+ * info about valid options, along with their types and defaults.
+ */
+ prototype.loadSpec = function(){
+ var proto, _this = this;
+ if (this.ready) {
+ return this;
+ }
+ proto = this.constructor.prototype;
+ jQuery.ajax({
+ url: this.SPEC_URL,
+ dataType: 'json',
+ success: function(spec){
+ proto.spec = spec;
+ proto.options_ordered = spec;
+ proto.options = _.synthesize(spec, function(it){
+ return [it.name, it];
+ });
+ proto.ready = true;
+ return _this.triggerReady();
+ },
+ error: function(it){
+ return console.error("Error loading " + _this.typeName + " spec! " + it);
+ }
+ });
+ return this;
+ };
+ /**
+ * @returns {ChartOption} Get an option's spec by name.
+ */
+ prototype.getOption = function(name, def){
+ return this.options[name] || def;
+ };
+ /**
+ * @returns {Object} An object, mapping from option.name to the
+ * result of the supplied function.
+ */
+ prototype.map = function(fn, context){
+ var _this = this;
+ context == null && (context = this);
+ return _.synthesize(this.options, function(it){
+ return [it.name, fn.call(context, it, it.name, _this)];
+ });
+ };
+ /**
+ * @param {String} attr Attribute to look up on each options object.
+ * @returns {Object} Map from name to the value found at the given attr.
+ */
+ prototype.pluck = function(attr){
+ return this.map(function(it){
+ return it[attr];
+ });
+ };
+ /**
+ * @returns {Boolean} Whether the supplied value is the same as
+ * the default value for the given key.
+ */
+ prototype.isDefault = function(name, value){
+ return _.isEqual(this.getOption(name)['default'], value);
+ };
+ /**
+ * When implementing a ChartType, you can add or override parsers
+ * merely by subclassing.
+ * @borrows ParserMixin
+ */;
+ ParserMixin.mix(ChartType);
+ /**
+ * @returns {Function} Parser for the given option name.
+ */
+ prototype.getParserFor = function(name){
+ return this.getParser(this.getOption(name).type);
+ };
+ /**
+ * Parses a single serialized option value into its proper type.
+ *
+ * @param {String} name Option-name of the value being parsed.
+ * @param {String} value Value to parse.
+ * @returns {*} Parsed value.
+ */
+ prototype.parseOption = function(name, value){
+ return this.getParserFor(name)(value);
+ };
+ /**
+ * Parses options using `parseOption(name, value)`.
+ *
+ * @param {Object} options Options to parse.
+ * @returns {Object} Parsed options.
+ */
+ prototype.parseOptions = function(options){
+ var out, k, v;
+ out = {};
+ for (k in options) {
+ v = options[k];
+ out[k] = this.parseOption(k, v);
+ }
+ return out;
+ };
+ /**
+ * Serializes option-value to a String.
+ *
+ * @param {*} v Value to serialize.
+ * @param {String} k Option-name of the given value.
+ * @returns {String} The serialized value
+ */
+ prototype.serialize = function(v, k){
+ if (_.isBoolean(v)) {
+ v = Number(v);
+ } else if (_.isObject(v)) {
+ v = JSON.stringify(v);
+ }
+ return String(v);
+ };
+ /**
+ * Formats a date for display on an axis: `MM/YYYY`
+ * @param {Date} d Date to format.
+ * @returns {String}
+ */
+ prototype.axisDateFormatter = function(d){
+ return moment(d).format('MM/YYYY');
+ };
+ /**
+ * Formats a date for display in the legend: `DD MMM YYYY`
+ * @param {Date} d Date to format.
+ * @returns {String}
+ */
+ prototype.dateFormatter = function(d){
+ return moment(d).format('DD MMM YYYY');
+ };
+ /**
+ * Formats a number for display, first dividing by the greatest suffix
+ * of {B = Billions, M = Millions, K = Thousands} that results in a
+ * absolute value greater than 0, and then rounding to `digits` using
+ * `result.toFixed(digits)`.
+ *
+ * @param {Number} n Number to format.
+ * @param {Number} [digits=2] Number of digits after the decimal to always display.
+ * @param {Boolean} [abbrev=true] Expand number suffixes if false.
+ * @returns {Object} Formatted number parts.
+ */
+ prototype.numberFormatter = function(n, digits, abbrev){
+ var suffixes, suffix, d, s, parts, whole, fraction, _i, _len, _ref;
+ digits == null && (digits = 2);
+ abbrev == null && (abbrev = true);
+ suffixes = abbrev
+ ? [['B', 1000000000], ['M', 1000000], ['K', 1000], ['', NaN]]
+ : [['Billion', 1000000000], ['Million', 1000000], ['', NaN]];
+ for (_i = 0, _len = suffixes.length; _i < _len; ++_i) {
+ _ref = suffixes[_i], suffix = _ref[0], d = _ref[1];
+ if (isNaN(d)) {
+ break;
+ }
+ if (n >= d) {
+ n = n / d;
+ break;
+ }
+ }
+ s = n.toFixed(digits);
+ parts = s.split('.');
+ whole = _.rchop(parts[0], 3).join(',');
+ fraction = '.' + parts.slice(1).join('.');
+ return {
+ n: n,
+ digits: digits,
+ whole: whole,
+ fraction: fraction,
+ suffix: suffix,
+ toString: function(){
+ return this.whole + "" + this.fraction + (abbrev ? '' : ' ') + this.suffix;
+ }
+ };
+ };
+ /**
+ * Finds the element in the view which plays the given role in the chart.
+ * Canonically, all charts have a "viewport" element. Other roles might
+ * include a "legend" element, or several "axis" elements.
+ *
+ * Default implementation looks up a selector in the `roles` hash, and if
+ * found, queries the view for matching children.
+ *
+ * @param {String} role Name of the role to look up.
+ * @returns {jQuery|null} $-wrapped DOM element.
+ */
+ prototype.getElementsForRole = function(role){
+ var that;
+ if (!this.view) {
+ return null;
+ }
+ if (that = this.roles[role]) {
+ return this.view.$(that);
+ } else {
+ return null;
+ }
+ };
+ /**
+ * Transform/extract the data for this chart from the model. Default
+ * implementation calls `model.getData()`.
+ *
+ * @returns {*} Data object for the chart.
+ */
+ prototype.getData = function(){
+ return this.model.getData();
+ };
+ /**
+ * Map from option-name to default value. Note that this reference will be
+ * modified by `.render()`.
+ *
+ * @returns {Object} Default options.
+ */
+ prototype.getDefaultOptions = function(){
+ return this.pluck('default');
+ };
+ /**
+ * Resizes the HTML viewport. Override to disable, etc.
+ */
+ prototype.resizeViewport = function(){
+ var size;
+ size = this.determineSize();
+ this.getElementsForRole('viewport').css(size);
+ return size;
+ };
+ /**
+ * Determines chart viewport size.
+ * @return { width, height }
+ */
+ prototype.determineSize = function(){
+ var width, modelW, height, modelH, viewport, Width;
+ modelW = width = this.model.get('width');
+ modelH = height = this.model.get('height');
+ if (!(this.view.ready && width && height)) {
+ return {
+ width: width,
+ height: height
+ };
+ }
+ viewport = this.getElementsForRole('viewport');
+ if (width === 'auto') {
+ Width = viewport.innerWidth() || 300;
+ }
+ width == null && (width = modelW);
+ if (height === 'auto') {
+ height = viewport.innerHeight() || 320;
+ }
+ height == null && (height = modelH);
+ return {
+ width: width,
+ height: height
+ };
+ };
+ /**
+ * Transforms domain data and applies it to the chart library to
+ * render or update the corresponding chart.
+ *
+ * @returns {Chart}
+ */
+ prototype.render = function(){
+ var data, options, viewport;
+ data = this.getData();
+ options = __import(this.getDefaultOptions(), this.transform(this.model, this.view));
+ viewport = this.getElementsForRole('viewport');
+ if (!((data != null && data.length) && (viewport != null && viewport.length))) {
+ return this.lastChart;
+ }
+ return this.lastChart = this.renderChart(data, viewport, options, this.chart);
+ };
+ /**
+ * Transforms the domain objects into a hash of derived values using
+ * chart-type-specific keys.
+ *
+ * Default implementation returns `model.getOptions()`.
+ *
+ * @returns {Object} The derived data.
+ */
+ prototype.transform = function(model, view){
+ return this.model.getOptions();
+ };
+ /**
+ * Called to render the chart.
+ *
+ * @abstract
+ * @returns {Chart}
+ */
+ prototype.renderChart = function(data, viewport, options, lastChart){
+ throw Error('unimplemented');
+ };
+ return ChartType;
+}(ReadyEmitter));
+function __extend(sub, sup){
+ function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+ (sub.prototype = new fun).constructor = sub;
+ if (typeof sup.extended == 'function') sup.extended(sub);
+ return sub;
+}
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
\ No newline at end of file
--- /dev/null
+require.define('/node_modules/kraken/chart/chart-type.js.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var moment, Backbone, op, ReadyEmitter, Parsers, ParserMixin, KNOWN_CHART_TYPES, ChartType, _ref, _, __slice = [].slice;
+moment = require('moment');
+Backbone = require('backbone');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+ReadyEmitter = require('kraken/util/event').ReadyEmitter;
+_ref = require('kraken/util/parser'), Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin;
+/**
+ * Map of known libraries by name.
+ * @type Object
+ */
+KNOWN_CHART_TYPES = exports.KNOWN_CHART_TYPES = {};
+/**
+ * @class Abstraction of a chart-type or charting library, encapsulating its
+ * logic and options. In addition, a `ChartType` also mediates the
+ * transformation of the domain-specific data types (the model and its view)
+ * with its specific needs.
+ *
+ * `ChartType`s mix in `ParserMixin`: when implementing a `ChartType`, you can
+ * add or supplement parsers merely by subclassing and overriding the
+ * corresponding `parseXXX` method (such as `parseArray` or `parseDate`).
+ *
+ * @extends EventEmitter
+ * @borrows ParserMixin
+ */
+exports.ChartType = ChartType = (function(superclass){
+ /**
+ * Register a new chart type.
+ */
+ ChartType.displayName = 'ChartType';
+ var prototype = __extend(ChartType, superclass).prototype, constructor = ChartType;
+ ChartType.register = function(Subclass){
+ return KNOWN_CHART_TYPES[Subclass.prototype.typeName] = Subclass;
+ };
+ /**
+ * Look up a `ChartType` by `typeName`.
+ */
+ ChartType.lookup = function(name){
+ if (name instanceof Backbone.Model) {
+ name = name.get('chartType');
+ }
+ return KNOWN_CHART_TYPES[name];
+ };
+ /**
+ * Look up a chart type by name, returning a new instance
+ * with the given model (and, optionally, view).
+ * @returns {ChartType}
+ */
+ ChartType.create = function(model, view){
+ var Type;
+ if (!(Type = this.lookup(model))) {
+ return null;
+ }
+ return new Type(model, view);
+ };
+ /*
+ * These are "class properties": each is set on the prototype at the class-level,
+ * and the reference is therefore shared by all instances. It is expected you
+ * will not modify this on the instance-level.
+ */
+ /**
+ * URL for the Chart Spec JSON. Loaded once, the first time an instance of
+ * that class is created.
+ * @type String
+ * @readonly
+ */
+ prototype.SPEC_URL = null;
+ /**
+ * Chart-type name.
+ * @type String
+ * @readonly
+ */
+ prototype.typeName = null;
+ /**
+ * Map of option name to ChartOption objects.
+ * @type { name:ChartOption, ... }
+ * @readonly
+ */
+ prototype.options = null;
+ /**
+ * Ordered ChartOption objects.
+ *
+ * This is a "class-property": it is set on the prototype at the class-level,
+ * and the reference is shared by all instances. It is expected you will not
+ * modify that instance.
+ *
+ * @type ChartOption[]
+ * @readonly
+ */
+ prototype.options_ordered = null;
+ /**
+ * Hash of role-names to the selector which, when applied to the view,
+ * returns the correct element.
+ * @type Object
+ */
+ prototype.roles = {
+ viewport: '.viewport'
+ };
+ /**
+ * Whether the ChartType has loaded all its data and is ready.
+ * @type Boolean
+ */
+ prototype.ready = false;
+ /**
+ * Model to be rendered as a chart.
+ * @type Backbone.Model
+ */
+ prototype.model = null;
+ /**
+ * View to render the chart into.
+ * @type Backbone.View
+ */
+ prototype.view = null;
+ /**
+ * Last chart rendered by this ChartType.
+ * @private
+ */
+ prototype.chart = null;
+ /**
+ * @constructor
+ */;
+ function ChartType(model, view){
+ this.model = model;
+ this.view = view;
+ this.roles || (this.roles = {});
+ _.bindAll.apply(_, [this].concat(__slice.call(this.__bind__)));
+ if (!this.ready) {
+ this.loadSpec();
+ }
+ }
+ prototype.withModel = function(model){
+ this.model = model;
+ return this;
+ };
+ prototype.withView = function(view){
+ this.view = view;
+ return this;
+ };
+ /**
+ * Load the corresponding chart specification, which includes
+ * info about valid options, along with their types and defaults.
+ */
+ prototype.loadSpec = function(){
+ var proto, _this = this;
+ if (this.ready) {
+ return this;
+ }
+ proto = this.constructor.prototype;
+ jQuery.ajax({
+ url: this.SPEC_URL,
+ dataType: 'json',
+ success: function(spec){
+ proto.spec = spec;
+ proto.options_ordered = spec;
+ proto.options = _.synthesize(spec, function(it){
+ return [it.name, it];
+ });
+ proto.ready = true;
+ return _this.triggerReady();
+ },
+ error: function(it){
+ return console.error("Error loading " + _this.typeName + " spec! " + it);
+ }
+ });
+ return this;
+ };
+ /**
+ * @returns {ChartOption} Get an option's spec by name.
+ */
+ prototype.getOption = function(name, def){
+ return this.options[name] || def;
+ };
+ /**
+ * @returns {Object} An object, mapping from option.name to the
+ * result of the supplied function.
+ */
+ prototype.map = function(fn, context){
+ var _this = this;
+ context == null && (context = this);
+ return _.synthesize(this.options, function(it){
+ return [it.name, fn.call(context, it, it.name, _this)];
+ });
+ };
+ /**
+ * @param {String} attr Attribute to look up on each options object.
+ * @returns {Object} Map from name to the value found at the given attr.
+ */
+ prototype.pluck = function(attr){
+ return this.map(function(it){
+ return it[attr];
+ });
+ };
+ /**
+ * @returns {Boolean} Whether the supplied value is the same as
+ * the default value for the given key.
+ */
+ prototype.isDefault = function(name, value){
+ return _.isEqual(this.getOption(name)['default'], value);
+ };
+ /**
+ * When implementing a ChartType, you can add or override parsers
+ * merely by subclassing.
+ * @borrows ParserMixin
+ */;
+ ParserMixin.mix(ChartType);
+ /**
+ * @returns {Function} Parser for the given option name.
+ */
+ prototype.getParserFor = function(name){
+ return this.getParser(this.getOption(name).type);
+ };
+ /**
+ * Parses a single serialized option value into its proper type.
+ *
+ * @param {String} name Option-name of the value being parsed.
+ * @param {String} value Value to parse.
+ * @returns {*} Parsed value.
+ */
+ prototype.parseOption = function(name, value){
+ return this.getParserFor(name)(value);
+ };
+ /**
+ * Parses options using `parseOption(name, value)`.
+ *
+ * @param {Object} options Options to parse.
+ * @returns {Object} Parsed options.
+ */
+ prototype.parseOptions = function(options){
+ var out, k, v;
+ out = {};
+ for (k in options) {
+ v = options[k];
+ out[k] = this.parseOption(k, v);
+ }
+ return out;
+ };
+ /**
+ * Serializes option-value to a String.
+ *
+ * @param {*} v Value to serialize.
+ * @param {String} k Option-name of the given value.
+ * @returns {String} The serialized value
+ */
+ prototype.serialize = function(v, k){
+ if (_.isBoolean(v)) {
+ v = Number(v);
+ } else if (_.isObject(v)) {
+ v = JSON.stringify(v);
+ }
+ return String(v);
+ };
+ /**
+ * Formats a date for display on an axis: `MM/YYYY`
+ * @param {Date} d Date to format.
+ * @returns {String}
+ */
+ prototype.axisDateFormatter = function(d){
+ return moment(d).format('MM/YYYY');
+ };
+ /**
+ * Formats a date for display in the legend: `DD MMM YYYY`
+ * @param {Date} d Date to format.
+ * @returns {String}
+ */
+ prototype.dateFormatter = function(d){
+ return moment(d).format('DD MMM YYYY');
+ };
+ /**
+ * Formats a number for display, first dividing by the greatest suffix
+ * of {B = Billions, M = Millions, K = Thousands} that results in a
+ * absolute value greater than 0, and then rounding to `digits` using
+ * `result.toFixed(digits)`.
+ *
+ * @param {Number} n Number to format.
+ * @param {Number} [digits=2] Number of digits after the decimal to always display.
+ * @param {Boolean} [abbrev=true] Expand number suffixes if false.
+ * @returns {Object} Formatted number parts.
+ */
+ prototype.numberFormatter = function(n, digits, abbrev){
+ var suffixes, suffix, d, s, parts, whole, fraction, _i, _len, _ref;
+ digits == null && (digits = 2);
+ abbrev == null && (abbrev = true);
+ suffixes = abbrev
+ ? [['B', 1000000000], ['M', 1000000], ['K', 1000], ['', NaN]]
+ : [['Billion', 1000000000], ['Million', 1000000], ['', NaN]];
+ for (_i = 0, _len = suffixes.length; _i < _len; ++_i) {
+ _ref = suffixes[_i], suffix = _ref[0], d = _ref[1];
+ if (isNaN(d)) {
+ break;
+ }
+ if (n >= d) {
+ n = n / d;
+ break;
+ }
+ }
+ s = n.toFixed(digits);
+ parts = s.split('.');
+ whole = _.rchop(parts[0], 3).join(',');
+ fraction = '.' + parts.slice(1).join('.');
+ return {
+ n: n,
+ digits: digits,
+ whole: whole,
+ fraction: fraction,
+ suffix: suffix,
+ toString: function(){
+ return this.whole + "" + this.fraction + (abbrev ? '' : ' ') + this.suffix;
+ }
+ };
+ };
+ /**
+ * Finds the element in the view which plays the given role in the chart.
+ * Canonically, all charts have a "viewport" element. Other roles might
+ * include a "legend" element, or several "axis" elements.
+ *
+ * Default implementation looks up a selector in the `roles` hash, and if
+ * found, queries the view for matching children.
+ *
+ * @param {String} role Name of the role to look up.
+ * @returns {jQuery|null} $-wrapped DOM element.
+ */
+ prototype.getElementsForRole = function(role){
+ var that;
+ if (!this.view) {
+ return null;
+ }
+ if (that = this.roles[role]) {
+ return this.view.$(that);
+ } else {
+ return null;
+ }
+ };
+ /**
+ * Transform/extract the data for this chart from the model. Default
+ * implementation calls `model.getData()`.
+ *
+ * @returns {*} Data object for the chart.
+ */
+ prototype.getData = function(){
+ return this.model.getData();
+ };
+ /**
+ * Map from option-name to default value. Note that this reference will be
+ * modified by `.render()`.
+ *
+ * @returns {Object} Default options.
+ */
+ prototype.getDefaultOptions = function(){
+ return this.pluck('default');
+ };
+ /**
+ * Resizes the HTML viewport. Override to disable, etc.
+ */
+ prototype.resizeViewport = function(){
+ var size;
+ size = this.determineSize();
+ this.getElementsForRole('viewport').css(size);
+ return size;
+ };
+ /**
+ * Determines chart viewport size.
+ * @return { width, height }
+ */
+ prototype.determineSize = function(){
+ var width, modelW, height, modelH, viewport, Width;
+ modelW = width = this.model.get('width');
+ modelH = height = this.model.get('height');
+ if (!(this.view.ready && width && height)) {
+ return {
+ width: width,
+ height: height
+ };
+ }
+ viewport = this.getElementsForRole('viewport');
+ if (width === 'auto') {
+ Width = viewport.innerWidth() || 300;
+ }
+ width == null && (width = modelW);
+ if (height === 'auto') {
+ height = viewport.innerHeight() || 320;
+ }
+ height == null && (height = modelH);
+ return {
+ width: width,
+ height: height
+ };
+ };
+ /**
+ * Transforms domain data and applies it to the chart library to
+ * render or update the corresponding chart.
+ *
+ * @returns {Chart}
+ */
+ prototype.render = function(){
+ var data, options, viewport;
+ data = this.getData();
+ options = __import(this.getDefaultOptions(), this.transform(this.model, this.view));
+ viewport = this.getElementsForRole('viewport');
+ if (!((data != null && data.length) && (viewport != null && viewport.length))) {