First pass at data UI, mostly inactive due to time. Adds simple dropdown for datasets.
authordsc <dsc@wikimedia.org>
Wed, 4 Apr 2012 20:04:22 +0000 (13:04 -0700)
committerdsc <dsc@wikimedia.org>
Wed, 4 Apr 2012 20:04:22 +0000 (13:04 -0700)
33 files changed:
lib/base.co
lib/chart/chart-option-model.co
lib/chart/chart-option-view.co
lib/dataset/data-view.co [new file with mode: 0644]
lib/dataset/dataset-model.co [new file with mode: 0644]
lib/dataset/dataset-view.co [new file with mode: 0644]
lib/dataset/datasource-model.co [new file with mode: 0644]
lib/dataset/datasource-ui-view.co [new file with mode: 0644]
lib/dataset/datasource-view.co [new file with mode: 0644]
lib/dataset/index.co [new file with mode: 0644]
lib/dataset/metric-edit-view.co [new file with mode: 0644]
lib/dataset/metric-model.co [new file with mode: 0644]
lib/dataset/metric-view.co [new file with mode: 0644]
lib/graph/graph-display-view.co
lib/graph/graph-edit-view.co
lib/graph/graph-model.co
lib/scaffold/scaffold-model.co
lib/scaffold/scaffold-view.co
lib/server/controllers/datasource.co
lib/server/server.co
lib/template/data.jade [new file with mode: 0644]
lib/template/dataset-metric.jade [new file with mode: 0644]
lib/template/dataset.jade [new file with mode: 0644]
lib/template/datasource-ui.jade [new file with mode: 0644]
lib/template/datasource.jade [new file with mode: 0644]
lib/template/graph-edit.jade
lib/template/metric-edit.jade [new file with mode: 0644]
lib/template/metric.jade [new file with mode: 0644]
lib/util/op.co
www/css/bootstrap-variables.styl [new file with mode: 0644]
www/css/colors.styl
www/css/data.styl [new file with mode: 0644]
www/modules.yaml

index f1f28f7..2ac0400 100644 (file)
@@ -1,5 +1,5 @@
-_  = require 'kraken/util/underscore'
-op = require 'kraken/util/op'
+{ _, op,
+} = require 'kraken/util'
 
 Backbone = require 'backbone'
 
