From 3a33132e354923030515b2f9bb0fb94ed879fa2d Mon Sep 17 00:00:00 2001 From: David Schoonover Date: Tue, 10 Jul 2012 06:19:39 -0700 Subject: [PATCH] Adds compiled JS source files to be published with the npm distribution. --- lib/app.js | 58 ++ lib/app.mod.js | 62 ++ lib/base/base-mixin.js | 216 +++++++ lib/base/base-mixin.mod.js | 220 +++++++ lib/base/base-model.js | 254 ++++++++ lib/base/base-model.mod.js | 258 ++++++++ lib/base/base-view.js | 330 +++++++++++ lib/base/base-view.mod.js | 334 +++++++++++ lib/base/base.js | 77 +++ lib/base/base.mod.js | 81 +++ lib/base/cascading-model.js | 54 ++ lib/base/cascading-model.mod.js | 58 ++ lib/base/data-binding.js | 65 ++ lib/base/data-binding.mod.js | 69 +++ lib/base/index.js | 14 + lib/base/index.mod.js | 18 + lib/base/model-cache.js | 218 +++++++ lib/base/model-cache.mod.js | 222 +++++++ lib/base/scaffold/index.js | 9 + lib/base/scaffold/index.mod.js | 13 + lib/base/scaffold/scaffold-model.js | 131 ++++ lib/base/scaffold/scaffold-model.mod.js | 135 +++++ lib/base/scaffold/scaffold-view.js | 116 ++++ lib/base/scaffold/scaffold-view.mod.js | 120 ++++ lib/chart/chart-type.js | 435 ++++++++++++++ lib/chart/chart-type.mod.js | 439 ++++++++++++++ lib/chart/index.js | 12 + lib/chart/index.mod.js | 16 + lib/chart/option/chart-option-model.js | 256 ++++++++ lib/chart/option/chart-option-model.mod.js | 260 ++++++++ lib/chart/option/chart-option-view.js | 256 ++++++++ lib/chart/option/chart-option-view.mod.js | 260 ++++++++ lib/chart/option/index.js | 9 + lib/chart/option/index.mod.js | 13 + lib/chart/type/d3-chart.js | 92 +++ lib/chart/type/d3-chart.mod.js | 96 +++ lib/chart/type/d3/d3-bar-element.js | 51 ++ lib/chart/type/d3/d3-bar-element.mod.js | 55 ++ lib/chart/type/d3/d3-chart-element.js | 91 +++ lib/chart/type/d3/d3-chart-element.mod.js | 95 +++ lib/chart/type/d3/d3-line-element.js | 59 ++ lib/chart/type/d3/d3-line-element.mod.js | 63 ++ lib/chart/type/d3/index.js | 10 + lib/chart/type/d3/index.mod.js | 14 + lib/chart/type/dygraphs.js | 127 ++++ lib/chart/type/dygraphs.mod.js | 131 ++++ lib/chart/type/index.mod.js | 5 + lib/dashboard/dashboard-model.js | 87 +++ lib/dashboard/dashboard-model.mod.js | 91 +++ lib/dashboard/dashboard-view.js | 148 +++++ lib/dashboard/dashboard-view.mod.js | 152 +++++ lib/dashboard/index.js | 9 + lib/dashboard/index.mod.js | 13 + lib/data/data-view.js | 142 +++++ lib/data/data-view.mod.js | 146 +++++ lib/data/dataset-model.js | 184 ++++++ lib/data/dataset-model.mod.js | 188 ++++++ lib/data/dataset-view.js | 131 ++++ lib/data/dataset-view.mod.js | 135 +++++ lib/data/datasource-model.js | 217 +++++++ lib/data/datasource-model.mod.js | 221 +++++++ lib/data/datasource-ui-view.js | 76 +++ lib/data/datasource-ui-view.mod.js | 80 +++ lib/data/datasource-view.js | 21 + lib/data/datasource-view.mod.js | 25 + lib/data/index.js | 15 + lib/data/index.mod.js | 19 + lib/data/metric-edit-view.js | 81 +++ lib/data/metric-edit-view.mod.js | 85 +++ lib/data/metric-model.js | 180 ++++++ lib/data/metric-model.mod.js | 184 ++++++ lib/data/project-colors.js | 48 ++ lib/data/project-colors.mod.js | 52 ++ lib/graph/graph-display-view.js | 67 +++ lib/graph/graph-display-view.mod.js | 71 +++ lib/graph/graph-edit-view.js | 194 ++++++ lib/graph/graph-edit-view.mod.js | 198 ++++++ lib/graph/graph-list-view.js | 30 + lib/graph/graph-list-view.mod.js | 34 ++ lib/graph/graph-model.js | 447 ++++++++++++++ lib/graph/graph-model.mod.js | 451 ++++++++++++++ lib/graph/graph-view.js | 311 ++++++++++ lib/graph/graph-view.mod.js | 315 ++++++++++ lib/graph/index.js | 12 + lib/graph/index.mod.js | 16 + lib/main-dashboard.js | 30 + lib/main-display.js | 32 + lib/main-edit.js | 33 + lib/main-geo.js | 102 ++++ lib/main-graph-list.js | 23 + lib/template/browser-helpers.jade.js | 13 + lib/template/chart/chart-option.jade.js | 107 ++++ lib/template/chart/chart-option.jade.mod.js | 111 ++++ lib/template/chart/chart-scaffold.jade.js | 28 + lib/template/chart/chart-scaffold.jade.mod.js | 32 + lib/template/dashboard/dashboard-tab.jade.js | 14 + lib/template/dashboard/dashboard-tab.jade.mod.js | 18 + lib/template/dashboard/dashboard.jade.js | 24 + lib/template/dashboard/dashboard.jade.mod.js | 28 + lib/template/data/data.jade.js | 20 + lib/template/data/data.jade.mod.js | 24 + lib/template/data/dataset-metric.jade.js | 28 + lib/template/data/dataset-metric.jade.mod.js | 32 + lib/template/data/dataset.jade.js | 38 ++ lib/template/data/dataset.jade.mod.js | 42 ++ lib/template/data/datasource-ui.jade.js | 232 ++++++++ lib/template/data/datasource-ui.jade.mod.js | 236 ++++++++ lib/template/data/datasource.jade.js | 87 +++ lib/template/data/datasource.jade.mod.js | 91 +++ lib/template/data/metric-edit.jade.js | 40 ++ lib/template/data/metric-edit.jade.mod.js | 44 ++ lib/template/graph/graph-display.jade.js | 74 +++ lib/template/graph/graph-display.jade.mod.js | 78 +++ lib/template/graph/graph-edit.jade.js | 115 ++++ lib/template/graph/graph-edit.jade.mod.js | 119 ++++ lib/template/graph/graph-list.jade.js | 38 ++ lib/template/graph/graph-list.jade.mod.js | 42 ++ lib/util/backbone.js | 130 ++++ lib/util/backbone.mod.js | 134 +++++ lib/util/cascade.js | 417 +++++++++++++ lib/util/cascade.mod.js | 421 +++++++++++++ lib/util/event/index.js | 2 + lib/util/event/index.mod.js | 6 + lib/util/event/ready-emitter.js | 72 +++ lib/util/event/ready-emitter.mod.js | 76 +++ lib/util/event/waiting-emitter.js | 63 ++ lib/util/event/waiting-emitter.mod.js | 67 +++ lib/util/formatters.js | 71 +++ lib/util/formatters.mod.js | 75 +++ lib/util/index.js | 43 ++ lib/util/index.mod.js | 47 ++ lib/util/op.js | 291 +++++++++ lib/util/op.mod.js | 295 +++++++++ lib/util/parser.js | 173 ++++++ lib/util/parser.mod.js | 177 ++++++ lib/util/timeseries/csv.js | 133 +++++ lib/util/timeseries/csv.mod.js | 137 +++++ lib/util/timeseries/index.js | 2 + lib/util/timeseries/index.mod.js | 6 + lib/util/timeseries/timeseries.js | 199 +++++++ lib/util/timeseries/timeseries.mod.js | 203 +++++++ lib/util/underscore/array.js | 58 ++ lib/util/underscore/array.mod.js | 62 ++ lib/util/underscore/class.js | 73 +++ lib/util/underscore/class.mod.js | 77 +++ lib/util/underscore/function.js | 34 ++ lib/util/underscore/function.mod.js | 38 ++ lib/util/underscore/index.js | 31 + lib/util/underscore/index.mod.js | 35 ++ lib/util/underscore/kv.js | 74 +++ lib/util/underscore/kv.mod.js | 78 +++ lib/util/underscore/object.js | 301 ++++++++++ lib/util/underscore/object.mod.js | 305 ++++++++++ lib/util/underscore/string.js | 101 ++++ lib/util/underscore/string.mod.js | 105 ++++ www/css/chart.css | 12 + www/css/colors.css | 5 + www/css/dashboard.css | 28 + www/css/data.css | 328 ++++++++++ www/css/docs.css | 40 ++ www/css/geo-display.css | 88 +++ www/css/graph-display-print.css | 9 + www/css/graph-display.css | 648 ++++++++++++++++++++ www/css/graph.css | 691 ++++++++++++++++++++++ www/css/hicons.css | 106 ++++ www/css/layout.css | 422 +++++++++++++ www/css/mixins.css | 48 ++ www/css/text.css | 48 ++ www/schema/d3/d3-bar.json | 1 + www/schema/d3/d3-chart.json | 1 + www/schema/d3/d3-geo-world.json | 1 + www/schema/d3/d3-line.json | 1 + www/schema/dygraph.json | 1 + 173 files changed, 19918 insertions(+), 0 deletions(-) create mode 100644 lib/app.js create mode 100644 lib/app.mod.js create mode 100644 lib/base/base-mixin.js create mode 100644 lib/base/base-mixin.mod.js create mode 100644 lib/base/base-model.js create mode 100644 lib/base/base-model.mod.js create mode 100644 lib/base/base-view.js create mode 100644 lib/base/base-view.mod.js create mode 100644 lib/base/base.js create mode 100644 lib/base/base.mod.js create mode 100644 lib/base/cascading-model.js create mode 100644 lib/base/cascading-model.mod.js create mode 100644 lib/base/data-binding.js create mode 100644 lib/base/data-binding.mod.js create mode 100644 lib/base/index.js create mode 100644 lib/base/index.mod.js create mode 100644 lib/base/model-cache.js create mode 100644 lib/base/model-cache.mod.js create mode 100644 lib/base/scaffold/index.js create mode 100644 lib/base/scaffold/index.mod.js create mode 100644 lib/base/scaffold/scaffold-model.js create mode 100644 lib/base/scaffold/scaffold-model.mod.js create mode 100644 lib/base/scaffold/scaffold-view.js create mode 100644 lib/base/scaffold/scaffold-view.mod.js create mode 100644 lib/chart/chart-type.js create mode 100644 lib/chart/chart-type.mod.js create mode 100644 lib/chart/index.js create mode 100644 lib/chart/index.mod.js create mode 100644 lib/chart/option/chart-option-model.js create mode 100644 lib/chart/option/chart-option-model.mod.js create mode 100644 lib/chart/option/chart-option-view.js create mode 100644 lib/chart/option/chart-option-view.mod.js create mode 100644 lib/chart/option/index.js create mode 100644 lib/chart/option/index.mod.js create mode 100644 lib/chart/type/d3-chart.js create mode 100644 lib/chart/type/d3-chart.mod.js create mode 100644 lib/chart/type/d3/d3-bar-element.js create mode 100644 lib/chart/type/d3/d3-bar-element.mod.js create mode 100644 lib/chart/type/d3/d3-chart-element.js create mode 100644 lib/chart/type/d3/d3-chart-element.mod.js create mode 100644 lib/chart/type/d3/d3-line-element.js create mode 100644 lib/chart/type/d3/d3-line-element.mod.js create mode 100644 lib/chart/type/d3/index.js create mode 100644 lib/chart/type/d3/index.mod.js create mode 100644 lib/chart/type/dygraphs.js create mode 100644 lib/chart/type/dygraphs.mod.js create mode 100644 lib/chart/type/index.js create mode 100644 lib/chart/type/index.mod.js create mode 100644 lib/dashboard/dashboard-model.js create mode 100644 lib/dashboard/dashboard-model.mod.js create mode 100644 lib/dashboard/dashboard-view.js create mode 100644 lib/dashboard/dashboard-view.mod.js create mode 100644 lib/dashboard/index.js create mode 100644 lib/dashboard/index.mod.js create mode 100644 lib/data/data-view.js create mode 100644 lib/data/data-view.mod.js create mode 100644 lib/data/dataset-model.js create mode 100644 lib/data/dataset-model.mod.js create mode 100644 lib/data/dataset-view.js create mode 100644 lib/data/dataset-view.mod.js create mode 100644 lib/data/datasource-model.js create mode 100644 lib/data/datasource-model.mod.js create mode 100644 lib/data/datasource-ui-view.js create mode 100644 lib/data/datasource-ui-view.mod.js create mode 100644 lib/data/datasource-view.js create mode 100644 lib/data/datasource-view.mod.js create mode 100644 lib/data/index.js create mode 100644 lib/data/index.mod.js create mode 100644 lib/data/metric-edit-view.js create mode 100644 lib/data/metric-edit-view.mod.js create mode 100644 lib/data/metric-model.js create mode 100644 lib/data/metric-model.mod.js create mode 100644 lib/data/project-colors.js create mode 100644 lib/data/project-colors.mod.js create mode 100644 lib/graph/graph-display-view.js create mode 100644 lib/graph/graph-display-view.mod.js create mode 100644 lib/graph/graph-edit-view.js create mode 100644 lib/graph/graph-edit-view.mod.js create mode 100644 lib/graph/graph-list-view.js create mode 100644 lib/graph/graph-list-view.mod.js create mode 100644 lib/graph/graph-model.js create mode 100644 lib/graph/graph-model.mod.js create mode 100644 lib/graph/graph-view.js create mode 100644 lib/graph/graph-view.mod.js create mode 100644 lib/graph/index.js create mode 100644 lib/graph/index.mod.js create mode 100644 lib/main-dashboard.js create mode 100644 lib/main-display.js create mode 100644 lib/main-edit.js create mode 100644 lib/main-geo.js create mode 100644 lib/main-graph-list.js create mode 100644 lib/template/browser-helpers.jade.js create mode 100644 lib/template/chart/chart-option.jade.js create mode 100644 lib/template/chart/chart-option.jade.mod.js create mode 100644 lib/template/chart/chart-scaffold.jade.js create mode 100644 lib/template/chart/chart-scaffold.jade.mod.js create mode 100644 lib/template/dashboard/dashboard-tab.jade.js create mode 100644 lib/template/dashboard/dashboard-tab.jade.mod.js create mode 100644 lib/template/dashboard/dashboard.jade.js create mode 100644 lib/template/dashboard/dashboard.jade.mod.js create mode 100644 lib/template/data/data.jade.js create mode 100644 lib/template/data/data.jade.mod.js create mode 100644 lib/template/data/dataset-metric.jade.js create mode 100644 lib/template/data/dataset-metric.jade.mod.js create mode 100644 lib/template/data/dataset.jade.js create mode 100644 lib/template/data/dataset.jade.mod.js create mode 100644 lib/template/data/datasource-ui.jade.js create mode 100644 lib/template/data/datasource-ui.jade.mod.js create mode 100644 lib/template/data/datasource.jade.js create mode 100644 lib/template/data/datasource.jade.mod.js create mode 100644 lib/template/data/metric-edit.jade.js create mode 100644 lib/template/data/metric-edit.jade.mod.js create mode 100644 lib/template/graph/graph-display.jade.js create mode 100644 lib/template/graph/graph-display.jade.mod.js create mode 100644 lib/template/graph/graph-edit.jade.js create mode 100644 lib/template/graph/graph-edit.jade.mod.js create mode 100644 lib/template/graph/graph-list.jade.js create mode 100644 lib/template/graph/graph-list.jade.mod.js create mode 100644 lib/util/backbone.js create mode 100644 lib/util/backbone.mod.js create mode 100644 lib/util/cascade.js create mode 100644 lib/util/cascade.mod.js create mode 100644 lib/util/event/index.js create mode 100644 lib/util/event/index.mod.js create mode 100644 lib/util/event/ready-emitter.js create mode 100644 lib/util/event/ready-emitter.mod.js create mode 100644 lib/util/event/waiting-emitter.js create mode 100644 lib/util/event/waiting-emitter.mod.js create mode 100644 lib/util/formatters.js create mode 100644 lib/util/formatters.mod.js create mode 100644 lib/util/index.js create mode 100644 lib/util/index.mod.js create mode 100644 lib/util/op.js create mode 100644 lib/util/op.mod.js create mode 100644 lib/util/parser.js create mode 100644 lib/util/parser.mod.js create mode 100644 lib/util/timeseries/csv.js create mode 100644 lib/util/timeseries/csv.mod.js create mode 100644 lib/util/timeseries/index.js create mode 100644 lib/util/timeseries/index.mod.js create mode 100644 lib/util/timeseries/timeseries.js create mode 100644 lib/util/timeseries/timeseries.mod.js create mode 100644 lib/util/underscore/array.js create mode 100644 lib/util/underscore/array.mod.js create mode 100644 lib/util/underscore/class.js create mode 100644 lib/util/underscore/class.mod.js create mode 100644 lib/util/underscore/function.js create mode 100644 lib/util/underscore/function.mod.js create mode 100644 lib/util/underscore/index.js create mode 100644 lib/util/underscore/index.mod.js create mode 100644 lib/util/underscore/kv.js create mode 100644 lib/util/underscore/kv.mod.js create mode 100644 lib/util/underscore/object.js create mode 100644 lib/util/underscore/object.mod.js create mode 100644 lib/util/underscore/string.js create mode 100644 lib/util/underscore/string.mod.js create mode 100644 www/css/bootstrap-variables.css create mode 100644 www/css/chart.css create mode 100644 www/css/colors.css create mode 100644 www/css/dashboard.css create mode 100644 www/css/data.css create mode 100644 www/css/docs.css create mode 100644 www/css/geo-display.css create mode 100644 www/css/graph-display-print.css create mode 100644 www/css/graph-display.css create mode 100644 www/css/graph.css create mode 100644 www/css/hicons.css create mode 100644 www/css/layout.css create mode 100644 www/css/mixins.css create mode 100644 www/css/text.css create mode 100644 www/schema/d3/d3-bar.json create mode 100644 www/schema/d3/d3-chart.json create mode 100644 www/schema/d3/d3-geo-world.json create mode 100644 www/schema/d3/d3-line.json create mode 100644 www/schema/dygraph.json diff --git a/lib/app.js b/lib/app.js new file mode 100644 index 0000000..b4937f3 --- /dev/null +++ b/lib/app.js @@ -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 index 0000000..e816fc5 --- /dev/null +++ b/lib/app.mod.js @@ -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 index 0000000..eba7b88 --- /dev/null +++ b/lib/base/base-mixin.js @@ -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 + */, + __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 index 0000000..e2c68e2 --- /dev/null +++ b/lib/base/base-mixin.mod.js @@ -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 + */, + __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 index 0000000..7a4a6d6 --- /dev/null +++ b/lib/base/base-model.js @@ -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 index 0000000..1fdd53b --- /dev/null +++ b/lib/base/base-model.mod.js @@ -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 index 0000000..9dec625 --- /dev/null +++ b/lib/base/base-view.js @@ -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; + }, + 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 diff --git a/lib/base/base-view.mod.js b/lib/base/base-view.mod.js new file mode 100644 index 0000000..2105eea --- /dev/null +++ b/lib/base/base-view.mod.js @@ -0,0 +1,334 @@ +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; +} + +}); diff --git a/lib/base/base.js b/lib/base/base.js new file mode 100644 index 0000000..e3f4bce --- /dev/null +++ b/lib/base/base.js @@ -0,0 +1,77 @@ +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 + */ + 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 diff --git a/lib/base/base.mod.js b/lib/base/base.mod.js new file mode 100644 index 0000000..68ac0c5 --- /dev/null +++ b/lib/base/base.mod.js @@ -0,0 +1,81 @@ +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 + */ + 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; +} + +}); diff --git a/lib/base/cascading-model.js b/lib/base/cascading-model.js new file mode 100644 index 0000000..8a1f89c --- /dev/null +++ b/lib/base/cascading-model.js @@ -0,0 +1,54 @@ +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 diff --git a/lib/base/cascading-model.mod.js b/lib/base/cascading-model.mod.js new file mode 100644 index 0000000..5fc62d5 --- /dev/null +++ b/lib/base/cascading-model.mod.js @@ -0,0 +1,58 @@ +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; +} + +}); diff --git a/lib/base/data-binding.js b/lib/base/data-binding.js new file mode 100644 index 0000000..9f1835c --- /dev/null +++ b/lib/base/data-binding.js @@ -0,0 +1,65 @@ +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 diff --git a/lib/base/data-binding.mod.js b/lib/base/data-binding.mod.js new file mode 100644 index 0000000..957bc2c --- /dev/null +++ b/lib/base/data-binding.mod.js @@ -0,0 +1,69 @@ +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; +}()); + +}); diff --git a/lib/base/index.js b/lib/base/index.js new file mode 100644 index 0000000..1c9458c --- /dev/null +++ b/lib/base/index.js @@ -0,0 +1,14 @@ +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 diff --git a/lib/base/index.mod.js b/lib/base/index.mod.js new file mode 100644 index 0000000..a599753 --- /dev/null +++ b/lib/base/index.mod.js @@ -0,0 +1,18 @@ +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; +} + +}); diff --git a/lib/base/model-cache.js b/lib/base/model-cache.js new file mode 100644 index 0000000..f07c776 --- /dev/null +++ b/lib/base/model-cache.js @@ -0,0 +1,218 @@ +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 + */ + prototype.ModelType = null; + /** + * Collection holding the cached Models. + * @private + * @type Backbone.Collection + */ + prototype.cache = null; + /** + * @constructor + * @param {Class} [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} [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} [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} 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} 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 diff --git a/lib/base/model-cache.mod.js b/lib/base/model-cache.mod.js new file mode 100644 index 0000000..4e529f9 --- /dev/null +++ b/lib/base/model-cache.mod.js @@ -0,0 +1,222 @@ +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 + */ + prototype.ModelType = null; + /** + * Collection holding the cached Models. + * @private + * @type Backbone.Collection + */ + prototype.cache = null; + /** + * @constructor + * @param {Class} [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} [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} [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} 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} id Model ID to lookup. + * @param {Function} cb