.viewport
.graph-label
- .tabbable
- 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(href="#graph-#{graph_id}-options", data-toggle="tab") Options
-
- .tab-content
- .graph-info-pane.tab-pane.active(id="graph-#{graph_id}-info")
- .row-fluid
- .half.control-group
- label.slug.control-label(for='slug') Slug
- .controls
- input.span3.slug(type='text', 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.
- .half.control-group
- .row-fluid
- .half.control-group
- label.width.control-label(for='width') Width
- .controls
- input.span1.width(type='text', id='width', name='width', value=width)
- p.help-block Choosing 'auto' will size the graph to the viewport bounds.
- .half.control-group
- label.height.control-label(for='height') Height
- .controls: input.span1.height(type='text', id='height', name='height', value=height)
-
- .graph-data-pane.tab-pane(id="graph-#{graph_id}-data")
- .row-fluid
- label.dataset.control-label(for='dataset') Data Set
- .controls
- input.span3.dataset(type='text', 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")
+ .row-fluid
+ .graph-settings.tabbable
+ //- nav.navbar: div.navbar-inner: div.container
+ nav
+ 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.graph-controls
+ input.redraw-button.btn(type="button", value="Redraw")
+ input.load-button.btn(type="button", value="Load")
+ input.save-button.btn-success(type="button", value="Save")
-
-
+ .tab-content
+ .graph-info-pane.tab-pane.active(id="graph-#{graph_id}-info")
+ .row-fluid
+ .half.control-group
+ label.slug.control-label(for='slug') Slug
+ .controls
+ input.span3.slug(type='text', 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.
+ .half.control-group
+ .row-fluid
+ .half.control-group
+ label.width.control-label(for='width') Width
+ .controls
+ input.span1.width(type='text', id='width', name='width', value=width)
+ p.help-block Choosing 'auto' will size the graph to the viewport bounds.
+ .half.control-group
+ label.height.control-label(for='height') Height
+ .controls: input.span1.height(type='text', id='height', name='height', value=height)
+
+ .graph-data-pane.tab-pane(id="graph-#{graph_id}-data")
+ .row-fluid
+ label.dataset.control-label(for='dataset') Data Set
+ .controls
+ input.span3.dataset(type='text', 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 metadata, such as name, description, slug
*/
VisView = exports.VisView = BaseView.extend do # {{{
- FILTER_CHART_OPTIONS : <[ file labels visibility colors dateWindow ticker timingName axisLabelFormatter valueFormatter xAxisLabelFormatter yAxisLabelFormatter xValueFormatter yValueFormatter xValueParser ]>
- __bind__ : <[ resizeViewport render renderAll onReady formatter axisFormatter ]>
+ FILTER_CHART_OPTIONS : <[
+ file labels visibility colors dateWindow ticker timingName xValueParser
+ axisLabelFormatter xAxisLabelFormatter yAxisLabelFormatter
+ valueFormatter xValueFormatter yValueFormatter
+ ]>
+ __bind__ : <[
+ render renderAll resizeViewport
+ formatter axisFormatter
+ onReady onModelChange onScaffoldChange onFirstClickRenderOptionsTab
+ ]>
+ __debounce__: <[ render renderAll ]>
ctorName : 'VisView'
tagName : 'section'
className : 'graph'
template : require 'kraken/template/graph'
events:
+ 'click .redraw-button' : 'render'
+ 'click .save-button' : 'save'
+ # 'click .load-button' : 'load'
'keypress form.details input[type="text"]' : 'onKeypress'
'keypress form.options .value' : 'onKeypress'
'submit form.details' : 'onDetailsSubmit'
BaseView::initialize ...
# console.log "#this.initialize!"
- for name of <[ render renderAll ]>
+ for name of @__debounce__
@[name] = _.debounce @[name], DEBOUNCE_RENDER
# Resize graph on window resize
+ # Note: can't debounce the method itself, as the debounce wrapper returns undefined
$ root .on 'resize', _.debounce(@resizeViewport, DEBOUNCE_RENDER)
@id = _.domize 'graph', (@model.id or @model.cid)
@model.on 'destroy', @remove, this
@model.on 'change', @render, this
- @model.on 'change:dataset', ~>
- changes = @model.changedAttributes()
- console.log 'VisModel.changed( dataset ) ->', JSON.stringify changes
- @chart.updateOptions file:that if changes?.dataset
- @model.on 'change:options', ~>
- changes = @model.changedAttributes()
- console.log 'VisModel.changed( options ) ->', JSON.stringify changes
- @chartOptions that, {+silent} if changes?.options
+ @model.on 'change:dataset', @onModelChange
+ @model.on 'change:options', @onModelChange
+
+ # Rerender the options boxes once the tab is visible
+ @$el.on 'click', '.graph-options-tab', @onFirstClickRenderOptionsTab
@viewport = @$el.find '.viewport'
@$el.find '.graph-options-pane' .append @scaffold.el
@scaffold.collection.reset that if o.graph_spec
- @scaffold.on 'change', (scaffold, value, key, field) ~>
- # console.log "scaffold.change!", key, value
- @model.setOption key, value, {+silent} #unless field.isDefault()
+ @scaffold.on 'change', @onScaffoldChange
options = @model.get 'options', {}
@chartOptions options, {+silent}
_.delay @onReady, DEBOUNCE_RENDER
- onReady: ->
- console.log "#this.ready!"
- @ready = @scaffold.ready = true
- @change()
- @renderAll()
-
change: ->
@model.change()
@scaffold.invoke 'change'
this
+ save: ->
+ console.log "#this.save!"
+ $.ajax do
+ url : '/graph/save'
+ type : 'POST'
+ data : @toJSON()
+ success : (response) ->
+ console.log 'saved!'
+ error : (err) ->
+ console.error "error!", arguments
+
chartOptions: (values, opts) ->
# Handle @chartOptions(k, v, opts)
delete opts[k]
opts
-
/**
* Resizes chart according to the model's width and height.
* @return { width, height }
*/
resizeViewport: ->
- modelW = width = @model.get 'width'
+ modelW = width = @model.get 'width'
modelH = height = @model.get 'height'
return { width, height } unless @ready
- axisFormatter: (n, granularity, opts, g) ->
- @formatter n, opts, g
-
- formatter: (n, opts, g) ->
- return n if n instanceof Date
- sigFigs = opts 'sigFigs'
- maxW = opts 'maxNumberWidth'
- digits = opts 'digitsAfterDecimal'
- v = Dygraph.round_ n, digits
- # Dygraph.floatFormat n, sigFigs
- # console.log n, "->", v, "?= %#{maxW}.#{digits}g (sigFigs=#sigFigs)"
- v
-
-
render: ->
return this unless @ready
- size = @resizeViewport()
+ dataset = @model.get 'dataset'
+ size = @resizeViewport()
+
+ # XXX: use @model.changedAttributes() to calculate what to update
options = @chartOptions() #import size
options import do
labelsDiv : @$el.find '.graph-label' .0
# axisLabelFormatter : @axisFormatter
# valueFormatter : @formatter
- dataset = @model.get 'dataset'
- console.log "#this.render!", dataset
- _.dump options, 'options'
+ # console.log "#this.render!", dataset
+ # _.dump options, 'options'
+ # Always rerender the chart to sidestep the case where we need to push defaults into
+ # dygraphs to reset the current option state.
@chart?.destroy()
@chart = new Dygraph do
@viewport.0
dataset
options
-
# unless @chart
# @chart = new Dygraph do
# @viewport.0
# @chart.updateOptions options
# @chart.resize size
- # path = String(root.location?.path or '/')
- data = @toJSON()
- title = @model.get('name', root.document?.title or '')
- url = @toURL()
- # console.log 'History.pushState', JSON.stringify(data), title, url
- History.pushState data, title, url
-
+ @updateURL()
this
renderAll: ->
@render()
this
+ /**
+ * Update the page URL using HTML5 History API
+ */
+ updateURL: ->
+ data = @toJSON()
+ title = @model.get('name', root.document?.title or '')
+ url = @toURL()
+ # console.log 'History.pushState', JSON.stringify(data), title, url
+ History.pushState data, title, url
+
+
+
+ ### Formatters {{{
+
+ axisFormatter: (n, granularity, opts, g) ->
+ @formatter n, opts, g
+
+ formatter: (n, opts, g) ->
+ return n if n instanceof Date
+ sigFigs = opts 'sigFigs'
+ maxW = opts 'maxNumberWidth'
+ digits = opts 'digitsAfterDecimal'
+ v = Dygraph.round_ n, digits
+ # Dygraph.floatFormat n, sigFigs
+ # console.log n, "->", v, "?= %#{maxW}.#{digits}g (sigFigs=#sigFigs)"
+ v
+
+
+ ### }}}
+ ### Event Handlers {{{
+
+ onReady: ->
+ console.log "#this.ready!"
+ @ready = @scaffold.ready = true
+ @change()
+ @renderAll()
+
+ onModelChange: ->
+ changes = @model.changedAttributes()
+ # console.log 'VisModel.changed( options ) ->', JSON.stringify changes
+ @chart.updateOptions file:that if changes?.dataset
+ @chartOptions that, {+silent} if changes?.options
+
+ onScaffoldChange: (scaffold, value, key, field) ->
+ # console.log "scaffold.change!", key, value
+ @model.setOption key, value, {+silent} #unless field.isDefault()
+
+ onFirstClickRenderOptionsTab: ->
+ @$el.off 'click', '.graph-options-tab', @onFirstClickRenderOptionsTab
+ @scaffold.render()
+
onKeypress: (evt) ->
$(evt.target).submit() if evt.keyCode is 13
@render()
false
- toJSON: ->
- @model.toJSON()
-
- toKV: ->
- @model.toKV.apply @model, arguments
-
- toURL: ->
- @model.toURL()
+ # }}}
toString: -> "#{@ctorName}(#{@model})"
# }}}