From 56597ffed10111e0b79062de197cae67dc582e30 Mon Sep 17 00:00:00 2001 From: dsc Date: Wed, 11 Apr 2012 02:01:47 -0700 Subject: [PATCH] Checkpoint on Data UI --- lib/base.co | 173 ++++++++++++++++++++++++------ lib/chart/chart-option-model.co | 2 - lib/chart/chart-option-view.co | 91 +++++++++++++--- lib/dataset/data-view.co | 79 +++++++++++---- lib/dataset/dataset-model.co | 1 - lib/dataset/dataset-view.co | 19 ++-- lib/dataset/datasource-model.co | 2 - lib/dataset/datasource-ui-view.co | 26 ++++-- lib/dataset/datasource-view.co | 1 - lib/dataset/metric-edit-view.co | 8 +- lib/dataset/metric-model.co | 2 - lib/graph/graph-display-view.co | 1 - lib/graph/graph-edit-view.co | 210 +++++++++++++++++++++++++++--------- lib/graph/graph-model.co | 26 +++-- lib/main-display.co | 3 +- lib/main-edit.co | 3 +- lib/scaffold/scaffold-model.co | 3 - lib/scaffold/scaffold-view.co | 16 ++- lib/template/chart-scaffold.jade | 12 ++- lib/template/data.jade | 28 +++--- lib/template/dataset.jade | 49 ++++----- lib/template/datasource-ui.jade | 54 +++++++--- lib/template/graph-display.jade | 2 +- lib/template/graph-edit.jade | 97 +++++++++-------- lib/template/metric-edit.jade | 8 +- lib/util/backbone.co | 38 +++++++- www/css/data.styl | 102 +++++++++++++++++- www/css/graph.styl | 117 +++++++++++++------- www/css/layout.styl | 2 +- www/modules.yaml | 3 +- 30 files changed, 848 insertions(+), 330 deletions(-) diff --git a/lib/base.co b/lib/base.co index 225ea6e..1a818a3 100644 --- a/lib/base.co +++ b/lib/base.co @@ -5,26 +5,93 @@ Backbone = require 'backbone' -/** - * @class Base model, extending Backbone.Model, used by scaffold and others. - * @extends Backbone.Model - */ -BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{ - ctorName : 'BaseModel' +BaseBackboneMixin = exports.BaseBackboneMixin = - # A list of method-names to bind on initialize; set this on a subclass to override. + 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 + + + + ### 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}" + @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}" + @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}() )" + self.unwait() + fn ... + + +mixinBase = exports.mixinBase = (body) -> + _.clone(BaseBackboneMixin) import body + + +/** + * @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 - initialize: -> - _.bindAll this, ...@__bind__ if @__bind__.length + ### Accessors @@ -56,6 +123,9 @@ BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{ # + + + ### Serialization serialize: (v) -> @@ -93,12 +163,11 @@ BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{ toURL: -> "?#{@toKV ...}" - toString: -> "#{@ctorName}(id=#{@id})" + toString: -> "#{@..name or @..displayName}(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}. @@ -117,23 +186,18 @@ BaseModel import do * @class Base collection, extending Backbone.Collection, used by scaffold and others. * @extends Backbone.Collection */ -BaseList = exports.BaseList = Backbone.Collection.extend do # {{{ - ctorName : 'BaseList' - - # A list of method-names to bind on initialize; set this on a subclass to override. - __bind__ : [] - +BaseList = exports.BaseList = Backbone.Collection.extend mixinBase do # {{{ constructor : function BaseList @__class__ = @constructor @__superclass__ = @..__super__.constructor + @waitingOn = 0 Backbone.Collection ... @trigger 'create', this - initialize : -> - _.bindAll this, ...@__bind__ if @__bind__.length + ### Serialization toKVPairs: -> _.collapseObject @toJSON() @@ -144,7 +208,7 @@ BaseList = exports.BaseList = Backbone.Collection.extend do # {{{ toURL: (item_delim='&', kv_delim='=') -> "?#{@toKV ...}" - toString: -> "#{@ctorName}(length=#{@length})" + toString: -> "#{@..name or @..displayName}(length=#{@length})" # }}} @@ -152,17 +216,12 @@ BaseList = exports.BaseList = Backbone.Collection.extend do # {{{ * @class Base view, extending Backbone.View, used by scaffold and others. * @extends Backbone.View */ -BaseView = exports.BaseView = Backbone.View.extend do # {{{ - ctorName : 'BaseView' - - /** - * A list of method-names to bind on initialize; set this on a subclass to override. - * @type Array - */ - __bind__ : [] +BaseView = exports.BaseView = Backbone.View.extend mixinBase do # {{{ + tagName : 'section' /** - * @type Array + * Array of [view, selector]-pairs. + * @type Array<[BaseView, String]> */ subviews : [] @@ -171,12 +230,13 @@ BaseView = exports.BaseView = Backbone.View.extend do # {{{ constructor : function BaseView @__class__ = @constructor @__superclass__ = @..__super__.constructor + @waitingOn = 0 @subviews = [] Backbone.View ... @trigger 'create', this initialize: -> - _.bindAll this, ...@__bind__ if @__bind__.length + @__apply_bind__() @setModel @model @build() @@ -196,24 +256,58 @@ BaseView = exports.BaseView = Backbone.View.extend do # {{{ @model.on 'destroy', @remove, this @model + + + ### Subviews + + addSubview: (selector, view) -> + [view, selector] = [selector, null] unless view + @subviews.push [view, selector] + view + + removeSubview: (view) -> + for [v, sel], idx of @subviews + if v is view + @subviews.splice(idx, 1) + return [v, sel] + null + + hasSubview: (view) -> + _.any @subviews, ([v]) -> v is view + + attachSubviews: -> + for [view, selector] of @subviews + return unless view + view.undelegateEvents() + return unless el = view.render()?.el + if selector + @$el.find selector .append el + else + @$el.append el + view.delegateEvents() + this + + + ### Rendering Chain + toTemplateLocals: -> json = {value:v} = @model.toJSON() if _.isArray(v) or _.isObject(v) json.value = JSON.stringify v - { $, _, op, @model, view:this } import json + json $template: (locals={}) -> - $ @template @toTemplateLocals() import locals + $ @template do + { $, _, op, @model, view:this } import @toTemplateLocals() import locals build: -> return this unless @template - outer = @$template() @$el.html outer.html() .attr do id : outer.attr 'id' class : outer.attr('class') - + @attachSubviews() this render: -> @@ -221,6 +315,15 @@ BaseView = exports.BaseView = Backbone.View.extend do # {{{ @trigger 'render', this this + renderSubviews: -> + _.invoke _.pluck(@subviews, 0), 'render' + this + + + + + ### UI Utilities + hide : -> @$el.hide(); this show : -> @$el.show(); this remove : -> @$el.remove(); this @@ -239,7 +342,7 @@ BaseView = exports.BaseView = Backbone.View.extend do # {{{ # @$el.appendTo parent if parent?.length # this - toString : -> "#{@ctorName}(model=#{@model})" + toString : -> "#{@..name or @..displayName}(model=#{@model})" # Proxy model methods diff --git a/lib/chart/chart-option-model.co b/lib/chart/chart-option-model.co index 7cf2bc7..a531efe 100644 --- a/lib/chart/chart-option-model.co +++ b/lib/chart/chart-option-model.co @@ -44,7 +44,6 @@ KNOWN_TAGS = exports.KNOWN_TAGS = new TagSet() * @class Field with chart-option-specific handling for validation, parsing, tags, etc. */ ChartOption = exports.ChartOption = Field.extend do # {{{ - ctorName : 'ChartOption' IGNORED_TAGS : <[ callback deprecated debugging ]> @@ -113,7 +112,6 @@ ChartOption = exports.ChartOption = Field.extend do # {{{ * @class List of ChartOption fields. */ ChartOptionList = exports.ChartOptionList = FieldList.extend do # {{{ - ctorName : 'ChartOptionList' model : ChartOption diff --git a/lib/chart/chart-option-view.co b/lib/chart/chart-option-view.co index 369c198..ff59a27 100644 --- a/lib/chart/chart-option-view.co +++ b/lib/chart/chart-option-view.co @@ -12,7 +12,6 @@ DEBOUNCE_RENDER = exports.DEBOUNCE_RENDER = 100ms */ ChartOptionView = exports.ChartOptionView = FieldView.extend do # {{{ # __bind__ : <[ onClick ]> - ctorName : 'ChartOptionView' tagName : 'div' className : 'field option' template : require 'kraken/template/chart-option' @@ -36,18 +35,39 @@ ChartOptionView = exports.ChartOptionView = FieldView.extend do # {{{ @$el.addClass 'collapsed' if @isCollapsed this + + /** + * 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 + onClick: (evt) -> target = $ evt.target - # console.log "#this.onClick()", target @toggleCollapsed() if @$el.hasClass('collapsed') and not target.hasClass('close') - toggleCollapsed: -> - starting = @$el.hasClass 'collapsed' #@isCollapsed - @$el.toggleClass 'collapsed' - @isCollapsed = not starting - # console.log "#this.toggleCollapsed!", starting, '->', @isCollapsed - @trigger 'change:collapse', this, @isCollapsed - this # }}} @@ -57,16 +77,23 @@ ChartOptionView = exports.ChartOptionView = FieldView.extend do # {{{ * @class View for configuring a chart type. */ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{ - ctorName : 'ChartOptionScaffold' + __bind__ : <[ collapseAll expandAll ]> tagName : 'form' className : 'options scaffold' template : require 'kraken/template/chart-scaffold' + collectionType : ChartOptionList subviewType : ChartOptionView fields : '.fields' + events: + 'click .options-filter-button' : 'onFilterOptions' + 'click .collapse-all-options-button' : 'collapseAll' + 'click .expand-all-options-button' : 'expandAll' + # GraphView will set this - ready : false + ready : false + constructor: function ChartOptionScaffold @@ -76,8 +103,9 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{ @render = _.debounce @render.bind(this), DEBOUNCE_RENDER Scaffold::initialize ... + render: -> - # console.log "#this.render() -> .isotope()" + console.log "#this.render(ready=#{@ready}) -> .isotope()" # Scaffold::render ... return this unless @ready container = if @fields then @$el.find @fields else @$el @@ -86,13 +114,35 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{ .find '.field.option' .addClass 'isotope-item' container.isotope do # itemPositionDataEnabled : true - itemSelector : '.field.option' - layoutMode : 'masonry' - masonry : columnWidth : 10 - getSortData : + itemSelector : '.field.option' + layoutMode : 'masonry' + masonry : { columnWidth:10 } + filter : @getOptionsFilter() + sortBy : 'category' + getSortData : category: ($el) -> $el.data 'model' .getCategory() - sortBy: 'category' + this + + getOptionsFilter: -> + data = @$el.find '.options-filter-button.active' .toArray().map -> $ it .data() + sel = data.reduce do + (sel, d) -> + sel += that if d.filter + sel + '' + sel + + collapseAll: -> + _.invoke @_subviews, 'collapse', true + # @renderSubviews() + false + + expandAll: -> + _.invoke @_subviews, 'collapse', false + # @renderSubviews() + false + /** * Add a ChartOption to this scaffold, rerendering the isotope @@ -100,12 +150,17 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{ */ addOne: (field) -> view = Scaffold::addOne ... - view.on 'change:collapse render', @render + view.on 'change:collapse render', @render, this view toKV: -> @collection.toKV ... + + onFilterOptions: (evt) -> + evt.preventDefault() + _.defer @render + # }}} diff --git a/lib/dataset/data-view.co b/lib/dataset/data-view.co index 75439f9..89d5be6 100644 --- a/lib/dataset/data-view.co +++ b/lib/dataset/data-view.co @@ -13,12 +13,12 @@ Seq = require 'seq' * @class */ DataView = exports.DataView = BaseView.extend do # {{{ - ctorName : 'DataView' + __bind__ : <[ onReady ]> tagName : 'section' className : 'data-ui' template : require 'kraken/template/data' - data : {} + datasources : {} @@ -28,36 +28,77 @@ DataView = exports.DataView = BaseView.extend do # {{{ initialize: -> @graph_id = @options.graph_id BaseView::initialize ... - + @on 'ready', @onReady @load() - - # @subviews.push @dataset_view = new DataSetView {@model, @graph_id} - # @$el.append @dataset_view.render().el - # @dataset_view.on 'edit-metric', @editMetric, this - # - # @subviews.push @metric_edit_view = new MetricEditView {dataset:@model, @graph_id} - # @$el.append @metric_edit_view.render().hide().el - - - toTemplateLocals: -> - attrs = _.clone @model.attributes - { $, _, op, @model, view:this, @data, @graph_id } import attrs load: -> - $.getJSON '/datasources/all', (@data) ~> + $.getJSON '/datasources/all', (@datasources) ~> + @canonicalizeDataSources @datasources @ready = true @render() @trigger 'ready', this + /** + * Transform the `columns` field to ensure an Array of {label, type} objects. + */ + canonicalizeDataSources: (datasources) -> + _.each datasources, (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'} + datasources + + + onReady: -> + @metric_edit_view = @addSubview new MetricEditView {@graph_id, dataset:@model, @datasources} + @metric_edit_view + .on 'update', @onUpdateMetric, this + + @dataset_view = @addSubview new DataSetView {@model, @graph_id, @dataset, @datasources} + @dataset_view + .on 'add-metric', @onMetricsChanged, this + .on 'remove-metric', @onMetricsChanged, this + .on 'edit-metric', @editMetric, this + + @attachSubviews() + this + + + toTemplateLocals: -> + attrs = _.clone @model.attributes + { $, _, op, @model, view:this, @graph_id, @datasources, } import attrs + + # attachSubviews: -> + # @$el.empty() + # BaseView::attachSubviews ... + # @$el.append '
' + # this + # Don't rebuild HTML, simply notify subviews render: -> - BaseView::render ... - - # _.invoke @subviews, 'render' + @renderSubviews() + @trigger 'render', this this editMetric: (metric) -> @metric_edit_view.editMetric metric + onMetricsChanged: -> + @$el.css 'min-height', @dataset_view.$el.height() + + onUpdateMetric: -> + @renderSubviews() # }}} diff --git a/lib/dataset/dataset-model.co b/lib/dataset/dataset-model.co index 8aa5262..c5b591d 100644 --- a/lib/dataset/dataset-model.co +++ b/lib/dataset/dataset-model.co @@ -16,7 +16,6 @@ ColorBrewer = require 'colorbrewer' * @class */ DataSet = exports.DataSet = BaseModel.extend do # {{{ - ctorName : 'DataSet' urlRoot : '/datasets' /** diff --git a/lib/dataset/dataset-view.co b/lib/dataset/dataset-view.co index eadaff0..b98169c 100644 --- a/lib/dataset/dataset-view.co +++ b/lib/dataset/dataset-view.co @@ -8,7 +8,6 @@ * @class */ DataSetView = exports.DataSetView = BaseView.extend do # {{{ - ctorName : 'DataSetView' tagName : 'section' className : 'dataset-ui dataset' template : require 'kraken/template/dataset' @@ -32,6 +31,7 @@ DataSetView = exports.DataSetView = BaseView.extend do # {{{ newMetric: -> + console.log "#this.newMetric!" # triggers 'add' on @model.metrics @model.newMetric() false @@ -39,27 +39,33 @@ DataSetView = exports.DataSetView = BaseView.extend do # {{{ addMetric: (metric) -> console.log "#this.addMetric!", metric if metric.view - _.remove @subviews, metric.view + @removeSubview metric.view delete @views_by_cid[metric.cid] - @subviews.push view = new DataSetMetricView {model:metric, @graph_id} + view = @addSubview new DataSetMetricView {model:metric, @graph_id} @views_by_cid[metric.cid] = view @$el.find '.metrics' .append view.render().el # @render() + @trigger 'add-metric', metric, view, this view editMetric: (metric) -> console.log "#this.editMetric!", metric if metric instanceof [jQuery.Event, Event] metric = $ metric.currentTarget .data 'model' - view = @views_by_cid[metric.cid] + view = @active_view = @views_by_cid[metric.cid] console.log ' --> metric:', metric, 'view:', view - @$el.find '.metrics' .removeClass 'metric-active' + @$el.find '.metrics .dataset-metric' .removeClass 'metric-active' view.$el.addClass 'metric-active' view.$el.find '.activity-arrow' .css 'font-size', 2+view.$el.height() - @trigger 'edit-metric', metric + + @trigger 'edit-metric', metric, view, this + this + + render: -> + this # }}} @@ -69,7 +75,6 @@ DataSetView = exports.DataSetView = BaseView.extend do # {{{ * @class */ DataSetMetricView = exports.DataSetMetricView = BaseView.extend do # {{{ - ctorName : 'DataSetMetricView' tagName : 'tr' className : 'dataset-metric metric' template : require 'kraken/template/dataset-metric' diff --git a/lib/dataset/datasource-model.co b/lib/dataset/datasource-model.co index ef23d98..1d31584 100644 --- a/lib/dataset/datasource-model.co +++ b/lib/dataset/datasource-model.co @@ -8,7 +8,6 @@ * @class */ DataSource = exports.DataSource = BaseModel.extend do # {{{ - ctorName : 'DataSource' urlRoot : '/datasources' @@ -33,7 +32,6 @@ DataSource = exports.DataSource = BaseModel.extend do # {{{ * @class */ DataSourceList = exports.DataSourceList = BaseList.extend do # {{{ - ctorName : 'DataSourceList' urlRoot : '/datasources' model : DataSource diff --git a/lib/dataset/datasource-ui-view.co b/lib/dataset/datasource-ui-view.co index 25d52d1..43b92b9 100644 --- a/lib/dataset/datasource-ui-view.co +++ b/lib/dataset/datasource-ui-view.co @@ -9,24 +9,36 @@ */ DataSourceUIView = exports.DataSourceUIView = BaseView.extend do # {{{ __bind__ : <[ ]> - ctorName : 'DataSourceUIView' tagName : 'section' className : 'datasource-ui' template : require 'kraken/template/datasource-ui' + events : + 'click .datasource-summary': 'onHeaderClick' + + graph_id : null + dataset : null + datasources : null + + constructor: function DataSourceUIView BaseView ... initialize: -> - @graph_id = @options.graph_id + this import @options.{graph_id, dataset, datasources} BaseView::initialize ... toTemplateLocals: -> locals = @model.toJSON() - locals import - graph_id : @graph_id - source_summary : 'Source Summary' - metric_summary : 'Metric Summary' - timespan_summary : 'Timespan Summary' + locals import { + @graph_id, @dataset, @datasources, + source_summary : '' + timespan_summary : ' p.help-block A description of the graph. - - .graph-data-pane.tab-pane(id="#{graph_id}-tab-data") + + .graph-data-pane.tab-pane(id="#{graph_id}-tab-data") + //- .row-fluid - //- - label.dataset.control-label(for="#{graph_id}_dataset") Data Set - input.span3.dataset(type='text', id="#{graph_id}_dataset", name='dataset', placeholder='URL to dataset file', value=dataset) - p.help-block This dataset filename will soon be replaced by a friendly UI. - - .graph-options-pane.tab-pane(id="#{graph_id}-tab-options") + label.dataset.control-label(for="#{graph_id}_dataset") Data Set + input.span3.dataset(type='text', id="#{graph_id}_dataset", name='dataset', placeholder='URL to dataset file', value=dataset) + p.help-block This dataset filename will soon be replaced by a friendly UI. + + .graph-options-pane.tab-pane(id="#{graph_id}-tab-options") diff --git a/lib/template/metric-edit.jade b/lib/template/metric-edit.jade index f48442a..ffe0a7f 100644 --- a/lib/template/metric-edit.jade +++ b/lib/template/metric-edit.jade @@ -1,14 +1,14 @@ section.metric-edit-ui .inner: form.form-horizontal - .metric-header.control-group.row-fluid - .color-picker + .metric-header.control-group + .color-swatch(style="background-color: #{color};") input.metric-color(type='hidden', id="#{graph_id}_metric", name="color", value=color) input.metric-label(type='text', id="#{graph_id}_metric_label", name='label', placeholder='Metric Label', value=label) - .metric-datasource.row-fluid + .metric-datasource.control-group - .metric-actions.row-fluid + .metric-actions.control-group a.delete-button.btn.btn-danger(href="#") i.icon-remove.icon-white | Delete diff --git a/lib/util/backbone.co b/lib/util/backbone.co index d867674..4ecfb14 100644 --- a/lib/util/backbone.co +++ b/lib/util/backbone.co @@ -1,3 +1,4 @@ +### Patches to make Backbone work with browserify # Expose Underscore so Backbone can find it _ = require 'underscore' @@ -11,22 +12,55 @@ window?.Backbone = Backbone Backbone.setDomLibrary that if window? and (window.jQuery or window.Zepto or window.ender) + + +/** + * @namespace Meta-utilities for working with Backbone classes. + */ _backbone = do /** * @returns {Array} The list of all superclasses for this class or object. */ getSuperClasses: function getSuperClasses(Cls) - Cls .= constructor if cls and typeof Cls is not 'function' - if superclass = Cls?.__super__?.constructor + return [] unless Cls + + if Cls.__superclass__ + superclass = that + else + Cls = Cls.constructor unless typeof Cls is 'function' + superclass = Cls.__super__?.constructor + + if superclass [superclass].concat getSuperClasses superclass else [] + /** + * Looks up an attribute on the prototype of each class in the class + * hierarchy. + * @returns {Array} + */ + pluckSuper: (obj, prop) -> + return [] unless obj + _ _backbone.getSuperClasses(obj) .chain() + .pluck 'prototype' + .pluck prop + .value() + + /** + * As `.pluckSuper()` but includes value of `prop` on passed `obj`. + * @returns {Array} + */ + pluckSuperAndSelf: (obj, prop) -> + return [] unless obj + [ obj[prop] ].concat _backbone.pluckSuper(obj, prop) + exports import _backbone + /** * Decorates a function so that its receiver (`this`) is always added as the * first argument, followed by the call arguments. diff --git a/www/css/data.styl b/www/css/data.styl index e971d11..9ab1258 100644 --- a/www/css/data.styl +++ b/www/css/data.styl @@ -5,6 +5,10 @@ section.graph section.data-ui + height 100% + min-height 300px + clearfix() + h4 display none @@ -13,7 +17,10 @@ section.graph section.data-ui /* * * * DataSet UI * * * */ section.dataset-ui + absolute 0 0 width 40% + height 100% + min-height 300px border-right 1px solid #ccc .inner @@ -93,13 +100,102 @@ section.graph section.data-ui /* * * * Edit Metric UI * * * */ section.metric-edit-ui - position absolute - top 0 - left 40% + display none + float right + width 60% + min-height 100% .inner padding 1em + .metric-header + min-height 38px + font-size 18px + line-height 25px + + .color-swatch + absolute top 3px right 0 + width 30px + height 30px + border 1px solid #333 + border-color #ddd #333 #333 #ddd + border-radius 3px + cursor pointer + + input + display block + absolute top 0 left 0 + right 38px + width auto + font-size 18px + line-height 25px + height 25px + padding 6px + /* * * * DataSource UI * * * */ + section.datasource-ui + + // Collapse/Expand icon + i + z-index 100 + .expand-datasource-ui-button + display block + .collapse-datasource-ui-button + display none + &.in + .expand-datasource-ui-button + display none + .collapse-datasource-ui-button + display block + + // Summary Header + .datasource-summary + &, &:hover + cursor pointer + i + absolute top 50% right 14px + margin-top -7px + .breadcrumb + margin-bottom 0 + font-weight bold + // &, .breadcrumb + // color #3a87ad + // background-color #d9edf7 + // border-color #bce8f1 + // background-image linear-gradient(top, #d9edf7, darken(#d9edf7, 10%)) + + // Selector UI (with tabs per-source, info, etc) + .datasource-selector + .datasource-tabs + top -1px + padding 7px + border 1px solid #ddd + border-top 0 + border-radius 3px + + h1, h2, h3, h4, h5, h6 + margin 0 -1px 3px 0 + padding 8px 12px + min-width 74px + + .datasource-sources-list + height 100% + display table-cell + float none + max-width 200px + + .datasource-sources-details + display table-cell + padding 1em + width auto + + + + + + .data-ui + clearfix() + + diff --git a/www/css/graph.styl b/www/css/graph.styl index 627748c..ce12b87 100644 --- a/www/css/graph.styl +++ b/www/css/graph.styl @@ -3,13 +3,29 @@ section.graph position relative - max-width 900px margin 0 auto + min-width 640px + max-width 960px * position relative + .graph-details > * + margin-left auto + margin-right auto + + /* * * * Chart & Viewport * * * {{{ */ + .graph-viewport-row + // margin 1em 1em 3em + margin-bottom 3em + + .viewport + position relative + min-width 200px + min-height 320px + overflow hidden + .graph-label position absolute z-index 100 @@ -18,9 +34,10 @@ section.graph width 200px padding 1em - border-radius 5px background-color rgba(255,255,255, 0.75) font 12px/1.5 "helvetica neue", helvetica, arial, sans-serif + border 1px solid $light + border-radius 5px b display inline-block @@ -37,19 +54,44 @@ section.graph .viewport:hover + .graph-label border 1px solid $light - .viewport - position relative - min-width 200px - min-height 320px - margin-bottom 1.5em - overflow hidden + /* }}} */ + + /* * * * Graph Details & Info Pane * * * {{{ */ + .graph-name-row + // margin 0 1em 1em + // max-width 900px + font-size 120% + line-height 2em + input.graph-name + font-size 120% + line-height 1.2 + height 1.2em + border-color $light + width 98% + .graph-info-pane + .row-fluid + .half.control-group + width 50% + float left + margin-left 0 + margin-right 0 + label + width 100px + .controls + margin-left 110px + .help-block + font-size 11px + line-height 1.3 /* }}} */ /* * * * Subnav & Tabs * * * {{{ */ + .graph-settings-row + max-width 900px + .graph-settings.tabbable - .nav + .graph-settings-nav > .nav margin-bottom 0 li h3 @@ -60,13 +102,15 @@ section.graph li margin-right 4px - .tab-pane + .graph-tab-content > .tab-pane padding 0.5em - margin-top 18px + margin-top 1em + height 100% &.graph-data-pane margin-top 0 padding 0 + border-bottom 1px solid #ddd .graph-controls z-index 100 @@ -82,41 +126,32 @@ section.graph min-width 5em text-align center - /* }}} */ - + .graph-spinner + display none + absolute bottom 3px left 0 + margin-left -2.0em + width 2.5em + height 2.5em - /* * * * Graph Details * * * {{{ */ - form.details - position relative - - .name-row - font-size 120% - line-height 2em - input.name - font-size 120% - line-height 1.2 - height 1.2em - border-color $light - width 98% - .row-fluid - .half.control-group - width 50% - float left - margin-left 0 - margin-right 0 - label - width 100px - .controls - margin-left 110px - .help-block - font-size 11px - line-height 1.3 /* }}} */ /* * * * Chart Options * * * {{{ */ - .options fieldset - border 0px + .graph-options + + .graph-options-controls + font-size 11px + + .control-group, .btn-group + display inline-block + font-size 11px + + .btn + font-size 11px + padding 3px 9px 3px + & > .btn, & :not(.btn-group) .btn + margin-right 0.75em + .field.option float left diff --git a/www/css/layout.styl b/www/css/layout.styl index 048da3f..f9d775a 100644 --- a/www/css/layout.styl +++ b/www/css/layout.styl @@ -21,7 +21,7 @@ header, footer, #content & > .inner position relative margin 0 auto - max-width 960px + // max-width 960px &.fluid-inner width 80% diff --git a/www/modules.yaml b/www/modules.yaml index c217b2d..93bb9d9 100644 --- a/www/modules.yaml +++ b/www/modules.yaml @@ -18,9 +18,8 @@ dev: - jquery.history.min - jquery.hotkeys.min - jquery.isotope.min + - jquery.spin.min - bootstrap.min - # - spin.min - # - jquery.spin.min ### CommonJS Support Starts Here: ### Browserify must come before any .mod files -- 1.7.0.4