Adds compiled JS source files to be published with the npm distribution.
authorDavid Schoonover <dsc@wikimedia.org>
Tue, 10 Jul 2012 13:19:39 +0000 (06:19 -0700)
committerDavid Schoonover <dsc@wikimedia.org>
Tue, 10 Jul 2012 13:19:39 +0000 (06:19 -0700)
175 files changed:
lib/app.js [new file with mode: 0644]
lib/app.mod.js [new file with mode: 0644]
lib/base/base-mixin.js [new file with mode: 0644]
lib/base/base-mixin.mod.js [new file with mode: 0644]
lib/base/base-model.js [new file with mode: 0644]
lib/base/base-model.mod.js [new file with mode: 0644]
lib/base/base-view.js [new file with mode: 0644]
lib/base/base-view.mod.js [new file with mode: 0644]
lib/base/base.js [new file with mode: 0644]
lib/base/base.mod.js [new file with mode: 0644]
lib/base/cascading-model.js [new file with mode: 0644]
lib/base/cascading-model.mod.js [new file with mode: 0644]
lib/base/data-binding.js [new file with mode: 0644]
lib/base/data-binding.mod.js [new file with mode: 0644]
lib/base/index.js [new file with mode: 0644]
lib/base/index.mod.js [new file with mode: 0644]
lib/base/model-cache.js [new file with mode: 0644]
lib/base/model-cache.mod.js [new file with mode: 0644]
lib/base/scaffold/index.js [new file with mode: 0644]
lib/base/scaffold/index.mod.js [new file with mode: 0644]
lib/base/scaffold/scaffold-model.js [new file with mode: 0644]
lib/base/scaffold/scaffold-model.mod.js [new file with mode: 0644]
lib/base/scaffold/scaffold-view.js [new file with mode: 0644]
lib/base/scaffold/scaffold-view.mod.js [new file with mode: 0644]
lib/chart/chart-type.js [new file with mode: 0644]
lib/chart/chart-type.mod.js [new file with mode: 0644]
lib/chart/index.js [new file with mode: 0644]
lib/chart/index.mod.js [new file with mode: 0644]
lib/chart/option/chart-option-model.js [new file with mode: 0644]
lib/chart/option/chart-option-model.mod.js [new file with mode: 0644]
lib/chart/option/chart-option-view.js [new file with mode: 0644]
lib/chart/option/chart-option-view.mod.js [new file with mode: 0644]
lib/chart/option/index.js [new file with mode: 0644]
lib/chart/option/index.mod.js [new file with mode: 0644]
lib/chart/type/d3-chart.js [new file with mode: 0644]
lib/chart/type/d3-chart.mod.js [new file with mode: 0644]
lib/chart/type/d3/d3-bar-element.js [new file with mode: 0644]
lib/chart/type/d3/d3-bar-element.mod.js [new file with mode: 0644]
lib/chart/type/d3/d3-chart-element.js [new file with mode: 0644]
lib/chart/type/d3/d3-chart-element.mod.js [new file with mode: 0644]
lib/chart/type/d3/d3-line-element.js [new file with mode: 0644]
lib/chart/type/d3/d3-line-element.mod.js [new file with mode: 0644]
lib/chart/type/d3/index.js [new file with mode: 0644]
lib/chart/type/d3/index.mod.js [new file with mode: 0644]
lib/chart/type/dygraphs.js [new file with mode: 0644]
lib/chart/type/dygraphs.mod.js [new file with mode: 0644]
lib/chart/type/index.js [new file with mode: 0644]
lib/chart/type/index.mod.js [new file with mode: 0644]
lib/dashboard/dashboard-model.js [new file with mode: 0644]
lib/dashboard/dashboard-model.mod.js [new file with mode: 0644]
lib/dashboard/dashboard-view.js [new file with mode: 0644]
lib/dashboard/dashboard-view.mod.js [new file with mode: 0644]
lib/dashboard/index.js [new file with mode: 0644]
lib/dashboard/index.mod.js [new file with mode: 0644]
lib/data/data-view.js [new file with mode: 0644]
lib/data/data-view.mod.js [new file with mode: 0644]
lib/data/dataset-model.js [new file with mode: 0644]
lib/data/dataset-model.mod.js [new file with mode: 0644]
lib/data/dataset-view.js [new file with mode: 0644]
lib/data/dataset-view.mod.js [new file with mode: 0644]
lib/data/datasource-model.js [new file with mode: 0644]
lib/data/datasource-model.mod.js [new file with mode: 0644]
lib/data/datasource-ui-view.js [new file with mode: 0644]
lib/data/datasource-ui-view.mod.js [new file with mode: 0644]
lib/data/datasource-view.js [new file with mode: 0644]
lib/data/datasource-view.mod.js [new file with mode: 0644]
lib/data/index.js [new file with mode: 0644]
lib/data/index.mod.js [new file with mode: 0644]
lib/data/metric-edit-view.js [new file with mode: 0644]
lib/data/metric-edit-view.mod.js [new file with mode: 0644]
lib/data/metric-model.js [new file with mode: 0644]
lib/data/metric-model.mod.js [new file with mode: 0644]
lib/data/project-colors.js [new file with mode: 0644]
lib/data/project-colors.mod.js [new file with mode: 0644]
lib/graph/graph-display-view.js [new file with mode: 0644]
lib/graph/graph-display-view.mod.js [new file with mode: 0644]
lib/graph/graph-edit-view.js [new file with mode: 0644]
lib/graph/graph-edit-view.mod.js [new file with mode: 0644]
lib/graph/graph-list-view.js [new file with mode: 0644]
lib/graph/graph-list-view.mod.js [new file with mode: 0644]
lib/graph/graph-model.js [new file with mode: 0644]
lib/graph/graph-model.mod.js [new file with mode: 0644]
lib/graph/graph-view.js [new file with mode: 0644]
lib/graph/graph-view.mod.js [new file with mode: 0644]
lib/graph/index.js [new file with mode: 0644]
lib/graph/index.mod.js [new file with mode: 0644]
lib/main-dashboard.js [new file with mode: 0644]
lib/main-display.js [new file with mode: 0644]
lib/main-edit.js [new file with mode: 0644]
lib/main-geo.js [new file with mode: 0644]
lib/main-graph-list.js [new file with mode: 0644]
lib/template/browser-helpers.jade.js [new file with mode: 0644]
lib/template/chart/chart-option.jade.js [new file with mode: 0644]
lib/template/chart/chart-option.jade.mod.js [new file with mode: 0644]
lib/template/chart/chart-scaffold.jade.js [new file with mode: 0644]
lib/template/chart/chart-scaffold.jade.mod.js [new file with mode: 0644]
lib/template/dashboard/dashboard-tab.jade.js [new file with mode: 0644]
lib/template/dashboard/dashboard-tab.jade.mod.js [new file with mode: 0644]
lib/template/dashboard/dashboard.jade.js [new file with mode: 0644]
lib/template/dashboard/dashboard.jade.mod.js [new file with mode: 0644]
lib/template/data/data.jade.js [new file with mode: 0644]
lib/template/data/data.jade.mod.js [new file with mode: 0644]
lib/template/data/dataset-metric.jade.js [new file with mode: 0644]
lib/template/data/dataset-metric.jade.mod.js [new file with mode: 0644]
lib/template/data/dataset.jade.js [new file with mode: 0644]
lib/template/data/dataset.jade.mod.js [new file with mode: 0644]
lib/template/data/datasource-ui.jade.js [new file with mode: 0644]
lib/template/data/datasource-ui.jade.mod.js [new file with mode: 0644]
lib/template/data/datasource.jade.js [new file with mode: 0644]
lib/template/data/datasource.jade.mod.js [new file with mode: 0644]
lib/template/data/metric-edit.jade.js [new file with mode: 0644]
lib/template/data/metric-edit.jade.mod.js [new file with mode: 0644]
lib/template/graph/graph-display.jade.js [new file with mode: 0644]
lib/template/graph/graph-display.jade.mod.js [new file with mode: 0644]
lib/template/graph/graph-edit.jade.js [new file with mode: 0644]
lib/template/graph/graph-edit.jade.mod.js [new file with mode: 0644]
lib/template/graph/graph-list.jade.js [new file with mode: 0644]
lib/template/graph/graph-list.jade.mod.js [new file with mode: 0644]
lib/util/backbone.js [new file with mode: 0644]
lib/util/backbone.mod.js [new file with mode: 0644]
lib/util/cascade.js [new file with mode: 0644]
lib/util/cascade.mod.js [new file with mode: 0644]
lib/util/event/index.js [new file with mode: 0644]
lib/util/event/index.mod.js [new file with mode: 0644]
lib/util/event/ready-emitter.js [new file with mode: 0644]
lib/util/event/ready-emitter.mod.js [new file with mode: 0644]
lib/util/event/waiting-emitter.js [new file with mode: 0644]
lib/util/event/waiting-emitter.mod.js [new file with mode: 0644]
lib/util/formatters.js [new file with mode: 0644]
lib/util/formatters.mod.js [new file with mode: 0644]
lib/util/index.js [new file with mode: 0644]
lib/util/index.mod.js [new file with mode: 0644]
lib/util/op.js [new file with mode: 0644]
lib/util/op.mod.js [new file with mode: 0644]
lib/util/parser.js [new file with mode: 0644]
lib/util/parser.mod.js [new file with mode: 0644]
lib/util/timeseries/csv.js [new file with mode: 0644]
lib/util/timeseries/csv.mod.js [new file with mode: 0644]
lib/util/timeseries/index.js [new file with mode: 0644]
lib/util/timeseries/index.mod.js [new file with mode: 0644]
lib/util/timeseries/timeseries.js [new file with mode: 0644]
lib/util/timeseries/timeseries.mod.js [new file with mode: 0644]
lib/util/underscore/array.js [new file with mode: 0644]
lib/util/underscore/array.mod.js [new file with mode: 0644]
lib/util/underscore/class.js [new file with mode: 0644]
lib/util/underscore/class.mod.js [new file with mode: 0644]
lib/util/underscore/function.js [new file with mode: 0644]
lib/util/underscore/function.mod.js [new file with mode: 0644]
lib/util/underscore/index.js [new file with mode: 0644]
lib/util/underscore/index.mod.js [new file with mode: 0644]
lib/util/underscore/kv.js [new file with mode: 0644]
lib/util/underscore/kv.mod.js [new file with mode: 0644]
lib/util/underscore/object.js [new file with mode: 0644]
lib/util/underscore/object.mod.js [new file with mode: 0644]
lib/util/underscore/string.js [new file with mode: 0644]
lib/util/underscore/string.mod.js [new file with mode: 0644]
www/css/bootstrap-variables.css [new file with mode: 0644]
www/css/chart.css [new file with mode: 0644]
www/css/colors.css [new file with mode: 0644]
www/css/dashboard.css [new file with mode: 0644]
www/css/data.css [new file with mode: 0644]
www/css/docs.css [new file with mode: 0644]
www/css/geo-display.css [new file with mode: 0644]
www/css/graph-display-print.css [new file with mode: 0644]
www/css/graph-display.css [new file with mode: 0644]
www/css/graph.css [new file with mode: 0644]
www/css/hicons.css [new file with mode: 0644]
www/css/layout.css [new file with mode: 0644]
www/css/mixins.css [new file with mode: 0644]
www/css/text.css [new file with mode: 0644]
www/schema/d3/d3-bar.json [new file with mode: 0644]
www/schema/d3/d3-chart.json [new file with mode: 0644]
www/schema/d3/d3-geo-world.json [new file with mode: 0644]
www/schema/d3/d3-line.json [new file with mode: 0644]
www/schema/dygraph.json [new file with mode: 0644]

