From 8d0a7f1dd8c3319113feedc4bdd3c13ec53c9af3 Mon Sep 17 00:00:00 2001 From: dsc Date: Tue, 28 Feb 2012 23:58:33 -0800 Subject: [PATCH] Refactors Graph{Model,View} as Vis{Model,View} to reflect the chart-vs-graph distinction. --- lib/graph/graph-model.co | 55 +------------- lib/graph/graph-view.co | 153 +------------------------------------ lib/main.co | 12 ++-- lib/scaffold/scaffold-model.co | 7 +- lib/scaffold/scaffold-view.co | 9 +- lib/vis/index.co | 3 + lib/vis/vis-model.co | 58 ++++++++++++++ lib/vis/vis-view.co | 161 ++++++++++++++++++++++++++++++++++++++++ www/css/graph.styl | 14 ++-- www/modules.yaml | 4 + 10 files changed, 254 insertions(+), 222 deletions(-) create mode 100644 lib/vis/index.co create mode 100644 lib/vis/vis-model.co create mode 100644 lib/vis/vis-view.co diff --git a/lib/graph/graph-model.co b/lib/graph/graph-model.co index 53fa6a9..2728efa 100644 --- a/lib/graph/graph-model.co +++ b/lib/graph/graph-model.co @@ -1,7 +1,7 @@ _ = require 'kraken/underscore' { BaseModel, BaseView, } = require 'kraken/base' -{ Field, FieldList, FieldView, Scaffold +{ Field, FieldList, FieldView, Scaffold, } = require 'kraken/scaffold' @@ -97,56 +97,3 @@ GraphOptionList = exports.GraphOptionList = FieldList.extend do # {{{ model : GraphOption # }}} - -/** - * Represents a Graph, including its charting options, dataset, annotations, and all - * other settings for both its content and presentation. - */ -GraphModel = exports.GraphModel = BaseModel.extend do # {{{ - ctorName : 'GraphModel' - urlRoot : '/graphs' - idAttribute : 'slug' - - - initialize : -> - name = @get 'name' - if name and not (@id or @get('slug')) - @set 'slug', _.underscored name - - defaults: -> - { - slug : '' - name : '' - dataset : '/data/pageviews_by.timestamp.language.csv' - desc : '' - width : 'auto' - height : 320 - options : {} - } - - - hasOption: (key) -> - options = @get 'options', {} - options[key]? - - getOption: (key, def) -> - @get('options', {})[key] ? def - - setOption: (key, value, opts={}) -> - options = @get 'options', {} - options[key] = value - @set 'options', options, opts - @trigger "change:options:#key", this, value, key, opts unless opts.silent - - unsetOption: (key, opts={}) -> - options = @get 'options', {} - delete options[key] - @set 'options', options, opts - @trigger "change:options:#key", this, value, key, opts unless opts.silent - - - toString: -> "#{@ctorName}(id=#{@id}, name=#{@get 'name'}, dataset=#{@get 'dataset'})" - -# }}} - - diff --git a/lib/graph/graph-view.co b/lib/graph/graph-view.co index d507f8e..4087cf5 100644 --- a/lib/graph/graph-view.co +++ b/lib/graph/graph-view.co @@ -1,11 +1,10 @@ _ = require 'kraken/underscore' {BaseView} = require 'kraken/base' -{ Field, FieldList, FieldView, Scaffold +{ Field, FieldList, FieldView, Scaffold, } = require 'kraken/scaffold' -{ GraphModel, GraphOption, GraphOptionList, TagSet, +{ GraphOption, GraphOptionList, TagSet, KNOWN_TAGS, } = require 'kraken/graph/graph-model' -root = do -> this DEBOUNCE_RENDER = exports.DEBOUNCE_RENDER = 100ms @@ -52,6 +51,9 @@ GraphOptionView = exports.GraphOptionView = FieldView.extend do # {{{ +/** + * + */ GraphOptionsScaffold = exports.GraphOptionsScaffold = Scaffold.extend do # {{{ ctorName : 'GraphOptionsScaffold' tagName : 'form' @@ -93,148 +95,3 @@ GraphOptionsScaffold = exports.GraphOptionsScaffold = Scaffold.extend do # {{{ # }}} - - -GraphView = exports.GraphView = BaseView.extend do # {{{ - __bind__ : <[ resizeViewport render renderAll onReady ]> - ctorName : 'GraphView' - tagName : 'section' - className : 'graph' - template : require 'kraken/template/graph' - - events: - 'keypress form.options .value' : 'onKeypress' - 'submit form.options' : 'onSubmit' - - ready: false - - - - initialize : (o={}) -> - @model or= new GraphModel - BaseView::initialize ... - # console.log "#this.initialize!" - - for name of <[ resizeViewport render renderAll ]> - @[name] = _.debounce @[name], DEBOUNCE_RENDER - - # Resize graph on window resize - $ root .on 'resize', @resizeViewport - - @model.on 'destroy', @remove, this - @model.on 'change', @render, this - @model.on 'change:options', ~> - changes = @model.changedAttributes() - # console.log 'Model.changed(options) ->', changes - @chartOptions changes, {+silent} - - @build() - @viewport = @$el.find '.viewport' - - @scaffold = new GraphOptionsScaffold - @$el.find 'fieldset' .append @scaffold.el - @scaffold.collection.reset that if o.graph_spec - - @scaffold.on 'change', (scaffold, value, key, field) ~> - # console.log "scaffold.change!", value, key, field - @model.setOption key, value, {+silent} unless field.isDefault() - - options = @model.get 'options', {} - @chartOptions options, {+silent} - - _.delay @onReady, DEBOUNCE_RENDER - - - onReady: -> - @ready = @scaffold.ready = true - @renderAll() - - - chartOptions: (values, opts) -> - # Handle @chartOptions(k, v, opts) - if arguments.length > 1 and typeof values is 'string' - [k, v, opts] = arguments - values = { "#k": v } - - options = @scaffold.collection - if values - for k, v in values - options.get(k)?.setValue v, opts - this - else - options.values() - - - /** - * Resizes chart according to the model's width and height. - * @return { width, height } - */ - resizeViewport: -> - return this unless @ready and @chart - - # Remove old style, as it confuses dygraph after options update - @viewport.attr 'style', '' - - if (width = @model.get 'width') is 'auto' - width = @viewport.width() - if (height = @model.get 'height') is 'auto' - height = @viewport.height() - size = { width, height } - @viewport.css size - console.log 'resizeViewport!', JSON.stringify(size), @viewport - # @chart.resize size if forceRedraw - size - - render: -> - return this unless @ready - - # console.log "#this" - # console.log do - # " .viewport.{ width=%s, height=%s, style=%s }" - # @viewport.css('width') - # @viewport.css('height') - # @viewport.attr 'style' - # console.log ' .options:', JSON.stringify options - - size = @resizeViewport() - options = @chartOptions() - # @chart?.destroy() - unless @chart - @chart = new Dygraph do - @viewport.0 - @model.get 'dataset' - options - else - @chart.updateOptions options - @chart.resize size - - path = root.location?.path or '/' - url = "#path?#{@toKVPairs()}" - # console.log 'History.pushState', url - History.pushState url, @model.get('name', root.document?.title or ''), url - - this - - renderAll: -> - return this unless @ready - _.invoke @scaffold.subviews, 'render' - @scaffold.render() - @render() - this - - onKeypress: (evt) -> - $(evt.target).submit() if evt.keyCode is 13 - - onSubmit: -> - # console.log "#this.onSubmit!" - @render() - false - - toKVPairs: -> - @model.toKVPairs.apply @model, arguments - - toString: -> "#{@ctorName}(#{@model})" -# }}} - - - diff --git a/lib/main.co b/lib/main.co index f226812..43962e4 100644 --- a/lib/main.co +++ b/lib/main.co @@ -1,14 +1,14 @@ -{ _, op +{ _, op, } = require 'kraken/util' { BaseView, BaseModel, BaseList, } = require 'kraken/base' { Field, FieldList, FieldView, Scaffold, } = require 'kraken/scaffold' -{ GraphView, GraphModel, TagSet, - GraphOption, GraphOptionList, GraphOptionView, - GraphOptionsScaffold, +{ GraphOption, GraphOptionList, GraphOptionView, + GraphOptionsScaffold, TagSet, } = require 'kraken/graph' - +{ VisView, VisModel, +} = require 'kraken/vis' root = do -> this @@ -17,7 +17,7 @@ main = -> History.Adapter.bind window, 'statechange', -> console.log 'StateChange!', String root.location - graph = root.graph = new GraphView do + graph = root.graph = new VisView do graph_spec : CHART_OPTIONS_SPEC # If we got querystring args, apply them to the graph diff --git a/lib/scaffold/scaffold-model.co b/lib/scaffold/scaffold-model.co index 5049c87..8142295 100644 --- a/lib/scaffold/scaffold-model.co +++ b/lib/scaffold/scaffold-model.co @@ -1,6 +1,7 @@ -_ = require 'kraken/underscore' -op = require 'kraken/util/op' -{BaseModel, BaseList} = require 'kraken/base' +_ = require 'kraken/underscore' +op = require 'kraken/util/op' +{ BaseModel, BaseList, +} = require 'kraken/base' diff --git a/lib/scaffold/scaffold-view.co b/lib/scaffold/scaffold-view.co index 12a46dd..8f2c770 100644 --- a/lib/scaffold/scaffold-view.co +++ b/lib/scaffold/scaffold-view.co @@ -1,8 +1,9 @@ -_ = require 'kraken/underscore' -op = require 'kraken/util/op' -{BaseView} = require 'kraken/base' +_ = require 'kraken/underscore' +op = require 'kraken/util/op' +{ BaseView, +} = require 'kraken/base' { Field, FieldList, -} = require 'kraken/scaffold/scaffold-model' +} = require 'kraken/scaffold/scaffold-model' FieldView = exports.FieldView = BaseView.extend do # {{{ diff --git a/lib/vis/index.co b/lib/vis/index.co new file mode 100644 index 0000000..9975640 --- /dev/null +++ b/lib/vis/index.co @@ -0,0 +1,3 @@ +models = require 'kraken/vis/vis-model' +views = require 'kraken/vis/vis-view' +exports import models import views diff --git a/lib/vis/vis-model.co b/lib/vis/vis-model.co new file mode 100644 index 0000000..5bd89c6 --- /dev/null +++ b/lib/vis/vis-model.co @@ -0,0 +1,58 @@ +_ = require 'kraken/underscore' +{ BaseModel, BaseView, +} = require 'kraken/base' + + + +/** + * Represents a Graph, including its charting options, dataset, annotations, and all + * other settings for both its content and presentation. + */ +VisModel = exports.VisModel = BaseModel.extend do # {{{ + ctorName : 'VisModel' + urlRoot : '/graphs' + idAttribute : 'slug' + + + initialize : -> + name = @get 'name' + if name and not (@id or @get('slug')) + @set 'slug', _.underscored name + + defaults: -> + { + slug : '' + name : '' + dataset : '/data/pageviews_by.timestamp.language.csv' + desc : '' + width : 'auto' + height : 320 + options : {} + } + + + hasOption: (key) -> + options = @get 'options', {} + options[key]? + + getOption: (key, def) -> + @get('options', {})[key] ? def + + setOption: (key, value, opts={}) -> + options = @get 'options', {} + options[key] = value + @set 'options', options, opts + @trigger "change:options:#key", this, value, key, opts unless opts.silent + + unsetOption: (key, opts={}) -> + options = @get 'options', {} + delete options[key] + @set 'options', options, opts + @trigger "change:options:#key", this, value, key, opts unless opts.silent + + + toString: -> "#{@ctorName}(id=#{@id}, name=#{@get 'name'}, dataset=#{@get 'dataset'})" + +# }}} + + diff --git a/lib/vis/vis-view.co b/lib/vis/vis-view.co new file mode 100644 index 0000000..b707d58 --- /dev/null +++ b/lib/vis/vis-view.co @@ -0,0 +1,161 @@ +root = do -> this + +_ = require 'kraken/underscore' +{ BaseView, +} = require 'kraken/base' +{ Field, FieldList, FieldView, Scaffold +} = require 'kraken/scaffold' +{ GraphOptionsScaffold, GraphOption, GraphOptionList, DEBOUNCE_RENDER, +} = require 'kraken/graph' +{ VisModel, +} = require 'kraken/vis/vis-model' + + + + + +/** + * View for a graph visualization encapsulating the UI for: + * - Graph metadata, such as name, description, slug + */ +VisView = exports.VisView = BaseView.extend do # {{{ + __bind__ : <[ resizeViewport render renderAll onReady ]> + ctorName : 'VisView' + tagName : 'section' + className : 'graph' + template : require 'kraken/template/graph' + + events: + 'keypress form.options .value' : 'onKeypress' + 'submit form.options' : 'onSubmit' + + ready: false + + + + initialize : (o={}) -> + @model or= new VisModel + BaseView::initialize ... + # console.log "#this.initialize!" + + for name of <[ resizeViewport render renderAll ]> + @[name] = _.debounce @[name], DEBOUNCE_RENDER + + # Resize graph on window resize + $ root .on 'resize', @resizeViewport + + @model.on 'destroy', @remove, this + @model.on 'change', @render, this + @model.on 'change:options', ~> + changes = @model.changedAttributes() + # console.log 'Model.changed(options) ->', changes + @chartOptions changes, {+silent} + + @build() + @viewport = @$el.find '.viewport' + + @scaffold = new GraphOptionsScaffold + @$el.find 'fieldset' .append @scaffold.el + @scaffold.collection.reset that if o.graph_spec + + @scaffold.on 'change', (scaffold, value, key, field) ~> + # console.log "scaffold.change!", value, key, field + @model.setOption key, value, {+silent} unless field.isDefault() + + options = @model.get 'options', {} + @chartOptions options, {+silent} + + _.delay @onReady, DEBOUNCE_RENDER + + + onReady: -> + @ready = @scaffold.ready = true + @renderAll() + + + chartOptions: (values, opts) -> + # Handle @chartOptions(k, v, opts) + if arguments.length > 1 and typeof values is 'string' + [k, v, opts] = arguments + values = { "#k": v } + + options = @scaffold.collection + if values + for k, v in values + options.get(k)?.setValue v, opts + this + else + options.values() + + + /** + * Resizes chart according to the model's width and height. + * @return { width, height } + */ + resizeViewport: -> + return this unless @ready and @chart + + # Remove old style, as it confuses dygraph after options update + @viewport.attr 'style', '' + + if (width = @model.get 'width') is 'auto' + width = @viewport.width() + if (height = @model.get 'height') is 'auto' + height = @viewport.height() + size = { width, height } + @viewport.css size + console.log 'resizeViewport!', JSON.stringify(size), @viewport + # @chart.resize size if forceRedraw + size + + render: -> + return this unless @ready + + # console.log "#this" + # console.log do + # " .viewport.{ width=%s, height=%s, style=%s }" + # @viewport.css('width') + # @viewport.css('height') + # @viewport.attr 'style' + # console.log ' .options:', JSON.stringify options + + size = @resizeViewport() + options = @chartOptions() + # @chart?.destroy() + unless @chart + @chart = new Dygraph do + @viewport.0 + @model.get 'dataset' + options + else + @chart.updateOptions options + @chart.resize size + + path = root.location?.path or '/' + url = "#path?#{@toKVPairs()}" + # console.log 'History.pushState', url + History.pushState url, @model.get('name', root.document?.title or ''), url + + this + + renderAll: -> + return this unless @ready + _.invoke @scaffold.subviews, 'render' + @scaffold.render() + @render() + this + + onKeypress: (evt) -> + $(evt.target).submit() if evt.keyCode is 13 + + onSubmit: -> + # console.log "#this.onSubmit!" + @render() + false + + toKVPairs: -> + @model.toKVPairs.apply @model, arguments + + toString: -> "#{@ctorName}(#{@model})" +# }}} + diff --git a/www/css/graph.styl b/www/css/graph.styl index 749129b..4f9b09f 100644 --- a/www/css/graph.styl +++ b/www/css/graph.styl @@ -20,9 +20,10 @@ section.graph form.details position relative - .help-block - font-size 11px - line-height 1.3 + .name-row + line-height 2em + // input.name + // width 50% .row-fluid .half.control-group width 50% @@ -33,10 +34,9 @@ section.graph width 100px .controls margin-left 110px - .name-row - line-height 2em - input.name - width 95% + .help-block + font-size 11px + line-height 1.3 fieldset.options border-radius 5px diff --git a/www/modules.yaml b/www/modules.yaml index a35ef90..6afc2de 100644 --- a/www/modules.yaml +++ b/www/modules.yaml @@ -47,6 +47,10 @@ all: - graph-model - graph-view - index + - vis: + - vis-model + - vis-view + - index # - suffix: .js -- 1.7.0.4