@@ -19,9 +19,7 @@ BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{
     
     constructor : function BaseModel
         @__class__      = @constructor
-        @__super__      = @constructor.__super__
-        @__superclass__ = @__super__.constructor
-        # Backbone.NestedModel ...
+        @__superclass__ = @..__super__.constructor
         Backbone.Model ...
         @trigger 'create', this
     
@@ -50,7 +48,7 @@ BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{
     #         if _.str.contains key, '.'
     #             _.setNested @attributes, key, value, opts
     #         else
-    #             @__super__.set.call this, key, value, opts
+    #             Backbone.Model::set.call this, key, value, opts
     #     
     #     this
     # 
@@ -126,8 +124,14 @@ BaseList = exports.BaseList = Backbone.Collection.extend do # {{{
     __bind__ : []
     
     
+    
+    constructor : function BaseList
+        @__class__      = @constructor
+        @__superclass__ = @..__super__.constructor
+        Backbone.Collection ...
+        @trigger 'create', this
+    
     initialize : ->
-        @__super__ = @constructor.__super__
         _.bindAll this, ...@__bind__ if @__bind__.length
     
     
@@ -151,13 +155,27 @@ BaseList = exports.BaseList = Backbone.Collection.extend do # {{{
 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.
+    /**
+     * A list of method-names to bind on initialize; set this on a subclass to override.
+     * @type Array<String>
+     */
     __bind__ : []
     
+    /**
+     * @type Array<BaseView>
+     */
+    subviews : []
+    
     
     
+    constructor : function BaseView
+        @__class__      = @constructor
+        @__superclass__ = @..__super__.constructor
+        @subviews       = []
+        Backbone.View ...
+        @trigger 'create', this
+    
     initialize: ->
-        @__super__ = @constructor.__super__
         _.bindAll this, ...@__bind__ if @__bind__.length
         
         @model.view = this
index e5277f8..7cf2bc7 100644 (file)
@@ -48,6 +48,9 @@ ChartOption = exports.ChartOption = Field.extend do # {{{
     IGNORED_TAGS : <[ callback deprecated debugging ]>
     
     
+    constructor: function ChartOption
+        Field ...
+    
     initialize : ->
         # console.log "#this.initialize!"
         Field::initialize ...
@@ -113,6 +116,10 @@ ChartOptionList = exports.ChartOptionList = FieldList.extend do # {{{
     ctorName   : 'ChartOptionList'
     model      : ChartOption
     
+    
+    constructor: function ChartOptionList
+        FieldList ...
+    
     /**
      * Override to omit defaults from URL.
      */
index fa68de9..369c198 100644 (file)
@@ -28,8 +28,11 @@ ChartOptionView = exports.ChartOptionView = FieldView.extend do # {{{
         'click .collapsed'                   : 'onClick'
     
     
+    constructor: function ChartOptionView
+        FieldView ...
+    
     render: ->
-        @__super__.render ...
+        FieldView::render ...
         @$el.addClass 'collapsed' if @isCollapsed
         this
     
@@ -60,12 +63,14 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{
     template       : require 'kraken/template/chart-scaffold'
     collectionType : ChartOptionList
     subviewType    : ChartOptionView
-    fields        : '.fields'
+    fields         : '.fields'
     
     # GraphView will set this
     ready          : false
     
     
+    constructor: function ChartOptionScaffold
+        Scaffold ...
     
     initialize : ->
         @render = _.debounce @render.bind(this), DEBOUNCE_RENDER
@@ -73,7 +78,7 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{
     
     render: ->
         # console.log "#this.render() -> .isotope()"
-        # @__super__.render ...
+        # Scaffold::render ...
         return this unless @ready
         container = if @fields then @$el.find @fields else @$el
         container
@@ -94,7 +99,7 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{
      * layout after collapse events.
      */
     addOne: (field) ->
-        view = @__super__.addOne ...
+        view = Scaffold::addOne ...
         view.on 'change:collapse render', @render
         view
     
diff --git a/lib/dataset/data-view.co b/lib/dataset/data-view.co
new file mode 100644 (file)
index 0000000..75439f9
--- /dev/null
@@ -0,0 +1,63 @@
+Seq = require 'seq'
+{ _, op,
+} = require 'kraken/util'
+{ BaseView,
+} = require 'kraken/base'
+{ DataSetView,
+} = require 'kraken/dataset/dataset-view'
+{ MetricEditView,
+} = require 'kraken/dataset/metric-edit-view'
+
+
+/**
+ * @class
+ */
+DataView = exports.DataView = BaseView.extend do # {{{
+    ctorName       : 'DataView'
+    tagName        : 'section'
+    className      : 'data-ui'
+    template       : require 'kraken/template/data'
+    
+    data : {}
+    
+    
+    
+    constructor: function DataView
+        BaseView ...
+    
+    initialize: ->
+        @graph_id = @options.graph_id
+        BaseView::initialize ...
+        
+        @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) ~>
+            @ready = true
+            @render()
+            @trigger 'ready', this
+    
+    # Don't rebuild HTML, simply notify subviews
+    render: ->
+        BaseView::render ...
+        
+        # _.invoke @subviews, 'render'
+        this
+    
+    editMetric: (metric) ->
+        @metric_edit_view.editMetric metric
+    
+# }}}
diff --git a/lib/dataset/dataset-model.co b/lib/dataset/dataset-model.co
new file mode 100644 (file)
index 0000000..1b40c21
--- /dev/null
@@ -0,0 +1,61 @@
+Seq = require 'seq'
+ColorBrewer = require 'colorbrewer'
+
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList,
+} = require 'kraken/base'
+{ Metric, MetricList,
+} = require 'kraken/dataset/metric-model'
+{ DataSource, DataSourceList,
+} = require 'kraken/dataset/datasource-model'
+
+
+
+/**
+ * @class
+ */
+DataSet = exports.DataSet = BaseModel.extend do # {{{
+    ctorName : 'DataSet'
+    urlRoot : '/datasets'
+    
+    /**
+     * @type DataSourceList
+     */
+    sources : []
+    
+    /**
+     * @type MetricList
+     */
+    metrics : []
+    
+    
+    constructor: function DataSet
+        BaseModel ...
+    
+    initialize : ->
+        BaseModel::initialize ...
+        @sources = new DataSourceList @attributes.sources
+        @metrics = new MetricList     @attributes.metrics
+    
+    
+    defaults : ->
+        sources : [] # XXX: needed? metrics now implies this info
+        metrics : []
+        # lines   : []
+    
+    
+    /**
+     * @returns {Array} The reified dataset, materialized to an array of data-series arrays.
+     */
+    getData: ->
+        '/data/datasources/non_mobile_pageviews_by.timestamp.language.csv'
+    
+    newMetric: ->
+        index = @metrics.length
+        @metrics.add m = new Metric { index, color:ColorBrewer.Spectral[11][index] }
+        m
+    
+    
+# }}}
+
diff --git a/lib/dataset/dataset-view.co b/lib/dataset/dataset-view.co
new file mode 100644 (file)
index 0000000..eadaff0
--- /dev/null
@@ -0,0 +1,111 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseView,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+DataSetView = exports.DataSetView = BaseView.extend do # {{{
+    ctorName  : 'DataSetView'
+    tagName   : 'section'
+    className : 'dataset-ui dataset'
+    template  : require 'kraken/template/dataset'
+    
+    events:
+        'click .new-metric-button'       : 'newMetric'
+        'click .metrics .dataset-metric' : 'editMetric'
+    
+    views_by_cid : {}
+    active_view : null
+    
+    
+    constructor: function DataSetView
+        BaseView ...
+    
+    initialize: ->
+        @graph_id = @options.graph_id
+        BaseView::initialize ...
+        @views_by_cid = {}
+        @model.metrics.on 'add', @addMetric, this
+    
+    
+    newMetric: ->
+        # triggers 'add' on @model.metrics
+        @model.newMetric()
+        false
+    
+    addMetric: (metric) ->
+        console.log "#this.addMetric!", metric
+        if metric.view
+            _.remove @subviews, metric.view
+            delete @views_by_cid[metric.cid]
+        
+        @subviews.push view = new DataSetMetricView {model:metric, @graph_id}
+        @views_by_cid[metric.cid] = view
+        @$el.find '.metrics' .append view.render().el
+        
+        # @render()
+        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]
+        console.log '  --> metric:', metric, 'view:', view
+        
+        @$el.find '.metrics' .removeClass 'metric-active'
+        view.$el.addClass 'metric-active'
+        view.$el.find '.activity-arrow' .css 'font-size', 2+view.$el.height()
+        @trigger 'edit-metric', metric
+    
+# }}}
+
+
+
+/**
+ * @class
+ */
+DataSetMetricView = exports.DataSetMetricView = BaseView.extend do # {{{
+    ctorName  : 'DataSetMetricView'
+    tagName   : 'tr'
+    className : 'dataset-metric metric'
+    template  : require 'kraken/template/dataset-metric'
+    
+    
+    
+    constructor: function DataSetMetricView
+        BaseView ...
+    
+    initialize: ->
+        @graph_id = @options.graph_id
+        BaseView::initialize ...
+    
+    
+    toTemplateLocals: ->
+        m = @model.toJSON()
+        m import
+            graph_id : @graph_id
+            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_name
+                    "#{m.source_id}.#{m.source_col_name}"
+                else
+                    'No source'
+            timespan :
+                if _.every ts = m.timespan, op.ok
+                    "#{ts.start} to #{ts.end} by #{ts.step}"
+                else
+                    '&mdash;'
+        
+        # XXX: Icons/classes for visible/disabled?
+        { $, _, op, @model, view:this } import m
+    
+# }}}
+
diff --git a/lib/dataset/datasource-model.co b/lib/dataset/datasource-model.co
new file mode 100644 (file)
index 0000000..ef23d98
--- /dev/null
@@ -0,0 +1,47 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList, BaseView,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+DataSource = exports.DataSource = BaseModel.extend do # {{{
+    ctorName : 'DataSource'
+    urlRoot  : '/datasources'
+    
+    
+    constructor: function DataSource
+        BaseModel ...
+    
+    initialize: ->
+        BaseModel::initialize ...
+        
+    
+    defaults: ->
+        {}
+    
+    url: ->
+        "/data/#{@id}.json"
+    
+# }}}
+
+
+
+/**
+ * @class
+ */
+DataSourceList = exports.DataSourceList = BaseList.extend do # {{{
+    ctorName : 'DataSourceList'
+    urlRoot  : '/datasources'
+    model    : DataSource
+    
+    constructor: function DataSourceList
+        BaseList ...
+    
+    initialize : ->
+        BaseList::initialize ...
+    
+    
+# }}}
diff --git a/lib/dataset/datasource-ui-view.co b/lib/dataset/datasource-ui-view.co
new file mode 100644 (file)
index 0000000..25d52d1
--- /dev/null
@@ -0,0 +1,32 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList, BaseView,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+DataSourceUIView = exports.DataSourceUIView = BaseView.extend do # {{{
+    __bind__       : <[  ]>
+    ctorName       : 'DataSourceUIView'
+    tagName        : 'section'
+    className      : 'datasource-ui'
+    template       : require 'kraken/template/datasource-ui'
+    
+    
+    constructor: function DataSourceUIView
+        BaseView ...
+    
+    initialize: ->
+        @graph_id = @options.graph_id
+        BaseView::initialize ...
+    
+    toTemplateLocals: ->
+        locals = @model.toJSON()
+        locals import
+            graph_id         : @graph_id
+            source_summary   : 'Source Summary'
+            metric_summary   : 'Metric Summary'
+            timespan_summary : 'Timespan Summary'
+# }}}
diff --git a/lib/dataset/datasource-view.co b/lib/dataset/datasource-view.co
new file mode 100644 (file)
index 0000000..e7d6aa2
--- /dev/null
@@ -0,0 +1,25 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList, BaseView,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+DataSourceView = exports.DataSourceView = BaseView.extend do # {{{
+    __bind__       : <[  ]>
+    ctorName       : 'DataSourceView'
+    tagName        : 'section'
+    className      : 'datasource'
+    template       : require 'kraken/template/datasource'
+    
+    
+    
+    constructor: function DataSourceView
+        BaseView ...
+    
+    initialize: ->
+        BaseView::initialize ...
+    
+# }}}
diff --git a/lib/dataset/index.co b/lib/dataset/index.co
new file mode 100644 (file)
index 0000000..56d3e89
--- /dev/null
@@ -0,0 +1,14 @@
+metric_model       = require 'kraken/dataset/metric-model'
+metric_view        = require 'kraken/dataset/metric-view'
+metric_edit_view   = require 'kraken/dataset/metric-edit-view'
+datasource_model   = require 'kraken/dataset/datasource-model'
+datasource_view    = require 'kraken/dataset/datasource-view'
+datasource_ui_view = require 'kraken/dataset/datasource-ui-view'
+dataset_model      = require 'kraken/dataset/dataset-model'
+dataset_view       = require 'kraken/dataset/dataset-view'
+data_view          = require 'kraken/dataset/data-view'
+
+exports import metric_model     import metric_view      import metric_edit_view     \
+        import datasource_model import datasource_view  import datasource_ui_view   \
+        import dataset_model    import dataset_view                                 \
+                                import data_view
diff --git a/lib/dataset/metric-edit-view.co b/lib/dataset/metric-edit-view.co
new file mode 100644 (file)
index 0000000..b397ff5
--- /dev/null
@@ -0,0 +1,55 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseView,
+} = require 'kraken/base'
+{ Metric,
+} = require 'kraken/dataset/metric-model'
+{ DataSourceUIView,
+} = require 'kraken/dataset/datasource-ui-view'
+
+
+
+/**
+ * @class
+ */
+MetricEditView = exports.MetricEditView = BaseView.extend do # {{{
+    ctorName       : 'MetricEditView'
+    tagName        : 'section'
+    className      : 'metric-edit-ui'
+    template       : require 'kraken/template/metric-edit'
+    
+    graph_id           : null
+    dataset            : null
+    datasource_ui_view : null
+    
+    
+    constructor: function MetricEditView
+        BaseView ...
+    
+    initialize: ->
+        this import @options.{graph_id, dataset}
+        @model or= new Metric
+        BaseView::initialize ...
+        @subviews.push @datasource_ui_view = new DataSourceUIView {@model, @graph_id}
+        @$el.find '.metric-datasource' .append @datasource_ui_view.render().el
+    
+    
+    toTemplateLocals: ->
+        locals = BaseView::toTemplateLocals ...
+        locals import {@graph_id}
+    
+    build: ->
+        BaseView::build ...
+        if @datasource_ui_view
+            @$el.find '.metric-datasource' .append @datasource_ui_view.render().el
+        this
+    
+    editMetric: (metric) ->
+        console.log "#this.editMetric!", metric
+        @model = metric
+        @render()
+        @show()
+        this
+    
+# }}}
+
diff --git a/lib/dataset/metric-model.co b/lib/dataset/metric-model.co
new file mode 100644 (file)
index 0000000..63a88a0
--- /dev/null
@@ -0,0 +1,73 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+Metric = exports.Metric = BaseModel.extend do # {{{
+    ctorName : 'Metric'
+    urlRoot  : '/metrics'
+    
+    /**
+     * Data source of the Metric.
+     * @type DataSource
+     */
+    source : null
+    
+    
+    constructor: function Metric
+        BaseModel ...
+    
+    initialize : ->
+        BaseModel::initialize ...
+    
+    
+    defaults : ->
+        index           : 0
+        label           : 'New Metric'
+        type            : 'int'
+        timespan        : { start:null, stop:null, step:null }
+        disabled        : false
+        
+        # DataSource
+        source_id       : null
+        source_col_idx  : -1
+        source_col_name : ''
+        
+        # Chart Options
+        color           : null
+        visible         : true
+        format_value    : null
+        format_axis     : null
+        
+        scale           : 1.0
+        transforms      : []
+    
+    
+    /**
+     * Check whether the metric has aiight-looking values so we don't
+     * attempt to graph unconfigured crap.
+     */
+    isOk: ->
+        (label = @get('label')) and label is not 'New Metric'
+        and @get('source_id')   and @get('source_col_idx') >= 0
+        and _.every @get('timespan'), op.ok
+    
+    
+    
+# }}}
+
+
+/**
+ * @class
+ */
+MetricList = exports.MetricList = BaseList.extend do # {{{
+    ctorName : 'MetricList'
+    urlRoot  : '/metrics'
+    model    : Metric
+    
+    constructor: function MetricList then BaseList ...
+# }}}
diff --git a/lib/dataset/metric-view.co b/lib/dataset/metric-view.co
new file mode 100644 (file)
index 0000000..e69de29
index 8d24a78..cdbdfd3 100644 (file)
@@ -21,6 +21,9 @@ GraphDisplayView = exports.GraphDisplayView = BaseView.extend do # {{{
     #     'submit .value' : 'update'
     
     
+    constructor: function GraphDisplayView
+        BaseView ...
+    
     initialize: ->
         @model or= new Graph
         BaseView::initialize ...
index 49dca93..c95a6e8 100644 (file)
@@ -7,7 +7,7 @@ _ = require 'kraken/util/underscore'
 } = require 'kraken/chart'
 { Graph,
 } = require 'kraken/graph/graph-model'