diff --git a/lib/app.js b/lib/app.js
new file mode 100644 (file)
index 0000000..b4937f3
--- /dev/null
@@ -0,0 +1,58 @@
+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
diff --git a/lib/app.mod.js b/lib/app.mod.js
new file mode 100644 (file)
index 0000000..e816fc5
--- /dev/null
@@ -0,0 +1,62 @@
+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() + "()";
+  }
+});
+
+});
diff --git a/lib/base/base-mixin.js b/lib/base/base-mixin.js
new file mode 100644 (file)
index 0000000..eba7b88
--- /dev/null
@@ -0,0 +1,216 @@
+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
diff --git a/lib/base/base-mixin.mod.js b/lib/base/base-mixin.mod.js
new file mode 100644 (file)
index 0000000..e2c68e2
--- /dev/null
@@ -0,0 +1,220 @@
+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;
+}
+
+});
diff --git a/lib/base/base-model.js b/lib/base/base-model.js
new file mode 100644 (file)
index 0000000..7a4a6d6
--- /dev/null
@@ -0,0 +1,254 @@
+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
diff --git a/lib/base/base-model.mod.js b/lib/base/base-model.mod.js
new file mode 100644 (file)
index 0000000..1fdd53b
--- /dev/null
@@ -0,0 +1,258 @@
+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;
+}
+
+});
diff --git a/lib/base/base-view.js b/lib/base/base-view.js
new file mode 100644 (file)
index 0000000..9dec625
--- /dev/null
@@ -0,0 +1,330 @@
+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;
+  },
+