-_ = require 'kraken/util/underscore'
-op = require 'kraken/util/op'
+{ _, op,
+} = require 'kraken/util'
Backbone = require 'backbone'
constructor : function BaseModel
@__class__ = @constructor
- @__super__ = @constructor.__super__
- @__superclass__ = @__super__.constructor
- # Backbone.NestedModel ...
+ @__superclass__ = @..__super__.constructor
Backbone.Model ...
@trigger 'create', this
# 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
#
__bind__ : []
+
+ constructor : function BaseList
+ @__class__ = @constructor
+ @__superclass__ = @..__super__.constructor
+ Backbone.Collection ...
+ @trigger 'create', this
+
initialize : ->
- @__super__ = @constructor.__super__
_.bindAll this, ...@__bind__ if @__bind__.length
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
IGNORED_TAGS : <[ callback deprecated debugging ]>
+ constructor: function ChartOption
+ Field ...
+
initialize : ->
# console.log "#this.initialize!"
Field::initialize ...
ctorName : 'ChartOptionList'
model : ChartOption
+
+ constructor: function ChartOptionList
+ FieldList ...
+
/**
* Override to omit defaults from URL.
*/
'click .collapsed' : 'onClick'
+ constructor: function ChartOptionView
+ FieldView ...
+
render: ->
- @__super__.render ...
+ FieldView::render ...
@$el.addClass 'collapsed' if @isCollapsed
this
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
render: ->
# console.log "#this.render() -> .isotope()"
- # @__super__.render ...
+ # Scaffold::render ...
return this unless @ready
container = if @fields then @$el.find @fields else @$el
container
* layout after collapse events.
*/
addOne: (field) ->
- view = @__super__.addOne ...
+ view = Scaffold::addOne ...
view.on 'change:collapse render', @render
view
--- /dev/null
+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
+
+# }}}
--- /dev/null
+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
+
+
+# }}}
+
--- /dev/null
+{ _, 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
+ '—'
+
+ # XXX: Icons/classes for visible/disabled?
+ { $, _, op, @model, view:this } import m
+
+# }}}
+
--- /dev/null
+{ _, 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 ...
+
+
+# }}}
--- /dev/null
+{ _, 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'
+# }}}
--- /dev/null
+{ _, 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 ...
+
+# }}}
--- /dev/null
+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
--- /dev/null
+{ _, 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
+
+# }}}
+
--- /dev/null
+{ _, 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 ...
+# }}}
# 'submit .value' : 'update'
+ constructor: function GraphDisplayView
+ BaseView ...
+
initialize: ->
@model or= new Graph
BaseView::initialize ...
} = require 'kraken/chart'
{ Graph,
} = require 'kraken/graph/graph-model'
-{ DataSetView,
+{ DataView, DataSetView, DataSet,
} = require 'kraken/dataset'
root = do -> this
'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
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
# 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
toTemplateLocals: ->
attrs = _.clone @model.attributes
delete attrs.options
- delete attrs.dataset
+ # delete attrs.dataset
+ attrs.data = @data
{ $, _, op, @model, view:this } import attrs
# 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
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
@model.setOption(key, value, {+silent})
onDataChange: ->
- ...
+ console.log 'onDataChange!'
onFirstClickRenderOptionsTab: ->
@$el.off 'click', '.graph-options-tab', @onFirstClickRenderOptionsTab
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'
-
- 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={}) ->
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')
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) ->
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
else
values = { "#key": value }
- _.dump values, "#this.setOption"
+ # _.dump values, "#this.setOption"
options = @get('options')
changed = false
for key, value in values
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
valueAttribute : 'value'
+ constructor: function Field
+ BaseModel ...
+
initialize: ->
_.bindAll this, ...(_.functions this .filter -> _.startsWith(it, 'parse'))
@set 'value', @get('default'), {+silent} if not @has 'value'
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}
'submit .value' : 'update'
+ constructor: function FieldView
+ BaseView ...
+
initialize: ->
# console.log "#this.initialize!"
BaseView::initialize ...
subviewType : FieldView
+ constructor: function Scaffold
+ BaseView ...
+
initialize: ->
- @subviews = []
CollectionType = @collectionType
@model = @collection or= new CollectionType
-
BaseView::initialize ...
@collection.on 'add', @addOne
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
dataDir : 'data/graphs'
mapping :
- all : 'getAllData'
+ all : 'allData'
-> super ...
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'
--- /dev/null
+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}
+
--- /dev/null
+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="#") ×
+ .activity-arrow: div.inner
+
--- /dev/null
+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
+
+
+
--- /dev/null
+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
-- 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
| 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)
| ×
- 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")
--- /dev/null
+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
# values
val : (def,o) -> o ? def
ok : (o) -> o?
+ notOk : (o) -> o!?
first : (a) -> a
second : (_,a) -> a
--- /dev/null
+// 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%
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 **/
--- /dev/null
+@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
+
+
+
+
- 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:
- 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