-{ DataSetView,
+{ DataView, DataSetView, DataSet,
 } = require 'kraken/dataset'
 
 root = do -> this
@@ -45,29 +45,33 @@ GraphEditView = exports.GraphEditView = BaseView.extend do # {{{
         'keypress form.details input[type="text"]' : 'onKeypress'
         'keypress form.options .value'             : 'onKeypress'
         'submit   form.details'                    : 'onDetailsSubmit'
+        'change   select'                          : 'onDetailsSubmit'
         'submit   form.options'                    : 'onOptionsSubmit'
         'change   input[type="checkbox"]'          : 'onOptionsSubmit'
     
-    subviews: []
-    ready: false
+    data  : {}
+    ready : false
     
     
+    constructor: function GraphEditView
+        BaseView ...
     
     initialize : (o={}) ->
-        @subviews = []
+        @data = {}
         @model or= new Graph
+        @id = _.domize 'graph', (@model.id or @model.get('slug') or @model.cid)
         BaseView::initialize ...
         # console.log "#this.initialize!"
         
         for name of @__debounce__
             @[name] = _.debounce @[name], DEBOUNCE_RENDER
         
-        @id = _.domize 'graph', (@model.get('slug') or @model.id or @model.cid)
+        @viewport = @$el.find '.viewport'
         
         ### Model Events
         @model
-            .on 'ready',            @onReady
-            .on 'sync',             @onSync
+            .on 'ready',            @onReady, this
+            .on 'sync',             @onSync,  this
             .on 'destroy',          @remove,  this
             .on 'change',           @render,  this
             .on 'change:dataset',   @onModelChange
@@ -76,11 +80,6 @@ GraphEditView = exports.GraphEditView = BaseView.extend do # {{{
                 console.error "#this.error!", arguments
                 # TODO: UI alert
         
-        ### Graph Data UI
-        @subviews.push @dataset = new DataSetView { model:@model.get 'dataset' }
-        @$el.find '.graph-data-pane' .append @dataset.el
-        @dataset.on 'change', @onDataChange
-        
         ### Chart Options Tab, Scaffold
         @scaffold = new ChartOptionScaffold
         @$el.find '.graph-options-pane' .append @scaffold.el
@@ -92,8 +91,13 @@ GraphEditView = exports.GraphEditView = BaseView.extend do # {{{
         # Rerender the options boxes once the tab is visible
         @$el.on 'click', '.graph-options-tab', @onFirstClickRenderOptionsTab
         
+        ### Graph Data UI
+        # @subviews.push @data = new DataView { model:@model.get('dataset'), graph_id:@id }
+        @subviews.push @data = new DataView { model:new DataSet, graph_id:@id }
+        @$el.find '.graph-data-pane' .append @data.render().el
+        @data.on 'change', @onDataChange
+        
         ### Chart Viewport
-        @viewport = @$el.find '.viewport'
         @resizeViewport()
         
         # Resize chart on window resize
@@ -145,7 +149,8 @@ GraphEditView = exports.GraphEditView = BaseView.extend do # {{{
     toTemplateLocals: ->
         attrs = _.clone @model.attributes
         delete attrs.options
-        delete attrs.dataset
+        # delete attrs.dataset
+        attrs.data = @data
         { $, _, op, @model, view:this } import attrs
     
     
@@ -196,7 +201,7 @@ GraphEditView = exports.GraphEditView = BaseView.extend do # {{{
     
     # Redraw chart inside viewport.
     renderChart: ->
-        data = @model.get 'dataset' .getData()
+        data = @model.get 'dataset' #.getData()
         size = @resizeViewport()
         
         # XXX: use @model.changedAttributes() to calculate what to update
@@ -302,9 +307,11 @@ GraphEditView = exports.GraphEditView = BaseView.extend do # {{{
     
     
     onReady: ->
-        console.log "(#this via GraphEditView).ready!"
-        @ready = @scaffold.ready = true
-        @onSync()
+        return if @ready
+        $.getJSON '/datasources/all', (@data) ~>
+            console.log "(#this via GraphEditView).ready!"
+            @ready = @scaffold.ready = true
+            @onSync()
     
     onSync: ->
         return unless @ready
@@ -340,7 +347,7 @@ GraphEditView = exports.GraphEditView = BaseView.extend do # {{{
             @model.setOption(key, value, {+silent})
     
     onDataChange: ->
-        ...
+        console.log 'onDataChange!'
     
     onFirstClickRenderOptionsTab: ->
         @$el.off 'click', '.graph-options-tab', @onFirstClickRenderOptionsTab
index 339b6e8..4b55d54 100644 (file)
@@ -59,8 +59,8 @@ Graph = exports.Graph = BaseModel.extend do # {{{
             slug    : ''
             name    : ''
             desc    : ''
-            # dataset : '/data/datasources/non_mobile_pageviews_by.timestamp.language.csv'
-            dataset : null
+            dataset : '/data/datasources/non_mobile_pageviews_by.timestamp.language.csv'
+            # dataset : null
             width   : 'auto'
             height  : 320
             chartType : 'dygraphs'
@@ -73,31 +73,28 @@ Graph = exports.Graph = BaseModel.extend do # {{{
     
     
     
-    
-    constructor : (attributes={}, opts) ->
-        @on 'ready', ~> console.log "(#this via Graph).ready!"
+    constructor: function Graph (attributes={}, opts)
+        # @on 'ready', ~> console.log "(#this via Graph).ready!"
         attributes.options or= {}
         @optionCascade = new Cascade attributes.options
         BaseModel.call this, attributes, opts
     
     
-    initialize : (attributes, opts) ->
-        @__super__.initialize ...
-        opts = {+autoLoad} import (opts or {})
+    initialize: (attributes, opts) ->
+        BaseModel::initialize ...
+        opts = {+autoload} import (opts or {})
         
         @constructor.register this
         @parents = new GraphList
+        
         # TODO: Load on-demand
         @chartType = ChartType.lookup @get('chartType')
         
-        # unless @id or @get('id') or @get('slug')
-        #     @set 'slug', "unsaved_graph_#{@cid}"
-        
         # Insert submodels in place of JSON
-        @set 'dataset', new DataSet(@get('dataset')), {+silent}
+        # @set 'dataset', new DataSet(@get('dataset')), {+silent}
         
         @trigger 'init', this
-        @load() if opts.autoLoad
+        @load() if opts.autoload
     
     
     load: (opts={}) ->
@@ -114,7 +111,7 @@ Graph = exports.Graph = BaseModel.extend do # {{{
                             console.error "#{this}.fetch() --> error! #arguments"
                             next.ok()
                         success : (model, res) ~>
-                            console.log "#{this}.fetch() --> success!", res
+                            # console.log "#{this}.fetch() --> success!", res
                             next.ok res
             .seq_ (next) ~>
                 next.ok @get('parents')
@@ -137,7 +134,7 @@ Graph = exports.Graph = BaseModel.extend do # {{{
         if _.startsWith key, 'options.'
             @getOption key.slice(8)
         else
-            (@__super__ or BaseModel::).get.call this, key
+            (@..__super__ or BaseModel::).get.call this, key
     
     
     set: (key, value, opts) ->
@@ -148,7 +145,7 @@ Graph = exports.Graph = BaseModel.extend do # {{{
             values = { "#key": value }
         values = @parse values
         
-        setter = (@__super__ or BaseModel::).set
+        setter = (@..__super__ or BaseModel::).set
         
         # Merge options in, firing granulated change events
         if values.options
@@ -182,7 +179,7 @@ Graph = exports.Graph = BaseModel.extend do # {{{
         else
             values = { "#key": value }
         
-        _.dump values, "#this.setOption"
+        # _.dump values, "#this.setOption"
         options = @get('options')
         changed = false
         for key, value in values
@@ -305,7 +302,7 @@ Graph import do
     CACHE : GRAPH_CACHE
     
     register: (model) ->
-        console.log "#{@CACHE}.register(#{model.id or model.get('id')})", model
+        # console.log "#{@CACHE}.register(#{model.id or model.get('id')})", model
         if @CACHE.contains model
             @CACHE.remove model, {+silent}
         @CACHE.add model
index eda72ed..05e320d 100644 (file)
@@ -13,6 +13,9 @@ Field = exports.Field = BaseModel.extend do # {{{
     valueAttribute : 'value'
     
     
+    constructor: function Field
+        BaseModel ...
+    
     initialize: ->
         _.bindAll this, ...(_.functions this .filter -> _.startsWith(it, 'parse'))
         @set 'value', @get('default'), {+silent} if not @has 'value'
@@ -107,6 +110,9 @@ FieldList = exports.FieldList = BaseList.extend do # {{{
     ctorName : 'FieldList'
     model    : Field
     
+    
+    constructor: function FieldList then BaseList ...
+    
     /**
      * Collects a map of fields to their values, excluding those set to `null` or their default.
      * @returns {Object}
index 21aa194..be8e36a 100644 (file)
@@ -17,6 +17,9 @@ FieldView = exports.FieldView = BaseView.extend do # {{{
         'submit .value' : 'update'
     
     
+    constructor: function FieldView
+        BaseView ...
+    
     initialize: ->
         # console.log "#this.initialize!"
         BaseView::initialize ...
@@ -66,11 +69,12 @@ Scaffold = exports.Scaffold = BaseView.extend do # {{{
     subviewType    : FieldView
     
     
+    constructor: function Scaffold
+        BaseView ...
+    
     initialize: ->
-        @subviews = []
         CollectionType = @collectionType
         @model = @collection or= new CollectionType
-        
         BaseView::initialize ...
         
         @collection.on 'add',   @addOne
@@ -80,7 +84,7 @@ Scaffold = exports.Scaffold = BaseView.extend do # {{{
     
     
     addOne: (field) ->
-        # console.log "[S] #this.addOne!", @__super__
+        # console.log "[S] #this.addOne!", @..__super__
         _.remove @subviews, field.view if field.view
         
         # avoid duplicating event propagation
index 90c68f3..b77d4e2 100644 (file)
@@ -14,7 +14,7 @@ class DataSourceController extends Controller
     dataDir : 'data/graphs'
     
     mapping :
-        all : 'getAllData'
+        all : 'allData'
     
     -> super ...
     
index fcf1090..afd3f66 100755 (executable)
@@ -139,7 +139,49 @@ app.configure ->
 
 Controller   = require './controller'
 app.controller require './controllers/graph'
-app.controller require './controllers/datasource'
+ds = app.controller require './controllers/datasource'
+# ds.map 'get', 'all', ds.allData.bind(ds)
+
+YAML_EXT_PAT = /\.ya?ml$/i
+app.get '/datasources/all', (req, res, next) ->
+    data = {}
+    files = []
+    Seq()
+        .seq fs.readdir, 'data/datasources', Seq
+        .flatten()
+        .filter -> /\.(json|ya?ml)$/.test it
+        .seq ->
+            files := @stack.slice()
+            # console.log 'files:', files
+            @ok files
+        .flatten()
+        .parMap (f) ->
+            # console.log "fs.readFile '#CWD/data/#f'"
+            fs.readFile "data/datasources/#f", 'utf8', this
+        .parMap (text, i) ->
+            f = files[i]
+            # console.log "parsing file[#i]: '#f' -> text[#{text.length}]..."
+            k = f.replace YAML_EXT_PAT, '.json'
+            v = data[k] = {}
+            try
+                if YAML_EXT_PAT.test f
+                    v = data[k] = yaml.load text
+                else
+                    v = data[k] = JSON.parse text
+                # console.log "#f ok!", data
+                @ok v
+            catch err
+                console.error "[/data/all] catch! #err"
+                console.error err
+                console.error that if err.stack
+                res.send { error:String(err), partial_data:data }
+        .seq -> res.send data
+        .catch (err) ->
+            console.error '[/data/all] catch!'
+            console.error err
+            console.error that if err.stack
+            res.send { error:String(err), partial_data:data }
+
 
 app.get '/', (req, res) ->
     res.render 'dashboard'
diff --git a/lib/template/data.jade b/lib/template/data.jade
new file mode 100644 (file)
index 0000000..21c9a26
--- /dev/null
@@ -0,0 +1,13 @@
+section.data-ui
+    .row-fluid
+        .dataset-controls.dropdown
+            select(name="dataset")
+                for datum, k in data
+                    option(value=datum.url, name=datum.id) #{datum.name}
+                
+            //- 
+                a.span3.dataset.dropdown-toggle(data-toggle="dropdown", data-target="#{graph_id} .dataset-dropdown", href="#dataset-dropdown") Select...
+                ul.dropdown-menu
+                    for datum, k in data
+                        li: a(href="#", data-dataset="#{datum.id}") #{datum.name}
+    
diff --git a/lib/template/dataset-metric.jade b/lib/template/dataset-metric.jade
new file mode 100644 (file)
index 0000000..e15dac5
--- /dev/null
@@ -0,0 +1,8 @@
+tr.dataset-metric(class=viewClasses)
+    td.col-label(style="color: #{color};") #{label}
+    td.col-source   #{source}
+    td.col-time     #{timespan}
+    td.col-actions
+        a.delete-metric-button.close(href="#") &times;
+        .activity-arrow: div.inner
+    
diff --git a/lib/template/dataset.jade b/lib/template/dataset.jade
new file mode 100644 (file)
index 0000000..89b5bb7
--- /dev/null
@@ -0,0 +1,26 @@
+section.dataset-ui.dataset
+    .inner
+        h4 Graph Data Set
+        
+        .dataset-buttons
+            a.new-metric-button.btn.btn-success.btn-small(href="#")
+                i.icon-plus-sign.icon-white
+                |  Add Metric
+            //- 
+                a.clear-metrics-button.btn.btn-danger.btn-small(href="#")
+                    i.icon-remove-sign.icon-white
+                    |  Remove All Metrics
+        
+        .dataset-metrics
+            table.table.table-striped
+                thead
+                    tr
+                        th.col-label    Label
+                        th.col-source   Source
+                        th.col-times    Timespan
+                        th.col-actions  Actions
+                tbody.metrics
+                    //- DataSetMetricViews attach here
+                
+            
+
diff --git a/lib/template/datasource-ui.jade b/lib/template/datasource-ui.jade
new file mode 100644 (file)
index 0000000..870c864
--- /dev/null
@@ -0,0 +1,23 @@
+section.datasource-ui
+    
+    section.datasource-summary(data-toggle="collapse", data-target="##{graph_id} .datasource-ui .datasource-selector")
+        ul.breadcrumb
+            li #{source_summary}
+                span.divider
+            li #{metric_summary}
+                span.divider
+            li #{timespan_summary}
+        
+        i.icon-chevron-down.pull-right
+        i.icon-chevron-up.pull-right
+    
+    section.datasource-selector.collapse.in
+        .tabbable.tabs-left
+            ul.datasource-sources-list.nav.nav-tabs
+                li: h4 Data Sources
+                //- li: a(href="#", data-toggle="tab", data-target="")
+            .datasource-sources-details.tab-content
+                .datasource-source.tab
+                    .datasource-source-details
+                    .datasource-source-time
+                    .datasource-source-metrics
diff --git a/lib/template/datasource.jade b/lib/template/datasource.jade
new file mode 100644 (file)
index 0000000..e69de29
index 574a32d..ba1ae30 100644 (file)
@@ -1,11 +1,10 @@
-- var id = model.id || model.cid || view.id
-- var graph_id = view.id || id
+- var graph_id = view.id || model.id || model.cid
 section.graph.graph-edit(id=graph_id)
     form.details.form-horizontal
         
         .name-row.row-fluid.control-group
             //- label.name.control-label(for="#{id}_name"): h3 Graph Name
-            input.span6.name(type='text', id="#{id}_name", name="name", placeholder='Graph Name', value=name)
+            input.span6.name(type='text', id="#{graph_id}_name", name="name", placeholder='Graph Name', value=name)
         
         .row-fluid
             .viewport
@@ -32,41 +31,40 @@ section.graph.graph-edit(id=graph_id)
                                 |  Done
                     ul.nav.subnav.nav-pills
                         li:         h3 Graph
-                        li.active:  a(href="#graph-#{graph_id}-info", data-toggle="tab") Info
-                        li:         a(href="#graph-#{graph_id}-data", data-toggle="tab") Data
-                        li: a.graph-options-tab(href="#graph-#{graph_id}-options", data-toggle="tab") Options
+                        li.active:  a(href="##{graph_id}-tab-info", data-toggle="tab") Info
+                        li:         a(href="##{graph_id}-tab-data", data-toggle="tab") Data
+                        li: a.graph-options-tab(href="##{graph_id}-tab-options", data-toggle="tab") Options
                     
                 
                 .tab-content
-                    .graph-info-pane.tab-pane.active(id="graph-#{graph_id}-info")
+                    .graph-info-pane.tab-pane.active(id="#{graph_id}-tab-info")
                         .row-fluid
                             .half.control-group
                                 .control-group
-                                    label.slug.control-label(for="#{id}_slug") Slug
+                                    label.slug.control-label(for="#{graph_id}_slug") Slug
                                     .controls
-                                        input.span3.slug(type='text', id="#{id}_slug", name='slug', placeholder='graph_slug', value=slug)
+                                        input.span3.slug(type='text', id="#{graph_id}_slug", name='slug', placeholder='graph_slug', value=slug)
                                         p.help-block The slug uniquely identifies this graph and will be displayed in the URL.
                                 .control-group
-                                    label.width.control-label(for="#{id}_width") Size
+                                    label.width.control-label(for="#{graph_id}_width") Size
                                     .controls
-                                        input.span1.width(type='text', id="#{id}_width", name='width', value=width)
+                                        input.span1.width(type='text', id="#{graph_id}_width", name='width', value=width)
                                         |  &times; 
-                                        input.span1.height(type='text', id="#{id}_height", name='height', value=height)
+                                        input.span1.height(type='text', id="#{graph_id}_height", name='height', value=height)
                                         p.help-block Choosing 'auto' will size the graph to the viewport bounds.
                             .half.control-group
-                                label.desc.control-label(for="#{id}_desc") Description
+                                label.desc.control-label(for="#{graph_id}_desc") Description
                                 .controls
                                     //- textarea.span3.desc(id='desc', name='desc', placeholder='Graph description.') #{desc}
-                                    <textarea class="span3 desc" id="#{id}_desc" name="desc" placeholder="Graph description.">#{desc}</textarea>
+                                    <textarea class="span3 desc" id="#{graph_id}_desc" name="desc" placeholder="Graph description.">#{desc}</textarea>
                                     p.help-block A description of the graph.
                     
-                    .graph-data-pane.tab-pane(id="graph-#{graph_id}-data")
-                        //- 
-                            .row-fluid
-                                label.dataset.control-label(for="#{id}_dataset") Data Set
-                                .controls
-                                    input.span3.dataset(type='text', id="#{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-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-#{graph_id}-options")
+                    .graph-options-pane.tab-pane(id="#{graph_id}-tab-options")
 
diff --git a/lib/template/metric-edit.jade b/lib/template/metric-edit.jade
new file mode 100644 (file)
index 0000000..f48442a
--- /dev/null
@@ -0,0 +1,17 @@
+section.metric-edit-ui
+    .inner: form.form-horizontal
+        
+        .metric-header.control-group.row-fluid
+            .color-picker
+            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-actions.row-fluid
+            a.delete-button.btn.btn-danger(href="#")
+                i.icon-remove.icon-white
+                |  Delete
+        
+        .metric-options.row-fluid
+        
\ No newline at end of file
diff --git a/lib/template/metric.jade b/lib/template/metric.jade
new file mode 100644 (file)
index 0000000..e69de29
index 0c7c0b3..9129433 100644 (file)
@@ -16,6 +16,7 @@ module.exports = op =
     # values
     val     : (def,o) -> o ? def
     ok      : (o)     -> o?
+    notOk   : (o)     -> o!?
     
     first   : (a)     -> a
     second  : (_,a)   -> a
diff --git a/www/css/bootstrap-variables.styl b/www/css/bootstrap-variables.styl
new file mode 100644 (file)
index 0000000..a8b08dd
--- /dev/null
@@ -0,0 +1,201 @@
+// Bootstrap Variables
+// Auto-translated to Stylus from LESS
+// -----------------------------------------------------
+
+
+
+// GLOBAL VALUES
+// --------------------------------------------------
+
+
+// Grays
+// -------------------------
+$boot_black                         = #000
+$boot_grayDarker                    = #222
+$boot_grayDark                      = #333
+$boot_gray                          = #555
+$boot_grayLight                     = #999
+$boot_grayLighter                   = #eee
+$boot_white                         = #fff
+
+
+// Accent colors
+// -------------------------
+$boot_blue                          = #049cdb
+$boot_blueDark                      = #0064cd
+$boot_green                         = #46a546
+$boot_red                           = #9d261d
+$boot_yellow                        = #ffc40d
+$boot_orange                        = #f89406
+$boot_pink                          = #c3325f
+$boot_purple                        = #7a43b6
+
+
+// Scaffolding
+// -------------------------
+$boot_bodyBackground                = $boot_white
+$boot_textColor                     = $boot_grayDark
+
+
+// Links
+// -------------------------
+$boot_linkColor                     = #08c
+$boot_linkColorHover                = darken($boot_linkColor, 15%)
+
+
+// Typography
+// -------------------------
+$boot_baseFontSize                  = 13px
+$boot_baseFontFamily                = "Helvetica Neue", Helvetica, Arial, sans-serif
+$boot_baseLineHeight                = 18px
+$boot_altFontFamily                 = Georgia, "Times New Roman", Times, serif
+
+$boot_headingsFontFamily            = inherit // empty to use BS default, $boot_baseFontFamily
+$boot_headingsFontWeight            = bold    // instead of browser default, bold
+$boot_headingsColor                 = inherit // empty to use BS default, $boot_textColor
+
+
+// Tables
+// -------------------------
+$boot_tableBackground               = transparent // overall background-color
+$boot_tableBackgroundAccent         = #f9f9f9 // for striping
+$boot_tableBackgroundHover          = #f5f5f5 // for hover
+$boot_tableBorder                   = #ddd // table and cell border
+
+
+// Buttons
+// -------------------------
+$boot_btnBackground                 = $boot_white
+$boot_btnBackgroundHighlight        = darken($boot_white, 10%)
+$boot_btnBorder                     = darken($boot_white, 20%)
+
+$boot_btnPrimaryBackground          = $boot_linkColor
+$boot_btnPrimaryBackgroundHighlight = spin($boot_btnPrimaryBackground, 15%)
+
+$boot_btnInfoBackground             = #5bc0de
+$boot_btnInfoBackgroundHighlight    = #2f96b4
+
+$boot_btnSuccessBackground          = #62c462
+$boot_btnSuccessBackgroundHighlight = #51a351
+
+$boot_btnWarningBackground          = lighten($boot_orange, 15%)
+$boot_btnWarningBackgroundHighlight = $boot_orange
+
+$boot_btnDangerBackground           = #ee5f5b
+$boot_btnDangerBackgroundHighlight  = #bd362f
+
+$boot_btnInverseBackground          = $boot_gray
+$boot_btnInverseBackgroundHighlight = $boot_grayDarker
+
+
+// Forms
+// -------------------------
+$boot_inputBackground               = $boot_white
+$boot_inputBorder                   = #ccc
+$boot_inputDisabledBackground       = $boot_grayLighter
+
+
+// Dropdowns
+// -------------------------
+$boot_dropdownBackground            = $boot_white
+$boot_dropdownBorder                = rgba(0,0,0,.2)
+$boot_dropdownLinkColor             = $boot_grayDark
+$boot_dropdownLinkColorHover        = $boot_white
+$boot_dropdownLinkBackgroundHover   = $boot_linkColor
+
+
+
+
+// COMPONENT VARIABLES
+// --------------------------------------------------
+
+// Z-index master list
+// -------------------------
+// Used for a bird's eye view of components dependent on the z-axis
+// Try to avoid customizing these :)
+$boot_zindexDropdown                = 1000
+$boot_zindexPopover                 = 1010
+$boot_zindexTooltip                 = 1020
+$boot_zindexFixedNavbar             = 1030
+$boot_zindexModalBackdrop           = 1040
+$boot_zindexModal                   = 1050
+
+
+// Sprite icons path
+// -------------------------
+$boot_iconSpritePath                = "../img/glyphicons-halflings.png"
+$boot_iconWhiteSpritePath           = "../img/glyphicons-halflings-white.png"
+
+
+// Input placeholder text color
+// -------------------------
+$boot_placeholderText               = $boot_grayLight
+
+
+// Hr border color
+// -------------------------
+$boot_hrBorder                      = $boot_grayLighter
+
+
+// Navbar
+// -------------------------
+$boot_navbarHeight                  = 40px
+$boot_navbarBackground              = $boot_grayDarker
+$boot_navbarBackgroundHighlight     = $boot_grayDark
+
+$boot_navbarText                    = $boot_grayLight
+$boot_navbarLinkColor               = $boot_grayLight
+$boot_navbarLinkColorHover          = $boot_white
+$boot_navbarLinkColorActive         = $boot_navbarLinkColorHover
+$boot_navbarLinkBackgroundHover     = transparent
+$boot_navbarLinkBackgroundActive    = $boot_navbarBackground
+
+$boot_navbarSearchBackground        = lighten($boot_navbarBackground, 25%)
+$boot_navbarSearchBackgroundFocus   = $boot_white
+$boot_navbarSearchBorder            = darken($boot_navbarSearchBackground, 30%)
+$boot_navbarSearchPlaceholderColor  = #ccc
+
+
+// Hero unit
+// -------------------------
+$boot_heroUnitBackground            = $boot_grayLighter
+$boot_heroUnitHeadingColor          = inherit
+$boot_heroUnitLeadColor             = inherit
+
+
+// Form states and alerts
+// -------------------------
+$boot_warningText                   = #c09853
+$boot_warningBackground             = #fcf8e3
+$boot_warningBorder                 = darken(spin($boot_warningBackground, -10), 3%)
+
+$boot_errorText                     = #b94a48
+$boot_errorBackground               = #f2dede
+$boot_errorBorder                   = darken(spin($boot_errorBackground, -10), 3%)
+
+$boot_successText                   = #468847
+$boot_successBackground             = #dff0d8
+$boot_successBorder                 = darken(spin($boot_successBackground, -10), 5%)
+
+$boot_infoText                      = #3a87ad
+$boot_infoBackground                = #d9edf7
+$boot_infoBorder                    = darken(spin($boot_infoBackground, -10), 7%)
+
+
+
+
+// GRID
+// --------------------------------------------------
+
+// Default 940px grid
+// -------------------------
+$boot_gridColumns                   = 12
+$boot_gridColumnWidth               = 60px
+$boot_gridGutterWidth               = 20px
+$boot_gridRowWidth                  = ($boot_gridColumns * $boot_gridColumnWidth) + ($boot_gridGutterWidth * ($boot_gridColumns - 1))
+
+
+// Fluid grid
+// -------------------------
+$boot_fluidGridColumnWidth          = 6.382978723%
+$boot_fluidGridGutterWidth          = 2.127659574%
index a486891..3ada78f 100644 (file)
@@ -4,8 +4,8 @@ invert($v)
     hsla( 180deg * hue($v), 100% - saturation($v), 100% - lightness($v), alpha($v) )
 
 contrast_direction($v)
-    // ((lightness($v) * (100% - saturation($v))) < 50%) ? 1 : -1
     (lightness($v) < 50%) ? 1 : -1
+    // ((lightness($v) * (100% - saturation($v))) < 50%) ? 1 : -1
 
 
 /** Project Colors **/
diff --git a/www/css/data.styl b/www/css/data.styl
new file mode 100644 (file)
index 0000000..e971d11
--- /dev/null
@@ -0,0 +1,105 @@
+@import 'nib'
+@import 'colors'
+@import 'bootstrap-variables'
+
+
+
+section.graph section.data-ui
+    h4
+        display none
+    
+    .dataset-controls
+        padding 1em
+    
+    /* * * *  DataSet UI  * * * */
+    section.dataset-ui
+        width 40%
+        border-right 1px solid #ccc
+        
+        .inner
+            padding 1em 0 1em 1em
+        
+        .dataset-buttons
+            margin-bottom 1em
+        
+        .dataset-metrics
+            thead
+                display none
+            td
+                line-height 1
+                vertical-align middle
+            tr:last-child td
+                border-bottom 1px solid #ddd
+            
+            .col-label
+                width 40%
+            .col-source
+                width 25%
+            .col-time
+                width 25%
+            .col-actions
+                padding-right 14px
+            
+            .col-source, .col-time
+                font-size 80%
+                color $dark
+            
+            .dataset-metric
+                cursor pointer
+                // &:hover
+                //     cursor pointer
+                
+                &.metric-active
+                    font-weight bold
+                    td
+                        border 1px solid #ccc
+                        border-width 1px 0
+                    .activity-arrow
+                        display block
+            
+            .activity-arrow
+                display none
+                absolute top -1px right -1px
+                width 0
+                height 0
+                border-top    0.5em  solid transparent
+                // border-right  0.25em solid #ccc
+                border-right  10px   solid #ccc
+                border-bottom 0.5em  solid transparent
+                .inner
+                    absolute top -0.5em left 1px
+                    width 0
+                    height 0
+                    padding 0
+                    border-top    0.5em  solid transparent
+                    // border-right  0.25em solid white
+                    border-right  10px   solid white
+                    border-bottom 0.5em  solid transparent
+            
+            .delete-metric-button
+                color $dark
+                &:hover
+                    color $boot_red
+                    opacity 1
+            
+            
+            
+        
+        
+        .dataset-metrics-empty
+            display none
+    
+    
+    
+    /* * * *  Edit Metric UI * * * */
+    section.metric-edit-ui
+        position absolute
+        top 0
+        left 40%
+        
+        .inner
+            padding 1em
+        
+        
+    
+
index 3670ee7..e97060b 100644 (file)
@@ -62,10 +62,13 @@ dev:
             - template:
                 - chart-option.jade
                 - chart-scaffold.jade
+                - dataset.jade
+                - dataset-metric.jade
                 - metric.jade
+                - metric-edit.jade
                 - datasource.jade
-                - dataset-metric.jade
-                - dataset.jade
+                - datasource-ui.jade
+                - data.jade
                 - graph-edit.jade
                 - graph-display.jade
             - chart:
@@ -77,10 +80,13 @@ dev:
             - dataset:
                 - metric-model
                 - metric-view
+                - metric-edit-view
                 - datasource-model
                 - datasource-view
+                - datasource-ui-view
                 - dataset-model
                 - dataset-view
+                - data-view
                 - index
             - graph:
                 - graph-model