From acb540386ad679413fb84305cb438228707f8cdf Mon Sep 17 00:00:00 2001 From: David Schoonover Date: Tue, 10 Jul 2012 04:30:14 -0700 Subject: [PATCH] Moves Coco source tree from 'lib' to 'src' -- compiled JS files will live in 'lib'. --- lib/app.co | 51 ---- lib/base/asset-manager.co | 43 --- lib/base/base-mixin.co | 221 --------------- lib/base/base-model.co | 252 ----------------- lib/base/base-view.co | 293 ------------------- lib/base/base.co | 74 ----- lib/base/cascading-model.co | 57 ---- lib/base/data-binding.co | 67 ----- lib/base/index.co | 9 - lib/base/model-cache.co | 196 ------------- lib/base/scaffold/index.co | 3 - lib/base/scaffold/scaffold-model.co | 105 ------- lib/base/scaffold/scaffold-view.co | 125 --------- lib/chart/chart-type.co | 425 ---------------------------- lib/chart/index.co | 8 - lib/chart/option/chart-option-model.co | 218 --------------- lib/chart/option/chart-option-view.co | 270 ------------------ lib/chart/option/index.co | 4 - lib/chart/type/d3-chart.co | 138 --------- lib/chart/type/d3/d3-bar-chart-type.co | 239 ---------------- lib/chart/type/d3/d3-bar-element.co | 78 ----- lib/chart/type/d3/d3-chart-element.co | 84 ------ lib/chart/type/d3/d3-geo-element.co | 185 ------------ lib/chart/type/d3/d3-line-element.co | 111 -------- lib/chart/type/d3/index.co | 6 - lib/chart/type/dygraphs.co | 133 --------- lib/dashboard/dashboard-model.co | 87 ------ lib/dashboard/dashboard-view.co | 174 ------------ lib/dashboard/index.co | 3 - lib/data/data-view.co | 122 -------- lib/data/dataset-model.co | 183 ------------ lib/data/dataset-view.co | 155 ---------- lib/data/datasource-model.co | 190 ------------- lib/data/datasource-ui-view.co | 69 ----- lib/data/datasource-view.co | 24 -- lib/data/index.co | 13 - lib/data/metric-edit-view.co | 94 ------- lib/data/metric-model.co | 175 ------------ lib/data/project-colors.co | 37 --- lib/graph/graph-display-view.co | 88 ------ lib/graph/graph-edit-view.co | 223 --------------- lib/graph/graph-list-view.co | 36 --- lib/graph/graph-model.co | 433 ----------------------------- lib/graph/graph-view.co | 299 -------------------- lib/graph/index.co | 7 - lib/main-dashboard.co | 37 --- lib/main-display.co | 53 ---- lib/main-edit.co | 53 ---- lib/main-geo.co | 181 ------------ lib/main-graph-list.co | 26 -- lib/server/controller.co | 105 ------- lib/server/controllers/dashboard.co | 91 ------ lib/server/controllers/datasource.co | 127 --------- lib/server/controllers/graph.co | 153 ---------- lib/server/file-controller.co | 95 ------- lib/server/files.co | 96 ------- lib/server/index.js | 8 - lib/server/middleware.co | 257 ----------------- lib/server/mkdirp.co | 46 --- lib/server/proxy.co | 37 --- lib/server/reqinfo.co | 20 -- lib/server/server.co | 58 ---- lib/server/view-helpers.co | 52 ---- lib/template/browser-helpers.jade | 3 - lib/template/chart/chart-option.jade | 41 --- lib/template/chart/chart-scaffold.jade | 12 - lib/template/dashboard/dashboard-tab.jade | 1 - lib/template/dashboard/dashboard.jade | 17 -- lib/template/data/data.jade | 4 - lib/template/data/dataset-metric.jade | 8 - lib/template/data/dataset.jade | 24 -- lib/template/data/datasource-ui.jade | 55 ---- lib/template/data/datasource.jade | 29 -- lib/template/data/metric-edit.jade | 22 -- lib/template/graph/graph-display.jade | 37 --- lib/template/graph/graph-edit.jade | 76 ----- lib/template/graph/graph-list.jade | 7 - lib/util/aliasdict.co | 158 ----------- lib/util/backbone.co | 128 --------- lib/util/bitstring.co | 247 ---------------- lib/util/cascade.co | 379 ------------------------- lib/util/crc.co | 58 ---- lib/util/event/index.co | 2 - lib/util/event/ready-emitter.co | 54 ---- lib/util/event/waiting-emitter.co | 55 ---- lib/util/formatters.co | 68 ----- lib/util/hashset.co | 418 ---------------------------- lib/util/index.co | 38 --- lib/util/op.co | 154 ---------- lib/util/parser.co | 127 --------- lib/util/timeseries/csv.co | 115 -------- lib/util/timeseries/index.co | 2 - lib/util/timeseries/timeseries.co | 191 ------------- lib/util/underscore/_functions.co | 220 --------------- lib/util/underscore/array.co | 48 ---- lib/util/underscore/class.co | 53 ---- lib/util/underscore/function.co | 27 -- lib/util/underscore/index.co | 29 -- lib/util/underscore/kv.co | 60 ---- lib/util/underscore/object.co | 270 ------------------ lib/util/underscore/string.co | 75 ----- src/app.co | 51 ++++ src/base/asset-manager.co | 43 +++ src/base/base-mixin.co | 221 +++++++++++++++ src/base/base-model.co | 252 +++++++++++++++++ src/base/base-view.co | 293 +++++++++++++++++++ src/base/base.co | 74 +++++ src/base/cascading-model.co | 57 ++++ src/base/data-binding.co | 67 +++++ src/base/index.co | 9 + src/base/model-cache.co | 196 +++++++++++++ src/base/scaffold/index.co | 3 + src/base/scaffold/scaffold-model.co | 105 +++++++ src/base/scaffold/scaffold-view.co | 125 +++++++++ src/chart/chart-type.co | 425 ++++++++++++++++++++++++++++ src/chart/index.co | 8 + src/chart/option/chart-option-model.co | 218 +++++++++++++++ src/chart/option/chart-option-view.co | 270 ++++++++++++++++++ src/chart/option/index.co | 4 + src/chart/type/d3-chart.co | 138 +++++++++ src/chart/type/d3/d3-bar-chart-type.co | 239 ++++++++++++++++ src/chart/type/d3/d3-bar-element.co | 78 +++++ src/chart/type/d3/d3-chart-element.co | 84 ++++++ src/chart/type/d3/d3-geo-element.co | 185 ++++++++++++ src/chart/type/d3/d3-line-element.co | 111 ++++++++ src/chart/type/d3/index.co | 6 + src/chart/type/dygraphs.co | 133 +++++++++ src/dashboard/dashboard-model.co | 87 ++++++ src/dashboard/dashboard-view.co | 174 ++++++++++++ src/dashboard/index.co | 3 + src/data/data-view.co | 122 ++++++++ src/data/dataset-model.co | 183 ++++++++++++ src/data/dataset-view.co | 155 ++++++++++ src/data/datasource-model.co | 190 +++++++++++++ src/data/datasource-ui-view.co | 69 +++++ src/data/datasource-view.co | 24 ++ src/data/index.co | 13 + src/data/metric-edit-view.co | 94 +++++++ src/data/metric-model.co | 175 ++++++++++++ src/data/project-colors.co | 37 +++ src/graph/graph-display-view.co | 88 ++++++ src/graph/graph-edit-view.co | 223 +++++++++++++++ src/graph/graph-list-view.co | 36 +++ src/graph/graph-model.co | 433 +++++++++++++++++++++++++++++ src/graph/graph-view.co | 299 ++++++++++++++++++++ src/graph/index.co | 7 + src/main-dashboard.co | 37 +++ src/main-display.co | 53 ++++ src/main-edit.co | 53 ++++ src/main-geo.co | 181 ++++++++++++ src/main-graph-list.co | 26 ++ src/server/controller.co | 105 +++++++ src/server/controllers/dashboard.co | 91 ++++++ src/server/controllers/datasource.co | 127 +++++++++ src/server/controllers/graph.co | 153 ++++++++++ src/server/file-controller.co | 95 +++++++ src/server/files.co | 96 +++++++ src/server/index.js | 8 + src/server/middleware.co | 257 +++++++++++++++++ src/server/mkdirp.co | 46 +++ src/server/proxy.co | 37 +++ src/server/reqinfo.co | 20 ++ src/server/server.co | 58 ++++ src/server/view-helpers.co | 52 ++++ src/template/browser-helpers.jade | 3 + src/template/chart/chart-option.jade | 41 +++ src/template/chart/chart-scaffold.jade | 12 + src/template/dashboard/dashboard-tab.jade | 1 + src/template/dashboard/dashboard.jade | 17 ++ src/template/data/data.jade | 4 + src/template/data/dataset-metric.jade | 8 + src/template/data/dataset.jade | 24 ++ src/template/data/datasource-ui.jade | 55 ++++ src/template/data/datasource.jade | 29 ++ src/template/data/metric-edit.jade | 22 ++ src/template/graph/graph-display.jade | 37 +++ src/template/graph/graph-edit.jade | 76 +++++ src/template/graph/graph-list.jade | 7 + src/util/aliasdict.co | 158 +++++++++++ src/util/backbone.co | 128 +++++++++ src/util/bitstring.co | 247 ++++++++++++++++ src/util/cascade.co | 379 +++++++++++++++++++++++++ src/util/crc.co | 58 ++++ src/util/event/index.co | 2 + src/util/event/ready-emitter.co | 54 ++++ src/util/event/waiting-emitter.co | 55 ++++ src/util/formatters.co | 68 +++++ src/util/hashset.co | 418 ++++++++++++++++++++++++++++ src/util/index.co | 38 +++ src/util/op.co | 154 ++++++++++ src/util/parser.co | 127 +++++++++ src/util/timeseries/csv.co | 115 ++++++++ src/util/timeseries/index.co | 2 + src/util/timeseries/timeseries.co | 191 +++++++++++++ src/util/underscore/_functions.co | 220 +++++++++++++++ src/util/underscore/array.co | 48 ++++ src/util/underscore/class.co | 53 ++++ src/util/underscore/function.co | 27 ++ src/util/underscore/index.co | 29 ++ src/util/underscore/kv.co | 60 ++++ src/util/underscore/object.co | 270 ++++++++++++++++++ src/util/underscore/string.co | 75 +++++ 202 files changed, 10614 insertions(+), 10614 deletions(-) delete mode 100644 lib/app.co delete mode 100644 lib/base/asset-manager.co delete mode 100644 lib/base/base-mixin.co delete mode 100644 lib/base/base-model.co delete mode 100644 lib/base/base-view.co delete mode 100644 lib/base/base.co delete mode 100644 lib/base/cascading-model.co delete mode 100644 lib/base/data-binding.co delete mode 100644 lib/base/index.co delete mode 100644 lib/base/model-cache.co delete mode 100644 lib/base/scaffold/index.co delete mode 100644 lib/base/scaffold/scaffold-model.co delete mode 100644 lib/base/scaffold/scaffold-view.co delete mode 100644 lib/chart/chart-type.co delete mode 100644 lib/chart/index.co delete mode 100644 lib/chart/option/chart-option-model.co delete mode 100644 lib/chart/option/chart-option-view.co delete mode 100644 lib/chart/option/index.co delete mode 100644 lib/chart/type/d3-chart.co delete mode 100644 lib/chart/type/d3/d3-bar-chart-type.co delete mode 100644 lib/chart/type/d3/d3-bar-element.co delete mode 100644 lib/chart/type/d3/d3-chart-element.co delete mode 100644 lib/chart/type/d3/d3-geo-element.co delete mode 100644 lib/chart/type/d3/d3-line-element.co delete mode 100644 lib/chart/type/d3/index.co delete mode 100644 lib/chart/type/dygraphs.co delete mode 100644 lib/chart/type/index.co delete mode 100644 lib/dashboard/dashboard-model.co delete mode 100644 lib/dashboard/dashboard-view.co delete mode 100644 lib/dashboard/index.co delete mode 100644 lib/data/data-view.co delete mode 100644 lib/data/dataset-model.co delete mode 100644 lib/data/dataset-view.co delete mode 100644 lib/data/datasource-model.co delete mode 100644 lib/data/datasource-ui-view.co delete mode 100644 lib/data/datasource-view.co delete mode 100644 lib/data/index.co delete mode 100644 lib/data/metric-edit-view.co delete mode 100644 lib/data/metric-model.co delete mode 100644 lib/data/project-colors.co delete mode 100644 lib/graph/graph-display-view.co delete mode 100644 lib/graph/graph-edit-view.co delete mode 100644 lib/graph/graph-list-view.co delete mode 100644 lib/graph/graph-model.co delete mode 100644 lib/graph/graph-view.co delete mode 100644 lib/graph/index.co delete mode 100644 lib/main-dashboard.co delete mode 100644 lib/main-display.co delete mode 100644 lib/main-edit.co delete mode 100644 lib/main-geo.co delete mode 100644 lib/main-graph-list.co delete mode 100644 lib/server/controller.co delete mode 100644 lib/server/controllers/dashboard.co delete mode 100644 lib/server/controllers/datasource.co delete mode 100644 lib/server/controllers/graph.co delete mode 100644 lib/server/controllers/index.co delete mode 100644 lib/server/file-controller.co delete mode 100644 lib/server/files.co delete mode 100755 lib/server/index.js delete mode 100755 lib/server/middleware.co delete mode 100644 lib/server/mkdirp.co delete mode 100644 lib/server/proxy.co delete mode 100644 lib/server/reqinfo.co delete mode 100755 lib/server/server.co delete mode 100644 lib/server/view-helpers.co delete mode 100644 lib/template/browser-helpers.jade delete mode 100644 lib/template/chart/chart-option.jade delete mode 100644 lib/template/chart/chart-scaffold.jade delete mode 100644 lib/template/dashboard/dashboard-tab.jade delete mode 100644 lib/template/dashboard/dashboard.jade delete mode 100644 lib/template/data/data.jade delete mode 100644 lib/template/data/dataset-metric.jade delete mode 100644 lib/template/data/dataset.jade delete mode 100644 lib/template/data/datasource-ui.jade delete mode 100644 lib/template/data/datasource.jade delete mode 100644 lib/template/data/metric-edit.jade delete mode 100644 lib/template/graph/graph-display.jade delete mode 100644 lib/template/graph/graph-edit.jade delete mode 100644 lib/template/graph/graph-list.jade delete mode 100644 lib/util/aliasdict.co delete mode 100644 lib/util/backbone.co delete mode 100644 lib/util/bitstring.co delete mode 100644 lib/util/cascade.co delete mode 100644 lib/util/crc.co delete mode 100644 lib/util/event/index.co delete mode 100644 lib/util/event/ready-emitter.co delete mode 100644 lib/util/event/waiting-emitter.co delete mode 100644 lib/util/formatters.co delete mode 100644 lib/util/hashset.co delete mode 100644 lib/util/index.co delete mode 100644 lib/util/op.co delete mode 100644 lib/util/parser.co delete mode 100644 lib/util/timeseries/csv.co delete mode 100644 lib/util/timeseries/index.co delete mode 100644 lib/util/timeseries/timeseries.co delete mode 100644 lib/util/underscore/_functions.co delete mode 100644 lib/util/underscore/array.co delete mode 100644 lib/util/underscore/class.co delete mode 100644 lib/util/underscore/function.co delete mode 100644 lib/util/underscore/index.co delete mode 100644 lib/util/underscore/kv.co delete mode 100644 lib/util/underscore/object.co delete mode 100644 lib/util/underscore/string.co create mode 100644 src/app.co create mode 100644 src/base/asset-manager.co create mode 100644 src/base/base-mixin.co create mode 100644 src/base/base-model.co create mode 100644 src/base/base-view.co create mode 100644 src/base/base.co create mode 100644 src/base/cascading-model.co create mode 100644 src/base/data-binding.co create mode 100644 src/base/index.co create mode 100644 src/base/model-cache.co create mode 100644 src/base/scaffold/index.co create mode 100644 src/base/scaffold/scaffold-model.co create mode 100644 src/base/scaffold/scaffold-view.co create mode 100644 src/chart/chart-type.co create mode 100644 src/chart/index.co create mode 100644 src/chart/option/chart-option-model.co create mode 100644 src/chart/option/chart-option-view.co create mode 100644 src/chart/option/index.co create mode 100644 src/chart/type/d3-chart.co create mode 100644 src/chart/type/d3/d3-bar-chart-type.co create mode 100644 src/chart/type/d3/d3-bar-element.co create mode 100644 src/chart/type/d3/d3-chart-element.co create mode 100644 src/chart/type/d3/d3-geo-element.co create mode 100644 src/chart/type/d3/d3-line-element.co create mode 100644 src/chart/type/d3/index.co create mode 100644 src/chart/type/dygraphs.co create mode 100644 src/chart/type/index.co create mode 100644 src/dashboard/dashboard-model.co create mode 100644 src/dashboard/dashboard-view.co create mode 100644 src/dashboard/index.co create mode 100644 src/data/data-view.co create mode 100644 src/data/dataset-model.co create mode 100644 src/data/dataset-view.co create mode 100644 src/data/datasource-model.co create mode 100644 src/data/datasource-ui-view.co create mode 100644 src/data/datasource-view.co create mode 100644 src/data/index.co create mode 100644 src/data/metric-edit-view.co create mode 100644 src/data/metric-model.co create mode 100644 src/data/project-colors.co create mode 100644 src/graph/graph-display-view.co create mode 100644 src/graph/graph-edit-view.co create mode 100644 src/graph/graph-list-view.co create mode 100644 src/graph/graph-model.co create mode 100644 src/graph/graph-view.co create mode 100644 src/graph/index.co create mode 100644 src/main-dashboard.co create mode 100644 src/main-display.co create mode 100644 src/main-edit.co create mode 100644 src/main-geo.co create mode 100644 src/main-graph-list.co create mode 100644 src/server/controller.co create mode 100644 src/server/controllers/dashboard.co create mode 100644 src/server/controllers/datasource.co create mode 100644 src/server/controllers/graph.co create mode 100644 src/server/controllers/index.co create mode 100644 src/server/file-controller.co create mode 100644 src/server/files.co create mode 100755 src/server/index.js create mode 100755 src/server/middleware.co create mode 100644 src/server/mkdirp.co create mode 100644 src/server/proxy.co create mode 100644 src/server/reqinfo.co create mode 100755 src/server/server.co create mode 100644 src/server/view-helpers.co create mode 100644 src/template/browser-helpers.jade create mode 100644 src/template/chart/chart-option.jade create mode 100644 src/template/chart/chart-scaffold.jade create mode 100644 src/template/dashboard/dashboard-tab.jade create mode 100644 src/template/dashboard/dashboard.jade create mode 100644 src/template/data/data.jade create mode 100644 src/template/data/dataset-metric.jade create mode 100644 src/template/data/dataset.jade create mode 100644 src/template/data/datasource-ui.jade create mode 100644 src/template/data/datasource.jade create mode 100644 src/template/data/metric-edit.jade create mode 100644 src/template/graph/graph-display.jade create mode 100644 src/template/graph/graph-edit.jade create mode 100644 src/template/graph/graph-list.jade create mode 100644 src/util/aliasdict.co create mode 100644 src/util/backbone.co create mode 100644 src/util/bitstring.co create mode 100644 src/util/cascade.co create mode 100644 src/util/crc.co create mode 100644 src/util/event/index.co create mode 100644 src/util/event/ready-emitter.co create mode 100644 src/util/event/waiting-emitter.co create mode 100644 src/util/formatters.co create mode 100644 src/util/hashset.co create mode 100644 src/util/index.co create mode 100644 src/util/op.co create mode 100644 src/util/parser.co create mode 100644 src/util/timeseries/csv.co create mode 100644 src/util/timeseries/index.co create mode 100644 src/util/timeseries/timeseries.co create mode 100644 src/util/underscore/_functions.co create mode 100644 src/util/underscore/array.co create mode 100644 src/util/underscore/class.co create mode 100644 src/util/underscore/function.co create mode 100644 src/util/underscore/index.co create mode 100644 src/util/underscore/kv.co create mode 100644 src/util/underscore/object.co create mode 100644 src/util/underscore/string.co diff --git a/lib/app.co b/lib/app.co deleted file mode 100644 index a4185b0..0000000 --- a/lib/app.co +++ /dev/null @@ -1,51 +0,0 @@ -Backbone = require 'backbone' - -{ _, op, -} = require 'kraken/util' - - -/** - * @class Application view, automatically attaching to an existing element - * found at `appSelector`. - * @extends Backbone.View - */ -AppView = exports.AppView = Backbone.View.extend do # {{{ - appSelector : '#content .inner' - - - /** - * @constructor - */ - constructor: function AppView (options={}) - if typeof options is 'function' - @initialize = options - options = {} - else - @initialize = that if options.initialize - - @appSelector = that if options.appSelector - options.el or= jQuery @appSelector .0 - Backbone.View.call this, options - - jQuery ~> @render() - this - - /** - * Override to set up your app. This method may be passed - * as an option to the constructor. - */ - initialize: -> # stub - - /** - * Append subviews. - */ - render : -> - @$el.append @view.el if @view and not @view.$el.parent()?.length - - getClassName: -> - "#{@..name or @..displayName}" - - toString: -> - "#{@getClassName()}()" -# }}} - diff --git a/lib/base/asset-manager.co b/lib/base/asset-manager.co deleted file mode 100644 index fa91012..0000000 --- a/lib/base/asset-manager.co +++ /dev/null @@ -1,43 +0,0 @@ -{ _, op, -} = require 'kraken/util' -{ ReadyEmitter, -} = require 'kraken/util/event' - - - - -class AssetManager extends ReadyEmitter - # Map from key/url to data. - assets : null - - - /** - * @constructor - */ - -> - super ... - @assets = {} - - - - - /** - * Load the corresponding chart specification, which includes - * info about valid options, along with their types and defaults. - */ - load: -> - return this if @ready - proto = @constructor:: - jQuery.ajax do - url : @SPEC_URL - success : (spec) ~> - proto.spec = spec - proto.options_ordered = spec - proto.options = _.synthesize spec, -> [it.name, it] - proto.ready = true - @emit 'ready', this - error: ~> console.error "Error loading #{@typeName} spec! #it" - this - - - diff --git a/lib/base/base-mixin.co b/lib/base/base-mixin.co deleted file mode 100644 index c6764cb..0000000 --- a/lib/base/base-mixin.co +++ /dev/null @@ -1,221 +0,0 @@ -Backbone = require 'backbone' - -{ _, op, -} = require 'kraken/util' - - - -BaseBackboneMixin = exports.BaseBackboneMixin = - - initialize: -> - @__apply_bind__() - - - ### Auto-Bound methods - - /** - * 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__: -> - names = _ @pluckSuperAndSelf '__bind__' .chain().flatten().compact().unique().value() - _.bindAll this, ...names if names.length - - - - - ### Events - - /** - * 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: (lock='ready', event='ready') -> - return this if @[lock] - @[lock] = true - @trigger event, this - this - - /** - * Resets the 'ready' event to its non-triggered state, firing a - * 'ready-reset' event. - * @returns {this} - */ - resetReady: (lock='ready', event='ready') -> - return this unless @[lock] - @[lock] = false - @trigger "#event-reset", this - 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: (events, callback, context=this) -> - return this if not callback - Backbone.Events.on ... - if @ready and _.contains events.split(/\s+/), 'ready' - callback.call context, this - this - - makeHandlersForCallback: (cb) -> - success : ~> cb.call this, [null].concat arguments - error : ~> cb.call this, it - - - - - ### Synchronization - - /** - * Count of outstanding tasks. - * @type Number - */ - waitingOn : 0 - - - /** - * Increment the waiting task counter. - * @returns {this} - */ - wait: -> - count = @waitingOn - @waitingOn += 1 - # console.log "#this.wait! #count --> #{@waitingOn}" - # console.trace() - @trigger('start-waiting', this) if count is 0 and @waitingOn > 0 - this - - /** - * Decrement the waiting task counter. - * @returns {this} - */ - unwait: -> - count = @waitingOn - @waitingOn -= 1 - # console.warn "#this.unwait! #{@waitingOn} < 0" if @waitingOn < 0 - # console.log "#this.unwait! #count --> #{@waitingOn}" - # console.trace() - @trigger('stop-waiting', this) if @waitingOn is 0 and count > 0 - 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: (fn) -> - self = this - -> - # console.log "#self.unwaitAnd( function #{fn.name or fn.displayName}() )" - # console.trace() - self.unwait(); fn ... - - - - ### - - getClassName: -> - "#{@..name or @..displayName}" - - toString: -> - "#{@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 - * - */ -class exports.Mixin - - /** - * Mixes this mixin into the target. If `target` is not a class, a new - * object will be returned which inherits from the mixin. - */ - @mix = (target) -> - return that unless target - - MixinClass = Mixin - MixinClass = @constructor if this instanceof Mixin - MixinClass = this if this instanceof Function - - if typeof target is 'function' - target:: import MixinClass:: - else - target = _.clone(MixinClass::) import target - - (target.__mixins__ or= []).push MixinClass - target - - /** - * Coco metaprogramming hook to propagate class properties and methods. - */ - @extended = (SubClass) -> - SuperClass = this - for own k, v in SuperClass - SubClass[k] = v unless SubClass[k] - SubClass - - - -# /** -# * @returns {Function} Function which takes a target object or class, -# * mixes the MixinClass into it, and then returns it. If the target is -# * not a class, a new object will be returned which inherits from the mixin. -# */ -# makeMixer = exports.makeMixer = (MixinClass) -> -# mixinBody = if typeof MixinClass is 'function' then MixinClass:: else MixinClass -# mixinMixer = (target) -> -# if typeof target is 'function' -# target:: import mixinBody -# else -# target = _.clone(mixinBody) import target -# target -# -# mixinBase = exports.mixinBase = makeMixer BaseBackboneMixin - - -/** - * Mixes BaseBackboneMixin into another object or prototype. - * @returns {Object} The merged prototype object. - */ -mixinBase = exports.mixinBase = (...bodies) -> - _.extend _.clone(BaseBackboneMixin), ...bodies - - - diff --git a/lib/base/base-model.co b/lib/base/base-model.co deleted file mode 100644 index 54b725e..0000000 --- a/lib/base/base-model.co +++ /dev/null @@ -1,252 +0,0 @@ -Backbone = require 'backbone' - -{ _, op, -} = require 'kraken/util' -{ BaseBackboneMixin, mixinBase, -} = require 'kraken/base/base-mixin' - - - -/** - * @class Base model, extending Backbone.Model, used by scaffold and others. - * @extends Backbone.Model - */ -BaseModel = exports.BaseModel = Backbone.Model.extend mixinBase do # {{{ - - constructor : function BaseModel - @__class__ = @constructor - @__superclass__ = @..__super__.constructor - @waitingOn = 0 - Backbone.Model ... - # @..trigger 'create', this - - - - - ### Accessors - - url: -> - "#{@urlRoot}/#{@get('id')}.json" - - has: (key) -> - @get(key)? - - get: (key) -> - _.getNested @attributes, key - - # TODO: nested sets, handling events - - # set: (key, value, opts) -> - # if _.isObject(key) and key? - # [values, opts] = [key, value] - # else - # values = { "#key": value } - # - # # TODO: Validation - # @_changed or= {} - # - # for key, value in values - # if _.str.contains key, '.' - # _.setNested @attributes, key, value, opts - # else - # Backbone.Model::set.call this, key, value, opts - # - # this - # - # unset : (key, opts) -> - # - - - ### Data & Model Loading - - /** - * 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: -> - console.log "#this.load()" - @loader do - start : @loadModel - completeEvent : 'fetch-success' - 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: (opts={}) -> - opts = { - -force - -readyIfError - startEvent : 'load' - completeEvent : 'load-success' - errorEvent : 'load-error' - ...opts - } - @resetReady() if opts.force - throw new Error('You must specify a `start` function to start loading!') unless opts.start - return this if @loading or @ready - - @wait() - @loading = true - @trigger opts.startEvent, this - - # Register a handler for the post-load event that will run only once - @once opts.completeEvent, ~> - # console.log "#{this}.onLoadComplete()" - @loading = false - @unwait() # terminates the `load` wait - @trigger 'load-success', this unless opts.completeEvent is 'load-success' - @triggerReady() - @once opts.errorEvent, ~> - # console.log "#{this}.onLoadError()" - @loading = false - @unwait() # terminates the `load` wait - @trigger 'load-error', this unless opts.errorEvent is 'load-error' - @triggerReady() if opts.readyIfError - - # Finally, start the loading process - opts.start.call this - this - - /** - * Runs `.fetch()`, triggering a `fetch` event at start, and - * `fetch-success` / `fetch-error` on completion. - * - * @protected - * @returns {this} - */ - loadModel: -> - @wait() - @trigger 'fetch', this - @fetch do - success : ~> @unwait(); @trigger 'fetch-success', this - error : ~> @unwait(); @trigger 'fetch-error', this, ...arguments - this - - - ### Serialization - - serialize: (v) -> - # if v!? - # v = '' - if _.isBoolean v - v = Number v - else if _.isObject v - v = JSON.stringify v - 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: (opts={}) -> - opts = {-keepFunctions, ...opts} - kvo = _.collapseObject @toJSON() - for k, v in kvo - kvo[k] = @serialize v if opts.keepFunctions or typeof v is not 'function' - kvo - - /** - * Serialize the model into a `www-form-encoded` string suitable for use as - * a query string or a POST body. - * @returns {String} - */ - toKV: (item_delim='&', kv_delim='=') -> - _.toKV @toKVPairs(), item_delim, kv_delim - - /** - * @returns {String} URL identifying this model. - */ - toURL: -> - "?#{@toKV ...}" - - toString: -> - "#{@getClassName()}(cid=#{@cid}, id=#{@id})" - - -# Class Methods -BaseModel import do - /** - * 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: (o, item_delim='&', kv_delim='=') -> - o = _.fromKV o, item_delim, kv_delim if typeof o is 'string' - Cls = if typeof this is 'function' then this else this.constructor - 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 do # {{{ - - constructor : function BaseList - @__class__ = @constructor - @__superclass__ = @..__super__.constructor - @waitingOn = 0 - Backbone.Collection ... - # @trigger 'create', this - - - getIds: -> - @models.map -> it.id or it.get('id') or it.cid - - - ### Serialization - - toKVPairs: -> - _.collapseObject @toJSON() - - toKV: (item_delim='&', kv_delim='=') -> - _.toKV @toKVPairs(), item_delim, kv_delim - - toURL: (item_delim='&', kv_delim='=') -> - "?#{@toKV ...}" - - toString: -> - "#{@getClassName()}[#{@length}]" - - toStringWithIds: -> - modelIds = @models - .map -> "\"#{it.id ? it.cid}\"" - .join ', ' - "#{@getClassName()}[#{@length}](#modelIds)" -# }}} - - diff --git a/lib/base/base-view.co b/lib/base/base-view.co deleted file mode 100644 index 18646a5..0000000 --- a/lib/base/base-view.co +++ /dev/null @@ -1,293 +0,0 @@ -Backbone = require 'backbone' - -{ _, op, -} = require 'kraken/util' -{ BaseBackboneMixin, mixinBase, -} = require 'kraken/base/base-mixin' -{ BaseModel, -} = require 'kraken/base/base-mixin' -{ DataBinding, -} = require 'kraken/base/data-binding' - - - -/** - * @class Base view, extending Backbone.View, used by scaffold and others. - * @extends Backbone.View - */ -BaseView = exports.BaseView = Backbone.View.extend mixinBase do # {{{ - 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 BaseView - @__class__ = @constructor - @__superclass__ = @..__super__.constructor - @waitingOn = 0 - @subviews = new ViewList - @onReturnKeypress = _.debounce @onReturnKeypress.bind(this), 50 - Backbone.View ... - @trigger 'create', this - - initialize: -> - @__apply_bind__() - - @setModel @model - @build() - @$el.on 'form submit', -> it.preventDefault() - - setModel: (model) -> - if @model - @model.off 'change', @render, this - @model.off 'destroy', @remove, this - delete @model.view - data = @$el.data() - delete data.model - delete data.view - if @model = model - @model.view = this - @$el.data { @model, view:this } - @model.on 'change', @render, this - @model.on 'destroy', @remove, this - @trigger 'change:model', this, model - model - - - - ### Subviews {{{ - - setParent: (parent) -> - [old_parent, @parent] = [@parent, parent] - @trigger 'parent', this, parent, old_parent - this - - unsetParent: -> - [old_parent, @parent] = [@parent, null] - @trigger 'unparent', this, old_parent - this - - - addSubview: (view) -> - @removeSubview view - @subviews.push view - view.setParent this - view - - removeSubview: (view) -> - if @hasSubview view - view.remove() - @subviews.remove view - view.unsetParent() - view - - hasSubview: (view) -> - @subviews.contains view - - invokeSubviews: -> - @subviews.invoke ...arguments - - removeAllSubviews: -> - @subviews.forEach @removeSubview, this - # @subviews = new ViewList - this - - - - ### }}} - ### DOM Utilities {{{ - - attach: (el) -> - # @undelegateEvents() - @$el.appendTo el - # only trigger the event the first time - return this if @isAttached - @isAttached = true - _.delay do - ~> # have to let DOM settle to ensure elements can be found - @delegateEvents() - @trigger 'attach', this - 50 - this - - remove : -> - # @undelegateEvents() - @$el.remove() - return this unless @isAttached - @isAttached = false - @trigger 'unattach', this - this - - clear : -> - @remove() - @model.destroy() - @trigger 'clear', this - this - - hide : -> @$el.hide(); @trigger('hide', this); this - show : -> @$el.show(); @trigger('show', this); this - - /** - * Attach each subview to its bind-point. - * @returns {this} - */ - attachSubviews: -> - bps = @getOwnSubviewBindPoints() - if @subviews.length and not bps.length - console.warn "#this.attachSubviews(): no subview bind-points found!" - return this - for view of @subviews - if bp = @findSubviewBindPoint view, bps - view.attach bp - else - console.warn "#this.attachSubviews(): Unable to find bind-point for #view!" - this - - /** - * Finds all subview bind-points under this view's element, but not under - * the view element of any subview. - * @returns {jQuery|undefined} - */ - getOwnSubviewBindPoints: -> - @$ '[data-subview]' .not @$ '[data-subview] [data-subview]' - - /** - * Find the matching subview bind-point for the given view. - */ - findSubviewBindPoint: (view, bind_points) -> - bind_points or= @getOwnSubviewBindPoints() - - # check if any bindpoint specifies this subview by id - if view.id - bp = bind_points.filter "[data-subview$=':#{view.id}']" - return bp.eq 0 if bp.length - - # Find all elements that specify this type as the subview type - bp = bind_points.filter "[data-subview='#{view.getClassName()}']" - return bp.eq 0 if bp.length - - - ### }}} - ### Rendering Chain {{{ - - toTemplateLocals: -> - @model.toJSON() - - $template: -> - $ @template { _, op, @model, view:this, ...@toTemplateLocals() } - - build: -> - return this unless @template - outer = @$template() - @$el.html outer.html() - .attr do - id : outer.attr 'id' - class : outer.attr 'class' - @attachSubviews() - @isBuilt = true - this - - render: -> - @wait() - if @isBuilt - @update() - else - @build() - @renderSubviews() - @trigger 'render', this - @unwait() - this - - renderSubviews: -> - @attachSubviews() - @subviews.invoke 'render' - this - - update: -> - new DataBinding this .update locals = @toTemplateLocals() - @trigger 'update', this, locals - this - - - /* * * * Events * * * */ - - bubbleEventDown: (evt) -> - @invokeSubviews 'trigger', ...arguments - this - - redispatch: (evt, ...args) -> - @trigger evt, this, ...args - this - - onlyOnReturn: (fn, ...args) -> - fn = _.debounce fn.bind(this), 50 - (evt) ~> fn.apply this, args if evt.keyCode is 13 - - /** - * Call a delegate on keypress == the return key. - * @returns {Function} Keypress event handler. - */ - onReturnKeypress: (evt) -> - fn = this[@callOnReturnKeypress] if @callOnReturnKeypress - fn.call this if fn and evt.keyCode is 13 - - toString : -> - "#{@getClassName()}(model=#{@model})" - - -# Proxy model methods -<[ get set unset toJSON toKV toURL ]> - .forEach (methodname) -> - BaseView::[methodname] = -> @model[methodname].apply @model, arguments - -# }}} - - - -class exports.ViewList extends Array - - (views=[]) -> - super ... - - extend: (views) -> - _.each views, ~> @push it - this - - findByModel: (model) -> - @find -> it.model is model - - toString: -> - contents = if @length then "\"#{@join '","'}\"" else '' - "ViewList[#{@length}](#contents)" - - -<[ each contains invoke pluck find remove compact flatten without union intersection difference unique uniq ]> - .forEach (methodname) -> - ViewList::[methodname] = -> _[methodname].call _, this, ...arguments - - diff --git a/lib/base/base.co b/lib/base/base.co deleted file mode 100644 index 5d3f699..0000000 --- a/lib/base/base.co +++ /dev/null @@ -1,74 +0,0 @@ -{EventEmitter} = require 'events' -EventEmitter::off = EventEmitter::removeListener -EventEmitter::trigger = EventEmitter::emit - -{ _, op -} = require 'kraken/util' - - - -/** - * @class Eventful base class. - * @extends EventEmitter - */ -class Base extends EventEmitter - - /** - * 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 - */ - -> - @__class__ = @.. - @__superclass__ = @..superclass - @__apply_bind__() - super() - @__class__.emit 'new', this - - - ### Auto-Bound methods - - /** - * 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__: -> - names = _ @pluckSuperAndSelf '__bind__' .chain().flatten().compact().unique().value() - _.bindAll this, ...names if names.length - - - - getClassName: -> - "#{@..name or @..displayName}" - - toString: -> - "#{@getClassName()}()" - - - - ### Class Methods - - @extended = (Subclass) -> - # copy over all class methods, including this - for own k, v in this - Subclass[k] = v if typeof v is 'function' - Subclass.__super__ = @:: - Subclass - - - - -for k of <[ getSuperClasses pluckSuper pluckSuperAndSelf ]> - Base[k] = Base::[k] = _.methodize _[k] - -Base import EventEmitter:: - - -module.exports = Base diff --git a/lib/base/cascading-model.co b/lib/base/cascading-model.co deleted file mode 100644 index 7582621..0000000 --- a/lib/base/cascading-model.co +++ /dev/null @@ -1,57 +0,0 @@ -{ _, op, -} = require 'kraken/util' -{ BaseModel, BaseList, -} = require 'kraken/base/base-model' - -Cascade = require 'kraken/util/cascade' - - - -/** - * @class A model that implements cascading lookups for its attributes. - */ -CascadingModel = exports.CascadingModel = BaseModel.extend do # {{{ - /** - * The lookup cascade. - * @type Cascade - */ - cascade : null - - - constructor: function CascadingModel (attributes={}, opts) - @cascade = new Cascade attributes - BaseModel.call this, attributes, opts - - initialize: -> - BaseModel::initialize ... - - - /** - * Recursively look up a (potenitally nested) attribute in the lookup chain. - * @param {String} key Attribute key (potenitally nested using dot-delimited subkeys). - * @returns {*} - */ - get: (key) -> - @cascade.get key - - - toJSON: (opts={}) -> - opts = {-collapseCascade, ...opts} - if opts.collapseCascade - @cascade.collapse() - else - BaseModel::toJSON ... - - - -# Proxy Cascade methods -<[ - addLookup removeLookup popLookup shiftLookup unshiftLookup - isOwnProperty isOwnValue isInheritedValue isModifiedValue -]>.forEach (methodname) -> - CascadingModel::[methodname] = -> @cascade[methodname].apply @cascade, arguments - -# }}} - - - diff --git a/lib/base/data-binding.co b/lib/base/data-binding.co deleted file mode 100644 index 4b2fa58..0000000 --- a/lib/base/data-binding.co +++ /dev/null @@ -1,67 +0,0 @@ -Backbone = require 'backbone' - -{ _, op, -} = require 'kraken/util' - - -class exports.DataBinding - - data : null - context : null - el : null - $el : null - bindPoints: null - - - - (el, @context=el) -> - if el instanceof Backbone.View - el = el.$el - @$el = $ el - @el = @$el.get 0 - - # Find all bind-points under this element, but not under a subview - @bindPoints = @$ '[data-bind], [name]' .not @$('[data-subview]').find('[data-bind], [name]') - - $: (sel) -> - @$el.find sel - - serialize: -> - it - - update: (@data) -> - for key, val in _.collapseObject(@data) - @updateBinding key, val - this - - updateBinding: (key, val) -> - # if val and _.isPlainObject val - # for k, v in val - # @updateBinding "#key.#k", v - # return this - - if bp = @findDataBindPoint key - if _.isFunction val - val.call @context, val, key, bp, @data - else if bp.is 'input:checkbox' - bp.attr 'checked', !!val - else if bp.is 'input, textarea' - bp.val @serialize val - else - if op.toBool bp.data 'data-bind-escape' - bp.text @serialize val - else - bp.html @serialize val - else - false and console.warn "#this.updateBinding(): Unable to find data bind-point for #key=#val!" - this - - findDataBindPoint: (key) -> - bp = @bindPoints.filter "[name='#key'], [data-bind='#key']" - return bp.eq(0) if bp.length - - - - - - diff --git a/lib/base/index.co b/lib/base/index.co deleted file mode 100644 index 17b6726..0000000 --- a/lib/base/index.co +++ /dev/null @@ -1,9 +0,0 @@ -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' -exports import mixins import models import views \ - import cache import cascading import data_binding diff --git a/lib/base/model-cache.co b/lib/base/model-cache.co deleted file mode 100644 index f636a7d..0000000 --- a/lib/base/model-cache.co +++ /dev/null @@ -1,196 +0,0 @@ -_ = require 'underscore' -Seq = require 'seq' - -{ReadyEmitter} = require 'kraken/util/event' - - -# TODO: Bubble events to decorated emitters -# TODO: Automatically create a cache for any class that extends BaseModel -/** - * @class Caches models and provides static lookups by ID. - */ -class exports.ModelCache extends ReadyEmitter - /** - * @see ReadyEmitter#readyEventName - * @private - * @constant - * @type String - */ - readyEventName : 'cache-ready' - - /** - * Default options. - * @private - * @constant - * @type Object - */ - DEFAULT_OPTIONS: - ready : true - cache : null - create : null - ModelType : null - - /** - * @private - * @type Object - */ - 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 - */ - ModelType : null - - /** - * Collection holding the cached Models. - * @private - * @type Backbone.Collection - */ - 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. - */ - (ModelType, options) -> - unless _.isFunction ModelType - [options, ModelType] = [ModelType or {}, null] - @options = {...@DEFAULT_OPTIONS, ...options} - - @cache = @options.cache or new Backbone.Collection - - @ModelType = ModelType or @options.ModelType - @createModel = that if @options.create - - @ready = !!@options.ready - @decorate @ModelType if @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. - */ - createModel: (id) -> - new @ModelType {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. - */ - register: (model) -> - # console.log "ModelCache(#{@CACHE}).register(#{model.id or model.get('id')})", model - if @cache.contains model - @cache.remove model, {+silent} - @cache.add model - @trigger 'add', this, model - model - - /** - * Synchronously check if a model is in the cache, returning it if so. - * - * @param {String} id The model ID to get. - * @returns {Model} - */ - get: (id) -> - @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} - */ - lookupAll: (ids, cb, cxt=this) -> - ids = [ids] unless _.isArray ids - # console.log "ModelCache(#{@cache}).lookup([#ids], #{typeof cb})" - - unless @ready - @on 'cache-ready', ~> - @off 'cache-ready', arguments.callee - @lookupAll ids, cb, cxt - return this - - Seq ids - .parMap_ (next, id) ~> - return next.ok(that) if @cache.get id - @register @createModel id - .on 'ready', -> next.ok it - .load() - .unflatten() - .seq (models) -> - cb.call cxt, null, models - .catch (err) -> - cb.call cxt, err - 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} - */ - lookup: (id, cb, cxt=this) -> - @lookupAll [id], (err, models) -> - if err then cb.call cxt, err - else 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. - */ - decorate: (obj) -> - obj.__cache__ = this - # Bind the ModelCache methods to the class - for m of <[ register get lookup lookupAll ]> - obj[m] = @[m].bind this - obj - - toString: -> - "#{@..displayName or @..name}(cache=#{@cache})" - - diff --git a/lib/base/scaffold/index.co b/lib/base/scaffold/index.co deleted file mode 100644 index 6524ae6..0000000 --- a/lib/base/scaffold/index.co +++ /dev/null @@ -1,3 +0,0 @@ -models = require 'kraken/base/scaffold/scaffold-model' -views = require 'kraken/base/scaffold/scaffold-view' -exports import models import views diff --git a/lib/base/scaffold/scaffold-model.co b/lib/base/scaffold/scaffold-model.co deleted file mode 100644 index 2800782..0000000 --- a/lib/base/scaffold/scaffold-model.co +++ /dev/null @@ -1,105 +0,0 @@ -_ = require 'kraken/util/underscore' -op = require 'kraken/util/op' -{ BaseModel, BaseList, -} = require 'kraken/base' - - - -### Scaffold Models - -Field = exports.Field = BaseModel.extend do # {{{ - valueAttribute : 'value' - - defaults: -> - name : '' - type : 'String' - default : null - desc : '' - include : 'diff' - tags : [] - examples : [] - - - - constructor: function Field - BaseModel ... - - initialize: -> - _.bindAll this, ...(_.functions this .filter -> _.startsWith(it, 'parse')) - @set 'id', @id = _.camelize @get 'name' - @set 'value', @get('default'), {+silent} if not @has 'value' - Field.__super__.initialize ... - - - - - - - /* * * Value Accessors * * */ - - getValue: (def) -> - @getParser() @get @valueAttribute, def - - setValue: (v, options) -> - def = @get 'default' - if not v and def == null - val = null - else - val = @getParser()(v) - @set @valueAttribute, val, options - - clearValue: -> - @set @valueAttribute, @get 'default' - - isDefault: -> - @get(@valueAttribute) is @get 'default' - - - /* * * Serializers * * */ - - serializeValue: -> - @serialize @getValue() - - toJSON: -> - {id:@id} import do - _.clone(@attributes) import { value:@getValue(), def:@get 'default' } - - toKVPairs: -> - { "#{@id}":@serializeValue() } - - toString: -> "(#{@id}: #{@serializeValue()})" - -# }}} - - -FieldList = exports.FieldList = BaseList.extend do # {{{ - model : Field - - constructor: function FieldList - BaseList ... - - - /** - * Collects a map of fields to their values, excluding those set to `null` or their default. - * @returns {Object} - */ - values: (opts={}) -> - opts = {+keepDefaults, -serialize} import opts - _.synthesize do - if opts.keepDefaults then @models else @models.filter -> not it.isDefault() - -> [ it.get('name'), if opts.serialize then it.serializeValue() else it.getValue() ] - - toJSON: -> - @values {+keepDefaults, -serialize} - - toKVPairs: -> - _.collapseObject @values {+keepDefaults, +serialize} - - toKV: (item_delim='&', kv_delim='=') -> - _.toKV @toKVPairs(), item_delim, kv_delim - - toURL: (item_delim='&', kv_delim='=') -> - "?#{@toKV ...}" - -# }}} - diff --git a/lib/base/scaffold/scaffold-view.co b/lib/base/scaffold/scaffold-view.co deleted file mode 100644 index b5299bc..0000000 --- a/lib/base/scaffold/scaffold-view.co +++ /dev/null @@ -1,125 +0,0 @@ -_ = require 'kraken/util/underscore' -op = require 'kraken/util/op' -{ BaseView, -} = require 'kraken/base' -{ Field, FieldList, -} = require 'kraken/base/scaffold/scaffold-model' - - -FieldView = exports.FieldView = BaseView.extend do # {{{ - tagName : 'div' - className : 'field' - - type : 'string' - - events : - 'blur .value' : 'onChange' - 'submit .value' : 'onChange' - - - constructor: function FieldView - BaseView ... - - initialize: -> - # console.log "#this.initialize!" - BaseView::initialize ... - @type = @model.get 'type' .toLowerCase() or 'string' - - onChange: -> - if @type is 'boolean' - val = !! @$('.value').attr('checked') - else - val = @model.getParser() @$('.value').val() - - current = @model.getValue() - return if _.isEqual val, current - # console.log "#this.onChange( #current -> #val )" - @model.setValue val, {+silent} - @trigger 'change', this - - toTemplateLocals: -> - json = FieldView.__super__.toTemplateLocals ... - json.id or= _.camelize json.name - json.value ?= '' - json.value = JSON.stringify v if v = json.value and (_.isArray(v) or _.isPlainObject(v)) - json - - /** - * A ghetto default template, typically overridden by superclass. - */ - template: (locals) -> - $ """ - - - """ - - render: -> - return @remove() if @model.get 'ignore' - FieldView.__super__.render ... - -# }}} - - -# There are several special options that, if passed, will be attached directly to the view: -# model, collection, el, id, className, tagName, attributes - -Scaffold = exports.Scaffold = BaseView.extend do # {{{ - __bind__ : <[ addField resetFields ]> - tagName : 'form' - className : 'scaffold' - - collectionType : FieldList - subviewType : FieldView - - - - constructor: function Scaffold - BaseView ... - - initialize: -> - CollectionType = @collectionType - @model = (@collection or= new CollectionType) - BaseView::initialize ... - - @collection.on 'add', @addField, this - @collection.on 'reset', @resetFields, this - - - - addField: (field) -> - @removeSubview field.view if field.view - - # avoid duplicating event propagation - field.off 'change:value', @onChange, this - - # propagate value-change events as key-value change events - field.on 'change:value', @onChange, this - - SubviewType = @subviewType - view = @addSubview new SubviewType model:field - view.on 'change', @onChange.bind(this, field) - - @render() - view - - resetFields: -> - @removeAllSubviews() - @collection.each @addField - this - - onChange: (field) -> - key = field.get 'name' - value = field.getValue() - @trigger "change:#key", this, value, key, field - @trigger "change", this, value, key, field - this - - - -# Proxy collection methods -<[ get at pluck invoke values toJSON toKVPairs toKV toURL ]> - .forEach (methodname) -> - Scaffold::[methodname] = -> @collection[methodname].apply @collection, arguments - -# }}} - diff --git a/lib/chart/chart-type.co b/lib/chart/chart-type.co deleted file mode 100644 index 2840b9d..0000000 --- a/lib/chart/chart-type.co +++ /dev/null @@ -1,425 +0,0 @@ -moment = require 'moment' -Backbone = require 'backbone' - -{ _, op, -} = require 'kraken/util' -{ ReadyEmitter, -} = require 'kraken/util/event' -{ Parsers, ParserMixin, -} = require 'kraken/util/parser' - - - -/** - * Map of known libraries by name. - * @type Object - */ -KNOWN_CHART_TYPES = exports.KNOWN_CHART_TYPES = {} - - -/** - * @class Abstraction of a chart-type or charting library, encapsulating its - * logic and options. In addition, a `ChartType` also mediates the - * transformation of the domain-specific data types (the model and its view) - * with its specific needs. - * - * `ChartType`s mix in `ParserMixin`: when implementing a `ChartType`, you can - * add or supplement parsers merely by subclassing and overriding the - * corresponding `parseXXX` method (such as `parseArray` or `parseDate`). - * - * @extends EventEmitter - * @borrows ParserMixin - */ -class exports.ChartType extends ReadyEmitter - - ### Class Methods - - /** - * Register a new chart type. - */ - @register = (Subclass) -> - # console.log "ChartType.register(#Subclass)" - KNOWN_CHART_TYPES[ Subclass::typeName ] = Subclass - - /** - * Look up a `ChartType` by `typeName`. - */ - @lookup = (name) -> - name = name.get('chartType') if name instanceof Backbone.Model - KNOWN_CHART_TYPES[name] - - /** - * Look up a chart type by name, returning a new instance - * with the given model (and, optionally, view). - * @returns {ChartType} - */ - @create = (model, view) -> - # console.log "ChartType.create(#model) ->", model - return null unless Type = @lookup model - new Type model, view - - - ### Class Properties - /* - * These are "class properties": each is set on the prototype at the class-level, - * and the reference is therefore shared by all instances. It is expected you - * will not modify this on the instance-level. - */ - - /** - * URL for the Chart Spec JSON. Loaded once, the first time an instance of - * that class is created. - * @type String - * @readonly - */ - SPEC_URL : null - - /** - * Chart-type name. - * @type String - * @readonly - */ - typeName: null - - /** - * Map of option name to ChartOption objects. - * @type { name:ChartOption, ... } - * @readonly - */ - options : null - - /** - * Ordered ChartOption objects. - * - * This is a "class-property": it is set on the prototype at the class-level, - * and the reference is shared by all instances. It is expected you will not - * modify that instance. - * - * @type ChartOption[] - * @readonly - */ - options_ordered : null - - /** - * Hash of role-names to the selector which, when applied to the view, - * returns the correct element. - * @type Object - */ - roles : - viewport : '.viewport' - - /** - * Whether the ChartType has loaded all its data and is ready. - * @type Boolean - */ - ready: false - - - - ### Instance properties - - /** - * Model to be rendered as a chart. - * @type Backbone.Model - */ - model : null - - /** - * View to render the chart into. - * @type Backbone.View - */ - view : null - - /** - * Last chart rendered by this ChartType. - * @private - */ - chart: null - - - - - /** - * @constructor - */ - (@model, @view) -> - @roles or= {} - _.bindAll this, ...@__bind__ # TODO: roll up MRO - @loadSpec() unless @ready - - - # Builder Pattern - withModel : (@model) -> this - withView : (@view) -> this - - - /** - * Load the corresponding chart specification, which includes - * info about valid options, along with their types and defaults. - */ - loadSpec: -> - return this if @ready - proto = @constructor:: - jQuery.ajax do - url : @SPEC_URL - dataType : 'json' - success : (spec) ~> - proto.spec = spec - proto.options_ordered = spec - proto.options = _.synthesize spec, -> [it.name, it] - proto.ready = true - @triggerReady() - error: ~> console.error "Error loading #{@typeName} spec! #it" - this - - - /** - * @returns {ChartOption} Get an option's spec by name. - */ - getOption: (name, def) -> - @options[name] or def - - - /** - * @returns {Object} An object, mapping from option.name to the - * result of the supplied function. - */ - map: (fn, context=this) -> - _.synthesize @options, ~> [it.name, fn.call(context, it, it.name, this)] - - - /** - * @param {String} attr Attribute to look up on each options object. - * @returns {Object} Map from name to the value found at the given attr. - */ - pluck: (attr) -> - @map -> it[attr] - - - /** - * @returns {Boolean} Whether the supplied value is the same as - * the default value for the given key. - */ - isDefault: (name, value) -> - _.isEqual @getOption(name).default, value - - - ### }}} - ### Parsers & Serialization {{{ - - /** - * When implementing a ChartType, you can add or override parsers - * merely by subclassing. - * @borrows ParserMixin - */ - ParserMixin.mix this - - /** - * @returns {Function} Parser for the given option name. - */ - getParserFor: (name) -> - @getParser @getOption(name).type - - /** - * Parses a single serialized option value into its proper type. - * - * @param {String} name Option-name of the value being parsed. - * @param {String} value Value to parse. - * @returns {*} Parsed value. - */ - parseOption: (name, value) -> - @getParserFor(name)(value) - - /** - * Parses options using `parseOption(name, value)`. - * - * @param {Object} options Options to parse. - * @returns {Object} Parsed options. - */ - parseOptions: (options) -> - out = {} - for k, v in options - out[k] = @parseOption k, v - out - - - /** - * Serializes option-value to a String. - * - * @param {*} v Value to serialize. - * @param {String} k Option-name of the given value. - * @returns {String} The serialized value - */ - serialize: (v, k) -> - # if v!? - # v = '' - if _.isBoolean v - v = Number v - else if _.isObject v - v = JSON.stringify v - String v - - - ### }}} - ### Formatters {{{ - - /** - * Formats a date for display on an axis: `MM/YYYY` - * @param {Date} d Date to format. - * @returns {String} - */ - axisDateFormatter: (d) -> - moment(d).format 'MM/YYYY' - - /** - * Formats a date for display in the legend: `DD MMM YYYY` - * @param {Date} d Date to format. - * @returns {String} - */ - dateFormatter: (d) -> - moment(d).format 'DD MMM YYYY' - - /** - * Formats a number for display, first dividing by the greatest suffix - * of {B = Billions, M = Millions, K = Thousands} that results in a - * absolute value greater than 0, and then rounding to `digits` using - * `result.toFixed(digits)`. - * - * @param {Number} n Number to format. - * @param {Number} [digits=2] Number of digits after the decimal to always display. - * @param {Boolean} [abbrev=true] Expand number suffixes if false. - * @returns {Object} Formatted number parts. - */ - numberFormatter: (n, digits=2, abbrev=true) -> - suffixes = do - if abbrev - [['B', 1000000000], ['M', 1000000], ['K', 1000], ['', NaN]] - else - [['Billion', 1000000000], ['Million', 1000000], ['', NaN]] - - for [suffix, d] of suffixes - break if isNaN d - if n >= d - n = n / d - break - s = n.toFixed(digits) - parts = s.split '.' - whole = _.rchop parts[0], 3 .join ',' - fraction = '.' + parts.slice(1).join '.' - { n, digits, whole, fraction, suffix, toString: -> - "#{@whole}#{@fraction}#{if abbrev then '' else ' '}#{@suffix}" - } - - - ### }}} - ### Rendering {{{ - - /** - * Finds the element in the view which plays the given role in the chart. - * Canonically, all charts have a "viewport" element. Other roles might - * include a "legend" element, or several "axis" elements. - * - * Default implementation looks up a selector in the `roles` hash, and if - * found, queries the view for matching children. - * - * @param {String} role Name of the role to look up. - * @returns {jQuery|null} $-wrapped DOM element. - */ - getElementsForRole: (role) -> - return null unless @view - if @roles[role] - @view.$ that - else - null - - - /** - * Transform/extract the data for this chart from the model. Default - * implementation calls `model.getData()`. - * - * @returns {*} Data object for the chart. - */ - getData: -> - @model.getData() - - - /** - * Map from option-name to default value. Note that this reference will be - * modified by `.render()`. - * - * @returns {Object} Default options. - */ - getDefaultOptions: -> - @pluck 'default' - - - - - /** - * Resizes the HTML viewport. Override to disable, etc. - */ - resizeViewport: -> - size = @determineSize() - @getElementsForRole 'viewport' .css size - size - - /** - * Determines chart viewport size. - * @return { width, height } - */ - determineSize: -> - modelW = width = @model.get 'width' - modelH = height = @model.get 'height' - return { width, height } unless @view.ready and width and height - - viewport = @getElementsForRole 'viewport' - - if width is 'auto' - Width = viewport.innerWidth() or 300 - width ?= modelW - - if height is 'auto' - height = viewport.innerHeight() or 320 - height ?= modelH - - { width, height } - - - /** - * Transforms domain data and applies it to the chart library to - * render or update the corresponding chart. - * - * @returns {Chart} - */ - render: -> - data = @getData() - options = @getDefaultOptions() import @transform @model, @view - viewport = @getElementsForRole 'viewport' - return @lastChart unless data?.length and viewport?.length - @lastChart = @renderChart data, viewport, options, @chart - - - /** - * Transforms the domain objects into a hash of derived values using - * chart-type-specific keys. - * - * Default implementation returns `model.getOptions()`. - * - * @returns {Object} The derived data. - */ - transform: (model, view) -> - @model.getOptions() - - - /** - * Called to render the chart. - * - * @abstract - * @returns {Chart} - */ - renderChart: (data, viewport, options, lastChart) -> - ... - - - ### }}} - diff --git a/lib/chart/index.co b/lib/chart/index.co deleted file mode 100644 index c5fd1e2..0000000 --- a/lib/chart/index.co +++ /dev/null @@ -1,8 +0,0 @@ -chart_type = require 'kraken/chart/chart-type' -chart_option = require 'kraken/chart/option' -dygraphs = require 'kraken/chart/type/dygraphs' -d3_chart = require 'kraken/chart/type/d3-chart' -d3_elements = require 'kraken/chart/type/d3' - -exports import chart_type import chart_option \ - import dygraphs import d3_chart import d3_elements diff --git a/lib/chart/option/chart-option-model.co b/lib/chart/option/chart-option-model.co deleted file mode 100644 index 2a3c37a..0000000 --- a/lib/chart/option/chart-option-model.co +++ /dev/null @@ -1,218 +0,0 @@ -{ _, op, -} = require 'kraken/util' -{ Parsers, ParserMixin, ParsingModel, ParsingView, -} = require 'kraken/util/parser' -{ BaseModel, BaseList, -} = require 'kraken/base' - - -/** - * @class A set of tags. - */ -class exports.TagSet extends Array - tags : {} - - (values=[]) -> - @tags = {} - @add values if values?.length - - has: (tag) -> - @tags[tag]? - - get: (tag) -> - return -1 unless tag - unless @tags[tag]? - @tags[tag] = @length - @push tag - @tags[tag] - - update: (tags) -> - is_single = typeof tags is 'string' - tags = [tags] if is_single - indices = ( for tag of tags then @get tag ) - if is_single then indices[0] else indices - - toString: -> "TagSet(length=#{@length}, values=[\"#{@join '", "'}\"])" - - - -/** - * @namespace All known tags, for mapping consistently onto colors. - */ -KNOWN_TAGS = exports.KNOWN_TAGS = new TagSet() - - - -/** - * @class Field with chart-option-specific handling for validation, parsing, tags, etc. - */ -ChartOption = exports.ChartOption = ParsingModel.extend do # {{{ - IGNORED_TAGS : <[ callback deprecated debugging ]> - valueAttribute : 'value' - - defaults: -> - name : '' - type : 'String' - default : null - desc : '' - include : 'diff' - tags : [] - examples : [] - - - - constructor: function ChartOption - ParsingModel ... - - initialize : -> - # console.log "#this.initialize!" - - # Bind all the `parseXXX()` methods so they can be passed about independent from the class - _.bindAll this, ...(_.functions this .filter -> _.startsWith(it, 'parse')) - - ChartOption.__super__.initialize ... - @set 'id', @id = _.camelize @get 'name' - @set 'value', @get('default'), {+silent} if not @has 'value' - - - # Notify Tag indexer of category when created, to ensure all category-tags - # get indices with colors :P - KNOWN_TAGS.update @getCategory() - - # Ignore functions/callbacks and, ahem, hidden tags. - type = @get 'type' .toLowerCase() or '' - tags = @get('tags') or [] - if _.str.include(type, 'function') or _.intersection(tags, @IGNORED_TAGS).length - @set 'ignore', true - - - - ### Tag Handling - - # Wrapper to ensure @set('tags') is called, as tags.push() - # will not trigger the 'changed:tags' event. - addTag: (tag) -> - return this unless tag - tags = @get('tags') or [] - tags.push tag - @set 'tags', tags - this - - # Wrapper to ensure @set('tags') is called, as tags.push() - # will not trigger the 'changed:tags' event. - removeTag: (tag) -> - return this unless tag - tags = @get('tags') or [] - _.remove tags, tag - @set 'tags', tags - this - - # Keep tag list up to date - onTagUpdate: -> - KNOWN_TAGS.update @get 'tags' - this - - getTagIndex: (tag) -> - KNOWN_TAGS.get tag - - # A field's category is its first tag. - getCategory: -> - tags = (@get('tags') or [])[0] - - getCategoryIndex: -> - @getTagIndex @getCategory() - - - - /* * * Value Accessors * * */ - - getValue: (def) -> - @getParser() @get @valueAttribute, def - - setValue: (v, options) -> - def = @get 'default' - if not v and def == null - val = null - else - val = @getParser()(v) - @set @valueAttribute, val, options - - clearValue: -> - @set @valueAttribute, @get 'default' - - isDefault: -> - @get(@valueAttribute) is @get 'default' - - - - /* * * Serialization * * */ - - /** - * Override to default `type` to the model attribute of the same name. - * @returns {Function} Parser for the given type. - */ - getParser: (type) -> - type or= @get('type') or 'String' - ChartOption.__super__.getParser.call this, type - - serializeValue: -> - @serialize @getValue() - - toJSON: -> - {id:@id} import do - _.clone(@attributes) import { value:@getValue(), def:@get 'default' } - - toKVPairs: -> - { "#{@id}":@serializeValue() } - - toString: -> "(#{@id}: #{@serializeValue()})" - -# }}} - - - -/** - * @class List of ChartOption fields. - */ -ChartOptionList = exports.ChartOptionList = BaseList.extend do # {{{ - model : ChartOption - - - constructor: function ChartOptionList - BaseList ... - - - /** - * Collects a map of fields to their values, excluding those set to `null` or their default. - * - * @param {Object} [opts={}] Options: - * @param {Boolean} [opts.keepDefaults=true] If false, exclude pairs that - * haven't changed from their default value. - * @param {Boolean} [opts.serialize=false] If true, replace each value - * with its String version by calling `value.serializeValue()`. - * @returns {Object} Map of fields to their values. - */ - values: (opts={}) -> - opts = {+keepDefaults, -serialize} import opts - _.synthesize do - if opts.keepDefaults then @models else @models.filter -> not it.isDefault() - -> [ it.get('name'), if opts.serialize then it.serializeValue() else it.getValue() ] - - toJSON: -> - @values {+keepDefaults, -serialize} - - /** - * Override to omit defaults from URL. - */ - toKVPairs: -> - _.collapseObject @values {-keepDefaults, +serialize} - - toKV: (item_delim='&', kv_delim='=') -> - _.toKV @toKVPairs(), item_delim, kv_delim - - toURL: (item_delim='&', kv_delim='=') -> - "?#{@toKV ...}" - - -# }}} - diff --git a/lib/chart/option/chart-option-view.co b/lib/chart/option/chart-option-view.co deleted file mode 100644 index 1b004e1..0000000 --- a/lib/chart/option/chart-option-view.co +++ /dev/null @@ -1,270 +0,0 @@ -{ _, op, -} = require 'kraken/util' -{ BaseView, -} = require 'kraken/base' -{ ChartOption, ChartOptionList, -} = require 'kraken/chart/option/chart-option-model' - -DEBOUNCE_RENDER = exports.DEBOUNCE_RENDER = 100ms - - -/** - * @class View for a single configurable option in a chart type. - */ -ChartOptionView = exports.ChartOptionView = BaseView.extend do # {{{ - tagName : 'section' - className : 'chart-option field' - template : require 'kraken/template/chart/chart-option' - - type : 'string' - isCollapsed : true - - events : - 'blur .value' : 'onChange' - 'click input[type="checkbox"].value' : 'onChange' - 'submit .value' : 'onChange' - 'click .close' : 'toggleCollapsed' - 'click h3' : 'toggleCollapsed' - 'click .collapsed' : 'onClick' - - - - constructor: function ChartOptionView - BaseView ... - - initialize: -> - ChartOptionView.__super__.initialize ... - @type = @model.get 'type' .toLowerCase() or 'string' - - - /* * * * Rendering * * * */ - - toTemplateLocals: -> - json = ChartOptionView.__super__.toTemplateLocals ... - json.id or= _.camelize json.name - json.value ?= '' - v = json.value - json.value = JSON.stringify(v) if v and ( _.isArray(v) or _.isPlainObject(v) ) - json - - /** - * Override to annotate with collapsed state and to kill off ignored options - * so they do not contribute their values when looking at form updates. - */ - render: -> - return @remove() if @model.get 'ignore' - ChartOptionView.__super__.render ... - @$el.addClass 'collapsed' if @isCollapsed - this - - - - /* * * * Option Collapsing * * * */ - - /** - * Sets the state of `isCollapsed` and updates the UI. If the state changed, - * a `'change:collapse`` event will be fired.` - * - * @param {Boolean} [makeCollapsed=true] If true, set state to collapsed. - * @returns {Boolean} Whether the state changed. - */ - collapse: (state=true) -> - state = !! state - @isCollapsed = @$el.hasClass 'collapsed' - - return this if state is @isCollapsed - if state - @$el.addClass 'collapsed' - else - @$el.removeClass 'collapsed' - @isCollapsed = state - @trigger 'change:collapse', this, @isCollapsed - true - - /** - * Toggles the collapsed state, updating the UI and firing a `'change:collapse'` event. - * @returns {this} - */ - toggleCollapsed: -> - @collapse not @$el.hasClass 'collapsed' - this - - - - /* * * * Events * * * */ - - /** - * To prevent `toggleCollapsed()` from being called multiple times due to - * overlapping listeners, we're only looking for clicks on the collapsed header. - */ - onClick: (evt) -> - target = $ evt.target - @toggleCollapsed() if @$el.hasClass('collapsed') and not target.hasClass('close') - - /** - * Propagate user input changes to the model, and upward to the parent view. - */ - onChange: -> - if @type is 'boolean' - val = !! @$('.value').attr('checked') - else - val = @model.getParser() @$('.value').val() - - current = @model.getValue() - return if _.isEqual val, current - console.log "#this.onChange( #current -> #val )" - @model.setValue val, {+silent} - @trigger 'change', @model, this - # false - - -# }}} - - - -/** - * @class View for configuring a chart type. - */ -ChartOptionScaffold = exports.ChartOptionScaffold = BaseView.extend do # {{{ - __bind__ : <[ addField ]> - tagName : 'form' - className : 'chart-options scaffold' - template : require 'kraken/template/chart/chart-scaffold' - - collectionType : ChartOptionList - subviewType : ChartOptionView - - events: - 'click .options-filter-button' : 'onFilterOptions' - 'click .collapse-all-options-button' : 'collapseAll' - 'click .expand-all-options-button' : 'expandAll' - - - - - constructor: function ChartOptionScaffold - BaseView ... - - initialize : -> - @render = _.debounce @render.bind(this), DEBOUNCE_RENDER - CollectionType = @collectionType - @model = (@collection or= new CollectionType) - ChartOptionScaffold.__super__.initialize ... - - @collection.on 'add', @addField, this - @collection.on 'reset', @onReset, this - @on 'render', @onRender, this - - - /** - * Bookkeeping for new ChartOptions, creating it a new subview and subscribing - * to its activity, and then rendering it. - * @returns {ChartOptionView} The Option's new view. - */ - addField: (field) -> - @removeSubview field.view if field.view - - # avoid duplicating event propagation - field.off 'change:value', @onChange, this - - # propagate value-change events as key-value change events - field.on 'change:value', @onChange, this - - SubviewType = @subviewType - @addSubview view = new SubviewType model:field - .on 'change', @onChange.bind(this, field) - .on 'change:collapse', @render, this - - @render() # WTF: hmm. - view - - - /* * * * UI * * * */ - - /** - * Collapse all expanded subviews. - * @returns {false} Returns false so event-dispatchers don't propagate - * the triggering event (usually a click or submit). - */ - collapseAll: -> - _.invoke @subviews, 'collapse', true - false - - /** - * Expand all collapsed subviews. - * @returns {false} Returns false so event-dispatchers don't propagate - * the triggering event (usually a click or submit). - */ - expandAll: -> - _.invoke @subviews, 'collapse', false - false - - /** - * Reflow Isotope post-`render()`. - */ - onRender: -> - # console.log "#this.onRender(ready=#{@ready}) -> .isotope()" - - # The DOM doesn't calculate dimensions of elements that are not visible, - # which makes it impossible for Isotope to do its job. - return unless @$el.is ':visible' - - # Invoke Isotope to re-layout the option elements - @$ '.isotope' .isotope do - # itemPositionDataEnabled : true - itemSelector : '.chart-option.field' - layoutMode : 'masonry' - masonry : { columnWidth:10 } - filter : @getOptionsFilter() - sortBy : 'category' - getSortData : - category: ($el) -> - $el.data 'model' .getCategory() - - /** - * @returns {String} Selector representing the selected set of Option filters. - */ - getOptionsFilter: -> - data = @$ '.options-filter-button.active' .toArray().map -> $ it .data() - sel = data.reduce do - (sel, d) -> sel += if d.filter then that else '' - ':not(.ignore)' - sel - - - - /* * * * Events * * * */ - - /** - * Propagate change events from fields as if they were attribute changes. - * Note: `field` is bound to the handler - */ - onChange: (field) -> - key = field.get 'name' - value = field.getValue() - @trigger "change:#key", this, value, key, field - @trigger "change", this, value, key, field - this - - onReset: -> - # The collection has been reset, assume all subviews are - # invalid and rebuild them. - @removeAllSubviews() - @collection.each @addField - _.defer @render - - onFilterOptions: (evt) -> - evt.preventDefault() - # Defer re-rendering until after we yield for the DOM to do its thang - _.defer @render - - -# Proxy collection methods -<[ get at pluck invoke values toJSON toKVPairs toKV toURL ]> - .forEach (methodname) -> - ChartOptionScaffold::[methodname] = -> @collection[methodname].apply @collection, arguments - - -# }}} - - diff --git a/lib/chart/option/index.co b/lib/chart/option/index.co deleted file mode 100644 index 5cdea4d..0000000 --- a/lib/chart/option/index.co +++ /dev/null @@ -1,4 +0,0 @@ -model = require 'kraken/chart/option/chart-option-model' -view = require 'kraken/chart/option/chart-option-view' - -exports import model import view diff --git a/lib/chart/type/d3-chart.co b/lib/chart/type/d3-chart.co deleted file mode 100644 index 02c2c34..0000000 --- a/lib/chart/type/d3-chart.co +++ /dev/null @@ -1,138 +0,0 @@ -d3 = require 'd3' -ColorBrewer = require 'colorbrewer' - -{ _, op, -} = require 'kraken/util' -{ ChartType, -} = require 'kraken/chart/chart-type' -{ D3ChartElement, -} = require 'kraken/chart/type/d3/d3-chart-element' - - -root = do -> this - - -class exports.D3ChartType extends ChartType - __bind__ : <[ determineSize ]> - SPEC_URL : '/schema/d3/d3-chart.json' - - # NOTE: ChartType.register() must come AFTER `typeName` declaration. - typeName : 'd3-chart' - ChartType.register this - - - /** - * Hash of role-names to the selector which, when applied to the view, - * returns the correct element. - * @type Object - */ - roles : - viewport : '.viewport' - legend : '.graph-legend' - - - - -> super ... - - - getData: -> - @model.dataset.getColumns() - - - transform: -> - dataset = @model.dataset - options = @model.getOptions() import @determineSize() - options import do - colors : dataset.getColors() - labels : dataset.getLabels() - options - - - renderChart: (data, viewport, options, lastChart) -> - ### Starting with http://bost.ocks.org/mike/chart/ - - - # margin convention http://bl.ocks.org/3019563 - margin = {top: 20, right: 20, bottom: 20, left: 20} - width = 760 - margin.left - margin.right - height = 320 - margin.top - margin.bottom - - xScale = d3.time.scale() - yScale = d3.scale.linear() - - dates = data[0] - cols = data.slice(1) - - # Calculate extents using all the data points (but not dates) - # allValues = d3.merge @model.dataset.getDataColumns() - allValues = d3.merge cols - - # Update the x-scale with the extents of the dates. - xScale - .domain d3.extent dates - .range [ 0, width ] - - # Update the y-scale with the extents of the data. - yScale - .domain d3.extent allValues - .range [ height, 0 ] - - - # Hack. Remove svg if it exists. If @model changes, the graph will be redrawn - svg = d3.select viewport.0 .selectAll "svg" - .remove() - # Select the svg element, if it exists. - svg = d3.select viewport.0 .selectAll "svg" - .data [cols] - # ...Otherwise, create the skeletal chart. - enterFrame = svg.enter() - .append "svg" .append "g" - .attr "class", "frame" - - # Update chart dimensions. - svg .attr "width", width + margin.left + margin.right - .attr "height", height + margin.top + margin.bottom - - frame = svg.select "g.frame" - .attr "transform", "translate(#{margin.left},#{margin.top})" - .attr "width", width - .attr "height", height - - - - # x-axis. - # TODO move axis to separate chart-type - enterFrame.append "g" - .attr "class", "x axis time" - - - xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 0) - frame.select ".x.axis.time" - .attr "transform", "translate(0,#{yScale.range()[0]})" - .call xAxis - - - metrics = frame.selectAll "metric" - .data @model.dataset.metrics.models - - metrics.enter() - .append("g") - .attr "class", (d) -> - "g metric line "+d.get 'label' - .each (d) -> - # console.log d - # metric defined charttype - chartElement = d.get "chartElement" - # otherwise the graph defined charttype - # FOR NOW take line as default - chartElement ?= 'd3-line' # @model.get "chartElement" - # create d3 chart element and render it - chEl = D3ChartElement.create chartElement - - chEl.renderChartElement d, frame ,xScale, yScale - - metrics.exit().remove() - - svg - - diff --git a/lib/chart/type/d3/d3-bar-chart-type.co b/lib/chart/type/d3/d3-bar-chart-type.co deleted file mode 100644 index 6bab42b..0000000 --- a/lib/chart/type/d3/d3-bar-chart-type.co +++ /dev/null @@ -1,239 +0,0 @@ -d3 = require 'd3' - -{ _, op, -} = require 'kraken/util' -{ ChartType, -} = require 'kraken/chart/chart-type' - -root = do -> this - - -class exports.BarChartType extends ChartType - __bind__ : <[ determineSize ]> - SPEC_URL : '/schema/d3/d3-bar.json' - - # NOTE: ChartType.register() must come AFTER `typeName` declaration. - typeName : 'd3-bar' - ChartType.register this - - - /** - * Hash of role-names to the selector which, when applied to the view, - * returns the correct element. - * @type Object - */ - roles : - viewport : '.viewport' - legend : '.graph-legend' - - - -> super ... - - getData: -> - @model.dataset.getColumns() - - - transform: -> - dataset = @model.dataset - options = @model.getOptions() import @determineSize() - options import do - colors : dataset.getColors() - labels : dataset.getLabels() - options - - - renderChartType: (metric, svgEl ,xScale, yScale) -> - - X = (d, i) -> xScale d[0] - Y = (d, i) -> yScale d[1] - - - ### Render the line path - metricBars = root.metricBars = svgEl.append "g" - .attr "class", "metric bars "+metric.get 'label' - - data = d3.zip metric.getDateColumn(),metric.getData() - - ### Render Bars - barWidth = svgEl.attr('width')/data.length - barHeight = (d) -> svgEl.attr('height')-Y(d) - - metricBars.selectAll "bar" - .data data - .enter().append "rect" - .attr "class", (d, i) -> "metric bar #i" - .attr "x", X - .attr "y", Y - .attr "height", barHeight - .attr "width", -> barWidth - .attr "fill", metric.get 'color' - .attr "stroke", "white" - .style "opacity", "0.4" - .style "z-index", -10 - - - # adding event listeners - chT = this - metricBars.selectAll ".metric.bar" - .on "mouseover", (d, i) -> - - svgEl.append "text" - .attr "class", "mf" - .attr "dx", 50 - .attr "dy", 100 - .style "font-size", "0px" - .transition() - .duration(800) - .text "Uh boy, the target would be: "+chT.numberFormatter(d[1]).toString() - .style "font-size", "25px" - .on "mouseout", (d, i) -> - - svgEl.selectAll ".mf" - .transition() - .duration(300) - .text "BUMMER!!!" - .style "font-size", "0px" - .remove() - - - - svgEl - - renderChart: (data, viewport, options, lastChart) -> - ### Starting with http://bost.ocks.org/mike/chart/ - - margin = {top: 20, right: 20, bottom: 20, left: 20} - width = 760 - height = 320 - xScale = d3.time.scale() - yScale = d3.scale.linear() - - dates = data[0] - cols = data.slice(1) - - # Calculate extents using all the data points (but not dates) - # allValues = d3.merge @model.dataset.getDataColumns() - allValues = d3.merge cols - - - # Update the x-scale with the extents of the dates. - xScale - .domain d3.extent dates - .range [ 0, width - margin.left - margin.right ] - - # Update the y-scale with the extents of the data. - yScale - .domain d3.extent allValues - .range [ height - margin.top - margin.bottom, 0 ] - - # Select the svg element, if it exists. - svg = d3.select viewport.0 .selectAll "svg" - .data [cols] - - # ...Otherwise, create the skeletal chart. - enterFrame = svg.enter() - .append "svg" .append "g" - .attr "class", "frame" - enterFrame.append "g" - .attr "class", "x axis time" - - # Update chart dimensions. - svg .attr "width", width - .attr "height", height - frame = svg.select "g.frame" - .attr "transform", "translate(#{margin.left},#{margin.top})" - - # Update the x-axis. - xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 0) - frame.select ".x.axis.time" - .attr "transform", "translate(0,#{yScale.range()[0]})" - .call xAxis - - X = (d, i) -> xScale d[0] - Y = (d, i) -> yScale d[1] - - ### Render Bars - barWidth = svg.attr('width')/dates.length - barHeight = (d) -> svg.attr('height')-Y(d) - - bars = frame.selectAll "g.bars" - .data cols.map -> d3.zip dates, it - bars.enter().append "g" - .attr "class", (col, i) -> "metric bars #i" - bars.exit().remove() - - bars.selectAll ".bar" - .data op.first - .enter().append "rect" - .attr "class", "bar" - .attr "x", X - .attr "y", Y - .attr "height", barHeight - .attr "width", -> barWidth - # TODO grab color from graph spec - .attr "fill", "red" - .attr "stroke", "white" - - - ### Mouse Lens - lens = root.lens = frame.selectAll "g.lens" - .data [[]] - gLens = lens.enter().append "g" - .attr "class", "lens" - .style "z-index", 1e9 - gInner = gLens.append "g" - .attr "transform", "translate(1.5em,0)" - gInner.append "circle" - .attr "r", "1.5em" - # .style "opacity", "0.4" - # .style "fill", "white" - .style "fill", "rgba(255, 255, 255, 0.4)" - .style "stroke", "white" - .style "stroke-width", "3px" - gInner.append "text" - .attr "y", "0.5em" - .attr "text-anchor", "middle" - .style "fill", "white" - .style "font", "12px Helvetica" - .style "font-weight", "bold" - - - mf = frame.selectAll "g.mf" - .data ["mf"] - .enter().append "g" - .attr "class", "mf" - .append "text" - .attr "class", "yoyo" - .attr "dx", 50 - .attr "dy", 100 - - - - bars.selectAll ".bar" - .on "mouseover", (d, i) -> - el = root.el = el # DOM element of event - # {r,g,b} = color = d3.rgb options.colors[i] - mf - .transition() - .duration(300) - .ease("exp") - .text "Uh boy, the target would be:"+d[1] - .style "font-size", "25px" - - .on "mouseout", (d, i) -> - mf - .transition() - .duration(1000) - .text "BUMMER!!!" - .style "font-size", "0px" - - - # {x:lineX, y:lineY} = root.pt = line.indexToPoint idx - # lens = frame.select "g.lens" - # .attr "transform", "translate(#lineX, #lineY)" - # lens.select "circle" .style "fill", "rgba(#r, #g, #b, 0.4)" - # lens.select "text" .text Y - - - - svg diff --git a/lib/chart/type/d3/d3-bar-element.co b/lib/chart/type/d3/d3-bar-element.co deleted file mode 100644 index 9f93fa7..0000000 --- a/lib/chart/type/d3/d3-bar-element.co +++ /dev/null @@ -1,78 +0,0 @@ -d3 = require 'd3' - -{ _, op, -} = require 'kraken/util' -{ D3ChartElement -} = require 'kraken/chart/type/d3/d3-chart-element' - -_fmt = require 'kraken/util/formatters' - -root = do -> this - -class exports.BarChartType extends D3ChartElement - __bind__ : <[ ]> - SPEC_URL : '/schema/d3/d3-bar.json' - - # NOTE: D3ChartElement.register() must come AFTER `typeName` declaration. - chartElement : 'd3-bar' - D3ChartElement.register this - - -> super ... - - renderChartElement: (metric, svgEl ,xScale, yScale) -> - - X = (d, i) -> xScale d[0] - Y = (d, i) -> yScale d[1] - - - ### Render the line path - metricBars = root.metricBars = svgEl.append "g" - .attr "class", "metric bars "+metric.get 'label' - - data = d3.zip metric.getDateColumn(),metric.getData() - - ### Render Bars - barWidth = svgEl.attr('width')/data.length - barHeight = (d) -> svgEl.attr('height')-Y(d) - - metricBars.selectAll "bar" - .data data - .enter().append "rect" - .attr "class", (d, i) -> "metric bar #i" - .attr "x", X - .attr "y", Y - .attr "height", barHeight - .attr "width", barWidth - .attr "fill", metric.get 'color' - .attr "stroke", "white" - .style "opacity", "0.4" - .style "z-index", -10 - - - # adding event listeners - chT = this - metricBars.selectAll ".metric.bar" - .on "mouseover", (d, i) -> - - svgEl.append "text" - .attr "class", "mf" - .attr "dx", 50 - .attr "dy", 100 - .style "font-size", "0px" - .transition() - .duration(800) - .text "Uh boy, the target would be: "+_fmt.numberFormatter(d[1]).toString() - .style "font-size", "25px" - .on "mouseout", (d, i) -> - - svgEl.selectAll ".mf" - .transition() - .duration(300) - .text "BUMMER!!!" - .style "font-size", "0px" - .remove() - - - - svgEl - diff --git a/lib/chart/type/d3/d3-chart-element.co b/lib/chart/type/d3/d3-chart-element.co deleted file mode 100644 index 29cc2a3..0000000 --- a/lib/chart/type/d3/d3-chart-element.co +++ /dev/null @@ -1,84 +0,0 @@ -d3 = require 'd3' -ColorBrewer = require 'colorbrewer' - -{ _, op, -} = require 'kraken/util' -{ ReadyEmitter, -} = require 'kraken/util/event' -# Base = require 'kraken/base/base' - - -root = do -> this - -/** - * Map of known libraries by name. - * @type Object - */ -KNOWN_CHART_ELEMENTS = exports.KNOWN_CHART_ELEMENTS = {} - -class exports.D3ChartElement extends ReadyEmitter - __bind__ : <[ ]> - SPEC_URL : '/schema/d3/d3-chart.json' - - - ### Class Methods - - /** - * Register a new d3 element - */ - @register = (Subclass) -> - # console.log "D3ChartElement.register(#Subclass)" - KNOWN_CHART_ELEMENTS[ Subclass::chartElement ] = Subclass - - /** - * Look up a `charttype` by `typeName`. - */ - @lookup = (name) -> - name = name.get('chartElement') if name instanceof Backbone.Model - KNOWN_CHART_ELEMENTS[name] - - /** - * Look up a chart type by name, returning a new instance - * with the given model (and, optionally, view). - * @returns {D3ChartElement} - */ - @create = (name) -> - # console.log "D3ChartElement.create(#name)" - return null unless Type = @lookup name - - new Type - - - -> - _.bindAll this, ...@__bind__ # TODO: roll up MRO - @loadSpec() unless @ready - super ... - - - - /** - * Load the corresponding chart specification, which includes - * info about valid options, along with their types and defaults. - */ - loadSpec: -> - return this if @ready - proto = @constructor:: - jQuery.ajax do - url : @SPEC_URL - dataType : 'json' - success : (spec) ~> - proto.spec = spec - proto.options_ordered = spec - proto.options = _.synthesize spec, -> [it.name, it] - proto.ready = true - @triggerReady() - error: ~> console.error "Error loading #{@typeName} spec! #it" - - this - - renderChartElement: (metric, svgEl ,xScale, yScale) -> svgEl - - - - - diff --git a/lib/chart/type/d3/d3-geo-element.co b/lib/chart/type/d3/d3-geo-element.co deleted file mode 100644 index 2238b31..0000000 --- a/lib/chart/type/d3/d3-geo-element.co +++ /dev/null @@ -1,185 +0,0 @@ -ColorBrewer = require 'colorbrewer' - -{ _, op, -} = require 'kraken/util' -{ ChartType, -} = require 'kraken/chart/chart-type' - - - - -class exports.GeoWorldChartType extends ChartType - __bind__ : <[ dygNumberFormatter dygNumberFormatterHTML ]> - SPEC_URL : '/schema/d3/d3-geo-world.json' - - # NOTE: ChartType.register() must come AFTER `typeName` declaration. - typeName : 'd3-geo-world' - ChartType.register this - - - /** - * Hash of role-names to the selector which, when applied to the view, - * returns the correct element. - * @type Object - */ - roles : - viewport : '.viewport' - legend : '.graph-legend' - - - - -> super ... - - - transform: -> - options = @model.getOptions() import @determineSize() - # options.colors.palette = ["black", "red"] if options.colors.palette? - options.colors.scaleDomain = d3.extent if options.colors.scaleDomain? - options - - - getProjection : (type) -> - switch type - case 'mercator' 'albers' 'albersUsa' - d3.geo[type]() - case 'azimuthalOrtho' - d3.geo.azimuthal() - .mode 'orthographic' - case 'azimuthalStereo' - d3.geo.azimuthal() - .mode 'stereographic' - default - throw new Error "Invalid map projection type '#type'!" - - - renderChart: (data, viewport, options, lastChart) -> - {width, height} = options - - fill = @fill = (data, options) -> - d3.scale[ options.colors.scale ]() - .domain options.colors.scaleDomain - .range options.colors.palette - - quantize = @quantize = (data, options) -> - (d) -> - if data[d.properties.name]? - return fill data[d.properties.name].editors - else - # console.log 'Country '+d.properties.name+' not in data' - return fill "rgb(0,0,0)" - - projection = @projection = @getProjection(options.map.projection) - .scale width - .translate [width/2, height/2] - - path = d3.geo.path() - .projection projection - - # path objects - feature = map.selectAll ".feature" - infobox = d3.select '.infobox' - - - move = -> - projection - .translate d3.event.translate - .scale d3.event.scale - feature.attr "d", path - - zoom = d3.behavior.zoom() - .translate projection.translate() - .scale projection.scale() - .scaleExtent [height,height*8] - .on "zoom", move - - - #### - - chart = d3.select viewport.0 - .append "svg:svg" - .attr "width", width - .attr "height", height - .append "svg:g" - .attr "transform", "translate(0,0)" - .call zoom - - # rectangle - map.append "svg:rect" - .attr "class", "frame" - .attr "width", width - .attr "height", height - - - ### infobox - infobox.select '#ball' - .append "svg:svg" - .attr "width", "100%" - .attr "height", "20px" - .append "svg:rect" - .attr "width", "60%" - .attr "height", "20px" - .attr "fill", '#f40500' - - setInfoBox = (d) -> - name = d.properties.name - ae = 0 - e5 = 0 - e100 = 0 - - if data[name]? - ae = parseInt data[name].editors - e5 = parseInt data[name].editors5 - e100 = parseInt data[name].editors100 - - infobox.select '#country' .text name - infobox.select '#ae' .text ae - infobox.select '#e5' .text e5+" ("+(100.0*e5/ae).toPrecision(3)+"%)" - infobox.select '#e100' .text e100+" ("+(100.0*e100/ae).toPrecision(3)+"%)" - - xy = d3.svg.mouse this - infobox.style "left", xy[0]+'px' - infobox.style "top", xy[1]+'px' - infobox.style "display", "block" - - - worldmap = -> - d3.json do - "/data/geo/maps/world-countries.json" - (json) -> - feature := feature - .data json.features - .enter().append "svg:path" - .attr "class", "feature" - .attr "d", path - .attr "fill", quantize - .attr "id", (d) -> d.properties.name - .on "mouseover", setInfoBox - .on "mouseout", -> infobox.style "display", "none" - - - - - -data = null -main = -> - jQuery.ajax do - url : "/data/geo/data/en_geo_editors.json" - dataType : 'json' - success : (res) -> - # result will be the returned JSON - data := res - - # delete & hide spinner - jQuery '.geo-spinner' .spin(false).hide() - - # load the world map - worldmap() - - # adding bootstrap tooltips - # $ '.page-header' .tooltip title:"for the header it works but is useless" - # $ '.feature' .tooltip title:"here it doesn't work" - - console.log 'Loaded geo coding map!' - error : (err) -> console.error err - - diff --git a/lib/chart/type/d3/d3-line-element.co b/lib/chart/type/d3/d3-line-element.co deleted file mode 100644 index 51ce742..0000000 --- a/lib/chart/type/d3/d3-line-element.co +++ /dev/null @@ -1,111 +0,0 @@ -d3 = require 'd3' -ColorBrewer = require 'colorbrewer' - -{ _, op, -} = require 'kraken/util' -{ D3ChartElement -} = require 'kraken/chart/type/d3/d3-chart-element' - -_fmt = require 'kraken/util/formatters' - -root = do -> this - -class exports.LineChartElement extends D3ChartElement - __bind__ : <[ ]> - SPEC_URL : '/schema/d3/d3-line.json' - - # NOTE: D3ChartElement.register() must come AFTER `typeName` declaration. - chartElement : 'd3-line' - D3ChartElement.register this - - -> super ... - - renderChartElement: (metric, svgEl ,xScale, yScale) -> - - X = (d, i) -> xScale d[0] - Y = (d, i) -> yScale d[1] - line = d3.svg.line().x(X).y(Y) - - ### Render the line path - metricLine = root.metricLine = svgEl.append "g" - .attr "class", "g metric line "+metric.get 'label' - - data = d3.zip metric.getDateColumn(),metric.getData() - - metricLine.selectAll "path.line" - .data d3.zip data.slice(0,-1), data.slice(1) - .enter().append "path" - .attr "d", line - .attr "class", (d, i) -> "metric line segment #i" - .style "stroke", metric.getColor 'color' - - - ### Mouse Lens - lens = root.lens = svgEl.selectAll "g.lens" - .data [[]] - gLens = lens.enter().append "g" - .attr "class", "lens" - .style "z-index", 1e9 - gInner = gLens.append "g" - .attr "transform", "translate(1.5em,0)" - gInner.append "circle" - .attr "r", "1.5em" - # .style "opacity", "0.4" - # .style "fill", "white" - .style "fill", "rgba(255, 255, 255, 0.4)" - .style "stroke", "white" - .style "stroke-width", "3px" - gInner.append "text" - .attr "y", "0.5em" - .attr "text-anchor", "middle" - .style "fill", "black" - .style "font", "12px Helvetica" - .style "font-weight", "bold" - - # event listeners - metricLine.selectAll ".line.segment" - .on "mouseover", (d, i) -> - - {r,g,b} = color = d3.rgb metric.getColor 'color' - lineX = (X(d[0])+X(d[1]))/2 - lineY = (Y(d[0])+Y(d[1]))/2 - - - lens = svgEl.select "g.lens" - .attr "transform", "translate(#lineX, #lineY)" - lens.select "circle" .style "fill", "rgba(#r, #g, #b, 0.4)" - lens.select "text" .text -> _fmt.numberFormatter(d[0][1]).toString() - - svgEl - - -# If instead of a line segment for each month we want to use one line segment per metric, we have to have a way of accessing the data associated with a given time step. - -# lines.attr "d", line -# .attr "class", (col, i) -> "metric line metric#i" -# .style "stroke", (col, i) -> options.colors[i] -# .each (col, i) -> -# {width} = bbox = @getBBox() -# # Add line-to-data position conversions -# @indexAtX = d3.scale.quantize() -# .domain [0, width] -# .range d3.range col.length -# @indexToPoint = (idx) -> -# @pathSegList.getItem idx - -# lines.on "mouseover", (col, i) -> -# line = root.line = this # DOM element of event -# {r,g,b} = color = d3.rgb options.colors[i] - -# # quantize mouse x-location to get for closest data-point (index into data array) -# [x,y] = root.pos = d3.mouse line -# idx = root.idx = line.indexAtX x -# {x:lineX, y:lineY} = root.pt = line.indexToPoint idx - -# lens = frame.select "g.lens" -# .data d3.select(line).data() -# .attr "transform", "translate(#lineX, #lineY)" -# lens.select "circle" .style "fill", "rgba(#r, #g, #b, 0.4)" -# lens.select "text" .text (col) -> col[idx][1] - - diff --git a/lib/chart/type/d3/index.co b/lib/chart/type/d3/index.co deleted file mode 100644 index a18badd..0000000 --- a/lib/chart/type/d3/index.co +++ /dev/null @@ -1,6 +0,0 @@ -d3chart = require 'kraken/chart/type/d3/d3-chart-element' -line = require 'kraken/chart/type/d3/d3-line-element' -bar = require 'kraken/chart/type/d3/d3-bar-element' -# geo = require 'kraken/chart/type/d3/d3-geo-element' - -exports import line import bar import d3chart # import geo diff --git a/lib/chart/type/dygraphs.co b/lib/chart/type/dygraphs.co deleted file mode 100644 index 4d0113e..0000000 --- a/lib/chart/type/dygraphs.co +++ /dev/null @@ -1,133 +0,0 @@ -_ = require 'kraken/util/underscore' -{ ChartType, -} = require 'kraken/chart/chart-type' - - -class exports.DygraphsChartType extends ChartType - __bind__ : <[ dygNumberFormatter dygNumberFormatterHTML ]> - SPEC_URL : '/schema/dygraph.json' - - # NOTE: ChartType.register() must come AFTER `typeName` declaration. - typeName : 'dygraphs' - ChartType.register this - - - /** - * Hash of role-names to the selector which, when applied to the view, - * returns the correct element. - * @type Object - */ - roles : - viewport : '.viewport' - legend : '.graph-legend' - - -> super ... - - - - ### Formatters {{{ - - # XXX: Dygraphs-specific - makeAxisFormatter: (fmttr) -> - (n, granularity, opts, g) -> fmttr n, opts, g - - # XXX: Dygraphs-specific - dygAxisDateFormatter: (n, granularity, opts, g) -> - moment(n).format 'MM/YYYY' - - # XXX: Dygraphs-specific - dygDateFormatter: (n, opts, g) -> - moment(n).format 'DD MMM YYYY' - - # XXX: Dygraphs-specific - dygNumberFormatter: (n, opts, g) -> - digits = if typeof opts('digitsAfterDecimal') is 'number' then that else 2 - { whole, fraction, suffix } = @numberFormatter n, digits - "#whole#fraction#suffix" - - # XXX: Dygraphs-specific - dygNumberFormatterHTML: (n, opts, g) -> - digits = if typeof opts('digitsAfterDecimal') is 'number' then that else 2 - # digits = opts('digitsAfterDecimal') ? 2 - { whole, fraction, suffix } = @numberFormatter n, digits - # coco will trim the whitespace - " - #whole - #fraction - #suffix - " - - - ### }}} - ### Rendering {{{ - - /** - * Determines chart viewport size. - * @return { width, height } - */ - determineSize: -> - modelW = width = @model.get 'width' - modelH = height = @model.get 'height' - return { width, height } unless @view.ready and width and height - - viewport = @getElementsForRole 'viewport' - legend = @getElementsForRole 'legend' - - if width is 'auto' - # Remove old style, as it confuses dygraph after options update - delete viewport.prop('style').width - vpWidth = viewport.innerWidth() or 300 - legendW = legend.outerWidth() or 228 - width = vpWidth - legendW - 10 - (vpWidth - legend.position().left - legendW) - width ?= modelW - - if height is 'auto' - # Remove old style, as it confuses dygraph after options update - delete viewport.prop('style').height - height = viewport.innerHeight() or 320 - height ?= modelH - - { width, height } - - - /** - * Transforms the domain objects into a hash of derived values using - * chart-type-specific keys. - * @returns {Object} The derived chart options. - */ - transform: -> - dataset = @model.dataset - options = @view.chartOptions() import @determineSize() - options import do - colors : dataset.getColors() - labels : dataset.getLabels() - labelsDiv : @getElementsForRole 'legend' .0 - valueFormatter : @dygNumberFormatterHTML - axes: - x: - axisLabelFormatter : @dygAxisDateFormatter - valueFormatter : @dygDateFormatter - y: - axisLabelFormatter : @makeAxisFormatter @dygNumberFormatter - valueFormatter : @dygNumberFormatterHTML - - - /** - * @returns {Dygraph} The Dygraph chart object. - */ - renderChart: (data, viewport, options, lastChart) -> - @resizeViewport() - - # console.log "#this.render!" - # _.dump options, 'options' - - # Always rerender the chart to sidestep the case where we need - # to push defaults into Dygraphs to reset the current option state. - lastChart?.destroy() - new Dygraph viewport.0, data, options - - - - ### }}} - - diff --git a/lib/chart/type/index.co b/lib/chart/type/index.co deleted file mode 100644 index e69de29..0000000 diff --git a/lib/dashboard/dashboard-model.co b/lib/dashboard/dashboard-model.co deleted file mode 100644 index d4e3fbf..0000000 --- a/lib/dashboard/dashboard-model.co +++ /dev/null @@ -1,87 +0,0 @@ -{ _, op, -} = require 'kraken/util' -{ BaseModel, -} = require 'kraken/base' -{ Graph, GraphList, -} = require 'kraken/graph/graph-model' - - -/** - * @class - */ -Dashboard = exports.Dashboard = BaseModel.extend do # {{{ - urlRoot : '/dashboards' - - # graph_ids : null - graphs : null - # tabs : null - - - constructor: function Dashboard - @graphs = new GraphList - BaseModel ... - - - initialize: -> - BaseModel::initialize ... - # @getGraphs() - - defaults: -> - name : null - tabs : [ { name:"Main", graph_ids:[] } ] - - load: -> - @once 'fetch-success', (~> @getGraphs()) .loadModel() - this - - - /** - * Look up a tab. - * - * @param {String|Number} tab Tab name or index. - * @returns {Tab} Tab object. - */ - getTab : (tab) -> - tabs = @get 'tabs' - return tabs[tab] if typeof tab is 'number' - _.find tabs, -> it.name is tab - -# addGraph: (graph, tabName) -> -# ... - - show : (cb, obj) -> - console.log('[show]') - console.log(obj) - cb null, obj - - pushAsync : (cb, arr) -> - (err, elem) -> - arr.push elem - cb null - - getGraphs : -> - console.log('[getGraphs]\tentering') - # consolidate graph_ids to one array - graph_ids = _(@tabs).chain().values().map((tab_obj) -> tab_obj.graph_ids).flatten().value() - - Seq graph_ids - .parMap_ (next, graph_id) -> - next null, [graph_id] - .parEach_ (next, graph_id_arr) ~> - Graph.lookup graph_id_arr[0], @pushAsync next, graph_id_arr - # Graph.lookup graph_id_arr[0], (err, el) -> - # graph_id_arr.push el - # next null - # .parEach_ @show - .parMap_ (next, [id, graph]:tuple) ~> - graph.once 'ready', -> next.ok tuple - .unflatten() - .seq_ (next, graph_tuples) ~> - # @graphs = _.generate graph_tuples - @graphs.reset _.pluck graph_tuples, 1 - console.log('[setter]\tcalling ready') - @triggerReady() - this - - -# }}} \ No newline at end of file diff --git a/lib/dashboard/dashboard-view.co b/lib/dashboard/dashboard-view.co deleted file mode 100644 index eada37e..0000000 --- a/lib/dashboard/dashboard-view.co +++ /dev/null @@ -1,174 +0,0 @@ -Seq = require 'seq' - -{ _, op, -} = require 'kraken/util' -{ BaseModel, BaseView, -} = require 'kraken/base' -{ Graph, GraphList, GraphDisplayView, -} = require 'kraken/graph' -{ Dashboard, -} = require 'kraken/dashboard/dashboard-model' - - -/** - * @class - */ -DashboardView = exports.DashboardView = BaseView.extend do # {{{ - __bind__ : <[ addTab ]> - tagName : 'section' - className : 'dashboard' - template : require 'kraken/template/dashboard/dashboard' - - - events: - # Select the whole permalink URI text when it receives focus. - 'click .graphs.tabbable .nav a' : 'onTabClick' - 'shown .graphs.tabbable .nav a' : 'render' - # 'shown .graphs.tabbable .nav a' : 'onTabShown' - # 'click a[data-target="#other-graphs"]' : 'onTabShown' - # 'click .load-button' : 'load' - - # subviews : [] - graphs : null - ready : false - - - constructor: function DashboardView(options={}) - @graphs = new GraphList - BaseView ... - - initialize: -> - @model or= new Dashboard - DashboardView.__super__.initialize ... - # @graphs.on 'add', @attachGraphs, this - # @graphs.on 'add', @attachGraph, this - @model.once 'ready', @load, this .load() - - - # FIXME: - # - combine all loads into one seq so... - # - trigger ready when finished - # TODO: - # - only render graph when scrolling makes it visible - load: -> - console.log "#this.load! Model ready!", @model - Seq @model.get('tabs') - .seqEach_ @addTab - .seq ~> - console.log "#{this}.load! Done adding tabs!" - @triggerReady() - - addTab: (nextTab, tab) -> - # self = this - # a(href="#core-graphs", data-toggle="tab") Core - tabModel = new BaseModel tab - tabView = @addSubview new DashboardTabView {model:tabModel} - tabId = tabView.getTabId() - @$ "nav > ul.nav" - .append "
  • #{tab.name}
  • " - - graphs = _(tab.graph_ids).map (graph_id) ~> @model.graphs.get graph_id - Seq graphs - .parMap_ (next, graph) ~> - @graphs.add graph - next null, new GraphDisplayView {model:graph} - .parMap_ (next, graphView) ~> - return next.ok() if graphView.isAttached - tabView.addSubview graphView - # tabEl = @$ tab.name - # if tabEl.length - # tabEl.append view.$el - # view.isAttached = true - # else - # console.log "#this.addTab: Unable to find bind-point for #view!", view - next.ok() - .seq ~> - console.log "#{this}.addTab: All graphs added!" - # @render() - nextTab.ok() - this - - ### Tabs {{{ - - onTabShown: (e) -> - @render() - # @renderSubviews() - # Seq @subviews - # .parMap (view) -> - # # view.resizeViewport() - # view.renderChart() - - onTabClick: (evt) -> - evt.preventDefault() - - - ### }}} - ### Navigation Between Graphs {{{ - - /** - * Scroll to the specified graph. - * - * @param {String|Number|Graph} graph The Graph to scroll to; can be specified as a - * Graph id, an index into the Graphs list, or a Graph object. - * @returns {this} - */ - scrollToGraph: (graph) -> - if typeof graph is 'string' - graph = @graphs.get graph - else if typeof graph is 'number' - graph = @graphs.at graph - unless graph instanceof Graph - console.error "#this.scrollToGraph() Unknown graph #graph!" - return this - - return this unless view = _.find @subviews, -> it.model is graph - $ 'body' .scrollTop view.$el.offset().top if view.$el.is ':visible' - - this - - findClosestGraph: (scroll) -> - scroll or= $ 'body' .scrollTop() - views = @subviews - .filter -> it.$el.is ':visible' - .map -> [ it.$el.offset().top, it ] - .filter -> it[0] >= scroll - .sort (a,b) -> op.cmp a[0], b[0] - return views[0][1] if views.length - - ### }}} - - - -/** - * @class - * @extends BaseView - */ -DashboardTabView = exports.DashboardTabView = BaseView.extend do # {{{ - __bind__ : <[ ]> - className : 'tab-pane' - tag : 'div' - template : require 'kraken/template/dashboard/dashboard-tab' - - - constructor: function DashboardTabView - BaseView ... - - initialize: -> - BaseView::initialize ... - - getTabId: -> - _.underscored @model.get('name') .toLowerCase() + '-graphs-tab' - - toTemplateLocals: -> - json = DashboardTabView.__super__.toTemplateLocals ... - tab_name = _.underscored @model.get('name') .toLowerCase() - json import - tab_cls : "#tab_name-graphs-pane" - tab_id : "#tab_name-graphs-tab" - -# }}} - - - - - diff --git a/lib/dashboard/index.co b/lib/dashboard/index.co deleted file mode 100644 index 03fc595..0000000 --- a/lib/dashboard/index.co +++ /dev/null @@ -1,3 +0,0 @@ -models = require 'kraken/dashboard/dashboard-model' -views = require 'kraken/dashboard/dashboard-view' -exports import models import views diff --git a/lib/data/data-view.co b/lib/data/data-view.co deleted file mode 100644 index c57eaa8..0000000 --- a/lib/data/data-view.co +++ /dev/null @@ -1,122 +0,0 @@ -Seq = require 'seq' -{ _, op, -} = require 'kraken/util' -{ BaseView, ViewList, -} = require 'kraken/base' -{ DataSetView, -} = require 'kraken/data/dataset-view' -{ MetricEditView, -} = require 'kraken/data/metric-edit-view' -{ DataSource, -} = require 'kraken/data/datasource-model' - -/** - * @class DataSet selection and customization UI (root of the `data` tab). - */ -DataView = exports.DataView = BaseView.extend do # {{{ - __bind__ : <[ onMetricsChanged ]> - tagName : 'section' - className : 'data-ui' - template : require 'kraken/template/data/data' - - datasources : null - - - /** - * @constructor - */ - constructor: function DataView - BaseView ... - - initialize: -> - @graph_id = @options.graph_id - BaseView::initialize ... - @metric_views = new ViewList - @datasources = DataSource.getAllSources() - # @on 'update', @onUpdate, this - @model.metrics - .on 'add', @addMetric, this - .on 'remove', @removeMetric, this - @model.once 'ready', @onReady, this - - onReady: -> - # console.log "#this.onReady! #{@model.metrics}" - dataset = @model - @model.metrics.each @addMetric, this - @dataset_view = new DataSetView {@model, @graph_id, dataset, @datasources} - @addSubview @dataset_view - .on 'add-metric', @onMetricsChanged, this - .on 'remove-metric', @onMetricsChanged, this - .on 'select-metric', @selectMetric, this - - @render() - @triggerReady() - this - - - /** - * Transform the `columns` field to ensure an Array of {label, type} objects. - */ - canonicalizeDataSource: (ds) -> - ds.shortName or= ds.name - ds.title or= ds.name - ds.subtitle or= '' - - cols = ds.columns - if _.isArray cols - ds.metrics = _.map cols, (col, idx) -> - if _.isArray col - [label, type] = col - {idx, label, type or 'int'} - else - col - else - ds.metrics = _.map cols.labels, (label, idx) -> - {idx, label, type:cols.types[idx] or 'int'} - ds - - - toTemplateLocals: -> - attrs = _.clone @model.attributes - { @graph_id, @datasources } import attrs - - addMetric: (metric) -> - # console.log "#this.addMetric!", metric - return metric if @metric_views.findByModel metric - view = new MetricEditView {model:metric, @graph_id, dataset:@model, @datasources} - .on 'metric-update', @onUpdateMetric, this - .on 'metric-change', @onUpdateMetric, this - @metric_views.push @addSubview view - @renderSubviews() - metric - - removeMetric: (metric) -> - # console.log "#this.removeMetric!", metric - return unless view = @metric_views.findByModel metric - @metric_views.remove view - @removeSubview view - metric - - selectMetric: (metric) -> - # console.log "#this.selectMetric!", metric - @metric_views.invoke 'hide' - @metric_edit_view = @metric_views.findByModel metric - @metric_edit_view?.show() - _.delay @onMetricsChanged, 10 - - onMetricsChanged: -> - return unless @dataset_view - oldMinHeight = parseInt @$el.css 'min-height' - newMinHeight = Math.max do - @dataset_view.$el.height() - @metric_edit_view?.$el.height() - # console.log 'onMetricsChanged!', oldMinHeight, '-->', newMinHeight - @$el.css 'min-height', newMinHeight - - onUpdateMetric: -> - # console.log "#this.onUpdateMetric!" - @trigger 'metric-change', @model, this - @render() - - -# }}} diff --git a/lib/data/dataset-model.co b/lib/data/dataset-model.co deleted file mode 100644 index 5bc6c25..0000000 --- a/lib/data/dataset-model.co +++ /dev/null @@ -1,183 +0,0 @@ -Seq = require 'seq' -ColorBrewer = require 'colorbrewer' - -{ _, op, -} = require 'kraken/util' -{ BaseModel, BaseList, -} = require 'kraken/base' -{ Metric, MetricList, -} = require 'kraken/data/metric-model' -{ DataSource, DataSourceList, -} = require 'kraken/data/datasource-model' - - - -/** - * @class - */ -DataSet = exports.DataSet = BaseModel.extend do # {{{ - urlRoot : '/datasets' - - /** - * @type DataSourceList - */ - sources : null - - /** - * @type MetricList - */ - metrics : null - - defaults : -> - palette : null - lines : [] - metrics : [] - - - constructor: function DataSet (attributes={}, opts) - @metrics = new MetricList attributes.metrics - BaseModel.call this, attributes, opts - - initialize : -> - BaseModel::initialize ... - @set 'metrics', @metrics, {+silent} - @on 'change:metrics', @onMetricChange, this - # @metrics.on 'add remove reset', ~> - # @trigger 'change:metrics', @metrics, this - - - load: (opts={}) -> - @resetReady() if opts.force - return this if @loading or @ready - - unless @metrics.length - return @triggerReady() - - # console.log "#this.load()..." - @wait() - @loading = true - @trigger 'load', this - Seq @metrics.models - .parEach_ (next, metric) -> - metric.once 'ready', next.ok .load() - .seq ~> - # console.log "#{this}.load() complete!" - @loading = false - @unwait() # terminates the `load` wait - @triggerReady() - this - - # refreshSubModels: -> - # # @set 'metrics', @metrics.toJSON(), {+silent} - # @set 'metrics', _.pluck(@metrics.models, 'attributes'), {+silent} - # this - - /** - * Override to handle the case where one of our rich sub-objects - * (basically `metrics`) is set as a result of the `fetch()` call by the - * Graph object. To prevent it from blowing away the `MetricList`, we - * perform a `reset()` here. But that won't trigger a `change:metrics` event, - * so we do a little dance to set it twice, as object identity would otherwise - * cause it to think nothing has changed. - */ - set: (key, value, opts) -> - # return DataSet.__super__.set ... unless @metrics - - if _.isObject(key) and key? - [values, opts] = [key, value] - else - values = { "#key": value } - opts or= {} - - for key, value in values - continue unless key is 'metrics' and _.isArray value - @metrics.reset value - delete values[key] - unless opts.silent - DataSet.__super__.set.call this, 'metrics', value, {+silent} - DataSet.__super__.set.call this, 'metrics', @metrics, opts - - DataSet.__super__.set.call this, values, opts - - - toJSON: -> - json = DataSet.__super__.toJSON ... - delete json.id - json - - - /* * * * TimeSeriesData interface * * * {{{ */ - - /** - * @returns {Array} The reified dataset, materialized to a list of rows including timestamps. - */ - getData: -> - return [] unless @ready - columns = @getColumns() - if columns?.length - _.zip ...columns - else - [] - - /** - * @returns {Array} List of all columns (including date column). - */ - getColumns: -> - return [] unless @ready - _.compact [ @getDateColumn() ].concat @getDataColumns() - - /** - * @returns {Array} The date column. - */ - getDateColumn: -> - return [] unless @ready - dates = @metrics.onlyOk().invoke 'getDateColumn' - maxLen = _.max _.pluck dates, 'length' - _.find dates, -> it.length is maxLen - - /** - * @returns {Array} List of all columns except the date column. - */ - getDataColumns: -> - return [] unless @ready - @metrics.onlyOk().invoke 'getData' - - /** - * @returns {Array} List of column labels. - */ - getLabels: -> - return [] unless @ready - [ 'Date' ].concat @metrics.onlyOk().invoke 'getLabel' - - getColors: -> - return [] unless @ready - @metrics.onlyOk().invoke 'getColor' - - # }}} - - - newMetric: -> - index = @metrics.length - @metrics.add m = new Metric { index, color:ColorBrewer.Spectral[11][index] } - m.on 'ready', ~> @trigger 'metric-data-loaded', this, m - # @trigger 'change:metrics', this, @metrics, 'metrics' - # @trigger 'change', this, @metrics, 'metrics' - m - - onMetricChange: -> - # console.log "#this.onMetricChange! ready=#{@ready}" - @resetReady() - @load() - - - # XXX: toJSON() must ensure columns in MetricList are ordered by index - # ...in theory, MetricList.comparator now does this - - # toJSON: -> - # @refreshSubModels() - # json = DataSet.__super__.toJSON ... - # json.metrics = json.metrics.map -> it.toJSON?() or it - # json - -# }}} - diff --git a/lib/data/dataset-view.co b/lib/data/dataset-view.co deleted file mode 100644 index 4a55a14..0000000 --- a/lib/data/dataset-view.co +++ /dev/null @@ -1,155 +0,0 @@ -{ _, op, -} = require 'kraken/util' -{ BaseView, -} = require 'kraken/base' - - -/** - * @class - */ -DataSetView = exports.DataSetView = BaseView.extend do # {{{ - tagName : 'section' - className : 'dataset-ui dataset' - template : require 'kraken/template/data/dataset' - - events: - 'click .new-metric-button' : 'onNewMetric' - 'click .delete-metric-button' : 'onDeleteMetric' - 'click .metrics .dataset-metric' : 'selectMetric' - - views_by_cid : {} - active_view : null - - - constructor: function DataSetView - BaseView ... - - initialize: -> - {@graph_id, @datasources, @dataset} = @options - BaseView::initialize ... - @views_by_cid = {} - @model - .on 'ready', @addAllMetrics, this - @model.metrics - .on 'add', @addMetric, this - .on 'remove', @removeMetric, this - .on 'change', @onMetricChange, this - .on 'reset', @addAllMetrics, this - - - addMetric: (metric) -> - # console.log "#this.addMetric!", metric - if @views_by_cid[metric.cid] - @removeSubview that - delete @views_by_cid[metric.cid] - - view = @addSubview new DataSetMetricView {model:metric, @graph_id} - @views_by_cid[metric.cid] = view - @trigger 'add-metric', metric, view, this - @render() - view - - removeMetric: (metric) -> - if metric instanceof [jQuery.Event, Event] - metric = @getMetricForElement metric.target - # console.log "#this.removeMetric!", metric - return unless metric - if view = @views_by_cid[metric.cid] - @removeSubview view - delete @views_by_cid[metric.cid] - @trigger 'remove-metric', metric, view, this - view - - addAllMetrics: -> - # console.log "#this.addAllMetrics! --> #{@model.metrics}" - @removeAllSubviews() - @model.metrics.each @addMetric, this - this - - - selectMetric: (metric) -> - if metric instanceof [jQuery.Event, Event] - metric = @getMetricForElement metric.target - # console.log "#this.selectMetric!", metric - return unless metric - view = @active_view = @views_by_cid[metric.cid] - - @$ '.metrics .dataset-metric' .removeClass 'metric-active' - view.$el.addClass 'metric-active' - view.$ '.activity-arrow' .css 'font-size', 2+view.$el.height() - - @trigger 'select-metric', metric, view, this - this - - onMetricChange: (metric) -> - return unless view = @views_by_cid[metric?.cid] - view.$ '.activity-arrow:visible' .css 'font-size', 2+view.$el.height() - - onNewMetric: -> - # console.log "#this.newMetric!" - # triggers 'add' on @model.metrics - @model.newMetric() - false - - onDeleteMetric: (evt) -> - metric = @getMetricForElement evt.target - # console.log "#this.onDeleteMetric!", metric - # Triggers a 'remove' event, which in turn calls `removeMetric()` - @model.metrics.remove metric - false - - - getMetricForElement: (el) -> - $ el .parents '.dataset-metric' .eq(0).data 'model' - -# }}} - - - -/** - * @class - */ -DataSetMetricView = exports.DataSetMetricView = BaseView.extend do # {{{ - tagName : 'tr' - className : 'dataset-metric metric' - template : require 'kraken/template/data/dataset-metric' - - - - constructor: function DataSetMetricView - BaseView ... - - initialize: -> - @graph_id = @options.graph_id - BaseView::initialize ... - @on 'update', @onUpdate, this - - - toTemplateLocals: -> - m = DataSetMetricView.__super__.toTemplateLocals ... - - # XXX: Icons/classes for visible/disabled? - m import - graph_id : @graph_id - label : @model.getLabel() - viewClasses : _.compact([ - if @model.isOk() then 'valid' else 'invalid', - if m.visible then 'visible' else 'hidden', - 'disabled' if m.disabled, - ]).map( -> "metric-#it" ).join ' ' - source : - if m.source_id and m.source_col - "#{m.source_id}[#{m.source_col}]" - else - 'No source' - timespan : - if _.every ts = m.timespan, op.ok - "#{ts.start} to #{ts.end} by #{ts.step}" - else - '—' - - onUpdate: -> - @$ '.col-color' .css 'color', @model.get 'color' - -# }}} - diff --git a/lib/data/datasource-model.co b/lib/data/datasource-model.co deleted file mode 100644 index 544af94..0000000 --- a/lib/data/datasource-model.co +++ /dev/null @@ -1,190 +0,0 @@ -{ _, op, -} = require 'kraken/util' -{ TimeSeriesData, CSVData, -} = require 'kraken/util/timeseries' -{ BaseModel, BaseList, ModelCache, -} = require 'kraken/base' -{ Metric, MetricList, -} = require 'kraken/data/metric-model' - - -/** - * @class - */ -DataSource = exports.DataSource = BaseModel.extend do # {{{ - __bind__ : <[ onLoadDataSuccess onLoadDataError ]> - urlRoot : '/datasources' - ready : false - - /** - * Parsed data for this datasource. - * @type Array - */ - data : null - - defaults: -> - id : '' - url : '' - format : 'json' - - name : '' - shortName : '' - title : '' - subtitle : '' - desc : '' - notes : '' - - timespan : - start : null - end : null - step : '1mo' - - columns : [] - - chart : - chartType : 'dygraphs' - options : {} - - url: -> - "/datasources/#{@id}.json" - - - - - - constructor: function DataSource - BaseModel ... - - initialize: -> - @attributes = @canonicalize @attributes - BaseModel::initialize ... - @constructor.register this - @metrics = new MetricList @attributes.metrics - @on 'change:metrics', @onMetricChange, this - - - canonicalize: (ds) -> - ds.shortName or= ds.name - ds.title or= ds.name - ds.subtitle or= '' - - cols = ds.columns - if _.isArray cols - ds.metrics = _.map cols, (col, idx) -> - if _.isArray col - [label, type] = col - {idx, label, type or 'int'} - else - col.type or= 'int' - col - else - ds.metrics = _.map cols.labels, (label, idx) -> - {idx, label, type:cols.types[idx] or 'int'} - ds - - - - loadAll: -> - @loader start: -> - Seq() - .seq_ (next) ~> - @once 'fetch-success', next.ok - @loadModel() - .seq_ (next) ~> - @once 'load-data-success', next.ok - @loadData() - .seq ~> - @trigger 'load-success', this - this - - loadData: -> - @wait() - @trigger 'load-data', this - return @onLoadDataSuccess @data if @data - switch @get 'format' - case 'json' then @loadJSON() - case 'csv' then @loadCSV() - default - console.error "#this.load() Unknown Data Format!" - @onLoadDataError null, 'Unknown Data Format!', new Error 'Unknown Data Format!' - this - - loadJSON: -> - $.ajax do - url : @get 'url' - dataType : 'json' - success : (data) ~> @onLoadDataSuccess new TimeSeriesData data - error : @onLoadDataError - this - - loadCSV: -> - $.ajax do - url : @get 'url' - dataType : 'text' - success : (data) ~> @onLoadDataSuccess new CSVData data - error : @onLoadDataError - this - - onLoadDataSuccess: (@data) -> - # console.log "#this.onLoadDataSuccess #{@data}" - @unwait() - @trigger 'load-data-success', this - @triggerReady() - - onLoadDataError: (jqXHR, txtStatus, err) -> - console.error "#this Error loading data! -- #msg: #{err or ''}" - @unwait() - @_errorLoading = true - @trigger 'load-data-error', this, txtStatus, err - - - getDateColumn: -> - @data?.dateColumn - - getData: -> - @data?.toJSON?() or @data - - getColumn: (idx) -> - @data?.columns[idx] - - getColumnName: (idx) -> - @get('metrics')?[idx]?.label - - getColumnIndex: (name) -> - return that.idx if _.find @get('metrics'), -> it.label is name - -1 - - onMetricChange: -> - @metrics.reset @get 'metrics' - - -# }}} - - -/** - * @class - */ -DataSourceList = exports.DataSourceList = BaseList.extend do # {{{ - urlRoot : '/datasources' - model : DataSource - - constructor: function DataSourceList then BaseList ... - initialize : -> BaseList::initialize ... -# }}} - - - -### DataSource Cache - -ALL_SOURCES = new DataSourceList -sourceCache = new ModelCache DataSource, {-ready, cache:ALL_SOURCES} - -# Fetch all DataSources -$.getJSON '/datasources/all', (data) -> - ALL_SOURCES.reset _.map data, op.I - sourceCache.triggerReady() - -DataSource.getAllSources = -> - ALL_SOURCES - - diff --git a/lib/data/datasource-ui-view.co b/lib/data/datasource-ui-view.co deleted file mode 100644 index 7f04041..0000000 --- a/lib/data/datasource-ui-view.co +++ /dev/null @@ -1,69 +0,0 @@ -{ _, op, -} = require 'kraken/util' -{ BaseModel, BaseList, BaseView, -} = require 'kraken/base' - - -/** - * @class - * Model is a Metric. - */ -DataSourceUIView = exports.DataSourceUIView = BaseView.extend do # {{{ - __bind__ : <[ ]> - tagName : 'section' - className : 'datasource-ui' - template : require 'kraken/template/data/datasource-ui' - - events : - 'click .datasource-summary' : 'onHeaderClick' - 'click .datasource-source-metric' : 'onSelectMetric' - - graph_id : null - dataset : null - datasources : null - - - - constructor: function DataSourceUIView - BaseView ... - - initialize: -> - this import @options.{graph_id, dataset, datasources} - BaseView::initialize ... - - toTemplateLocals: -> - locals = @model.toJSON() - locals import {@graph_id, @dataset, @datasources, cid:@model.cid} - - ds = @model.source - hasSource = @model.get('source_id')? and ds - locals.source_summary = unless hasSource then '' else @model.getSourceColumnName() - - dsts = ds?.get('timespan') or {} - ts = locals.timespan = _.defaults _.clone(@model.get('timespan')), dsts - hasTimespan = hasMetric and ts.start and ts.end and ts.step - locals.timespan_summary = unless hasTimespan then '