From 2518e585238b1881eb2403cce35409c3ee6a9f86 Mon Sep 17 00:00:00 2001 From: dsc Date: Mon, 27 Feb 2012 09:18:31 -0800 Subject: [PATCH] Splits out models and views from graph.co --- lib/graph.co | 301 ---------------------------------------------------- lib/graph/index.co | 3 + lib/graph/model.co | 107 +++++++++++++++++++ lib/graph/view.co | 202 +++++++++++++++++++++++++++++++++++ lib/main.co | 2 +- www/modules.yaml | 6 +- 6 files changed, 318 insertions(+), 303 deletions(-) delete mode 100644 lib/graph.co create mode 100644 lib/graph/index.co create mode 100644 lib/graph/model.co create mode 100644 lib/graph/view.co diff --git a/lib/graph.co b/lib/graph.co deleted file mode 100644 index e393110..0000000 --- a/lib/graph.co +++ /dev/null @@ -1,301 +0,0 @@ -_ = require 'kraken/underscore' -{ Field, FieldList, BaseView, FieldView, Scaffold -} = require 'kraken/scaffold' - - - -class exports.TagSet extends Array - tags : {} - - (values=[]) -> - @tags = {} - @add values if values?.length - - has: (tag) -> - @tags[tag]? - - get: (tag) -> - return -1 unless tag - unless @tags[tag]? - @tags[tag] = @length - @push tag - @tags[tag] - - update: (tags) -> - is_single = typeof tags is 'string' - tags = [tags] if is_single - indices = ( for tag of tags then @get tag ) - if is_single then indices[0] else indices - - toString: -> "Tags(length=#{@length}, values=[\"#{@join '", "'}\"])" - - -KNOWN_TAGS = exports.KNOWN_TAGS = new TagSet() - - -/** - * Field with graph-option-specific handling for validation, parsing, tags, etc. - */ -GraphOption = exports.GraphOption = Field.extend do # {{{ - ctorName : 'GraphOption' - - initialize : -> - # console.log "#this.initialize!" - Field::initialize ... - - # Notify Tag indexer of category when created, to ensure all category-tags - # get indices with colors :P - KNOWN_TAGS.update @getCategory() - - - # Wrapper to ensure @set('tags') is called, as tags.push() - # will not trigger the 'changed:tags' event. - addTag: (tag) -> - return this unless tag - tags = @get('tags', []) - tags.push tag - @set 'tags', tags - this - - # Wrapper to ensure @set('tags') is called, as tags.push() - # will not trigger the 'changed:tags' event. - removeTag: (tag) -> - return this unless tag - tags = @get('tags', []) - _.remove tags, tag - @set 'tags', tags - this - - # Keep tag list up to date - onTagUpdate: -> - KNOWN_TAGS.update @get 'tags' - this - - getTagIndex: (tag) -> - KNOWN_TAGS.get tag - - # A field's category is its first tag. - getCategory: -> - @get('tags', [])[0] - - getCategoryIndex: -> - @getTagIndex @getCategory() - - - toJSON: -> - o = Field::toJSON ... - for k, v in o - o[k] = '' if v!? - o - -# }}} - - -GraphOptionList = exports.GraphOptionList = FieldList.extend do # {{{ - ctorName : 'GraphOptionList' - model : GraphOption - categories : {} - - - initialize : -> - FieldList::initialize ... - @categories = {} - - -# }}} - - -/** - * The view for a single configurable option. - */ -GraphOptionView = exports.GraphOptionView = FieldView.extend do # {{{ - # __bind__ : <[ onClick ]> - ctorName : 'GraphOptionView' - tagName : 'div' - className : 'field option' - template : require 'kraken/template/graph-option' - - isCollapsed : true - - events : - 'blur .value' : 'update' - 'submit .value' : 'update' - 'click .close' : 'toggleCollapsed' - 'click h3' : 'toggleCollapsed' - 'click .collapsed' : 'onClick' - - - update: -> - val = @$el.find('.value').val() - current = @model.get 'value' - return if val is current - console.log "#this.update( #current -> #val )" - @model.setValue val, {+silent} - - render: -> - @__super__.render ... - @$el.addClass 'collapsed' if @isCollapsed - this - - onClick: (evt) -> - target = $ evt.target - # console.log "#this.onClick()", target - @toggleCollapsed() if @$el.hasClass('collapsed') and not target.hasClass('close') - - toggleCollapsed: -> - starting = @$el.hasClass 'collapsed' #@isCollapsed - @$el.toggleClass 'collapsed' - @isCollapsed = @$el.hasClass 'collapsed' - # console.log "#this.toggleCollapsed!", starting, '->', @isCollapsed - @trigger 'change:collapse', this - this - -# }}} - - - -GraphOptionsScaffold = exports.GraphOptionsScaffold = Scaffold.extend do # {{{ - ctorName : 'GraphOptionsScaffold' - tagName : 'form' - className : 'options scaffold' - collectionType : GraphOptionList - subviewType : GraphOptionView - - - initialize : -> - Scaffold::initialize ... - @render = _.debounce @render.bind(this), 50 - - render: -> - # console.log "#this.render() -> .isotope()" - @__super__.render ... - @$el.isotope do - itemSelector : '.field.option' - layoutMode : 'masonry' - masonry : columnWidth : 10 - # itemPositionDataEnabled : true - # animationEngine : 'jquery' - - addOne: -> - # console.log "#this.addOne!" - view = @__super__.addOne ... - view.on 'change:collapse render', @render - view - -# }}} - - - - -/** - * Represents a Graph, including its charting options, dataset, annotations, and all - * other settings for both its content and presentation. - */ -GraphModel = exports.GraphModel = Backbone.Model.extend do # {{{ - ctorName : 'GraphModel' - urlRoot : '/graphs' - - initialize : -> - name = @get 'name' - if name and not (@id or @has 'id') - @id = @attributes.id = _.underscored name - - defaults : -> - { - name : 'Kraken Graph' - dataset : '/data/page_views_by_language.csv' - options : {} - } - - toString: -> "#{@ctorName}(id=#{@id}, name=#{@get 'name'}, dataset=#{@get 'dataset'})" -# }}} - - -GraphView = exports.GraphView = BaseView.extend do # {{{ - ctorName : 'GraphView' - tagName : 'section' - className : 'graph' - template : require 'kraken/template/graph' - - events: - 'keypress form.options .value' : 'onKeypress' - 'submit form.options' : 'onSubmit' - - - initialize : (o={}) -> - @model or= new GraphModel - BaseView::initialize ... - # console.log "#this.initialize!" - - @model.on 'change', @render, this - @model.on 'destroy', @remove, this - - @build() - @viewport = @$el.find '.viewport' - - @scaffold = new GraphOptionsScaffold - @$el.find 'fieldset' .append @scaffold.el - @scaffold.collection.reset that if o.graph_spec - - setTimeout do - ~> @render() - 50 - - - chartOptions: (values) -> - # Handle @chartOptions k, v - if arguments.length > 1 - [k, v] = arguments - values = { "#k": v } - - options = @scaffold.collection - if values - for k, v in values - options.get(k)?.setValue v - this - else - options.values() - - render: -> - options = @chartOptions() - w = options.width or= @scaffold.get 'width' .getValue() or 480 - h = options.height or= @scaffold.get 'height' .getValue() or 320 - @viewport.css { width:w, height:h } - - # Remove old style, as it confuses dygraph after options update - @viewport.attr 'style', '' - - console.log "#this" - console.log do - " .viewport.{ width=%s, height=%s, style=%s }" - @viewport.css('width') - @viewport.css('height') - @viewport.attr 'style' - @viewport - console.log ' .options:', JSON.stringify options - - # @chart?.destroy() - unless @chart - @chart = new Dygraph do - @viewport.0 - @model.get 'dataset' - options - else - @chart.updateOptions options - @chart.resize w, h - - this - - onKeypress: (evt) -> - $(evt.target).submit() if evt.keyCode is 13 - - onSubmit: -> - console.log "#this.onSubmit!" - @render() - false - - toString: -> "#{@ctorName}(#{@model})" -# }}} - - - diff --git a/lib/graph/index.co b/lib/graph/index.co new file mode 100644 index 0000000..881b673 --- /dev/null +++ b/lib/graph/index.co @@ -0,0 +1,3 @@ +models = require 'kraken/graph/model' +views = require 'kraken/graph/view' +exports import models import views diff --git a/lib/graph/model.co b/lib/graph/model.co new file mode 100644 index 0000000..125fc4d --- /dev/null +++ b/lib/graph/model.co @@ -0,0 +1,107 @@ +_ = require 'kraken/underscore' +{ Field, FieldList, BaseView, FieldView, Scaffold +} = require 'kraken/scaffold' + + + +class exports.TagSet extends Array + tags : {} + + (values=[]) -> + @tags = {} + @add values if values?.length + + has: (tag) -> + @tags[tag]? + + get: (tag) -> + return -1 unless tag + unless @tags[tag]? + @tags[tag] = @length + @push tag + @tags[tag] + + update: (tags) -> + is_single = typeof tags is 'string' + tags = [tags] if is_single + indices = ( for tag of tags then @get tag ) + if is_single then indices[0] else indices + + toString: -> "Tags(length=#{@length}, values=[\"#{@join '", "'}\"])" + + +KNOWN_TAGS = exports.KNOWN_TAGS = new TagSet() + + +/** + * Field with graph-option-specific handling for validation, parsing, tags, etc. + */ +GraphOption = exports.GraphOption = Field.extend do # {{{ + ctorName : 'GraphOption' + + initialize : -> + # console.log "#this.initialize!" + Field::initialize ... + + # Notify Tag indexer of category when created, to ensure all category-tags + # get indices with colors :P + KNOWN_TAGS.update @getCategory() + + + # Wrapper to ensure @set('tags') is called, as tags.push() + # will not trigger the 'changed:tags' event. + addTag: (tag) -> + return this unless tag + tags = @get('tags', []) + tags.push tag + @set 'tags', tags + this + + # Wrapper to ensure @set('tags') is called, as tags.push() + # will not trigger the 'changed:tags' event. + removeTag: (tag) -> + return this unless tag + tags = @get('tags', []) + _.remove tags, tag + @set 'tags', tags + this + + # Keep tag list up to date + onTagUpdate: -> + KNOWN_TAGS.update @get 'tags' + this + + getTagIndex: (tag) -> + KNOWN_TAGS.get tag + + # A field's category is its first tag. + getCategory: -> + @get('tags', [])[0] + + getCategoryIndex: -> + @getTagIndex @getCategory() + + + toJSON: -> + o = Field::toJSON ... + for k, v in o + o[k] = '' if v!? + o + +# }}} + + +GraphOptionList = exports.GraphOptionList = FieldList.extend do # {{{ + ctorName : 'GraphOptionList' + model : GraphOption + categories : {} + + + initialize : -> + FieldList::initialize ... + @categories = {} + + +# }}} + + diff --git a/lib/graph/view.co b/lib/graph/view.co new file mode 100644 index 0000000..31653f2 --- /dev/null +++ b/lib/graph/view.co @@ -0,0 +1,202 @@ +_ = require 'kraken/underscore' +{ Field, FieldList, BaseView, FieldView, Scaffold +} = require 'kraken/scaffold' +{ GraphOption, GraphOptionList, TagSet, +} = require 'kraken/graph/model' + + + +/** + * The view for a single configurable option. + */ +GraphOptionView = exports.GraphOptionView = FieldView.extend do # {{{ + # __bind__ : <[ onClick ]> + ctorName : 'GraphOptionView' + tagName : 'div' + className : 'field option' + template : require 'kraken/template/graph-option' + + isCollapsed : true + + events : + 'blur .value' : 'update' + 'submit .value' : 'update' + 'click .close' : 'toggleCollapsed' + 'click h3' : 'toggleCollapsed' + 'click .collapsed' : 'onClick' + + + update: -> + val = @$el.find('.value').val() + current = @model.get 'value' + return if val is current + console.log "#this.update( #current -> #val )" + @model.setValue val, {+silent} + + render: -> + @__super__.render ... + @$el.addClass 'collapsed' if @isCollapsed + this + + onClick: (evt) -> + target = $ evt.target + # console.log "#this.onClick()", target + @toggleCollapsed() if @$el.hasClass('collapsed') and not target.hasClass('close') + + toggleCollapsed: -> + starting = @$el.hasClass 'collapsed' #@isCollapsed + @$el.toggleClass 'collapsed' + @isCollapsed = @$el.hasClass 'collapsed' + # console.log "#this.toggleCollapsed!", starting, '->', @isCollapsed + @trigger 'change:collapse', this + this + +# }}} + + + +GraphOptionsScaffold = exports.GraphOptionsScaffold = Scaffold.extend do # {{{ + ctorName : 'GraphOptionsScaffold' + tagName : 'form' + className : 'options scaffold' + collectionType : GraphOptionList + subviewType : GraphOptionView + + + initialize : -> + Scaffold::initialize ... + @render = _.debounce @render.bind(this), 50 + + render: -> + # console.log "#this.render() -> .isotope()" + @__super__.render ... + @$el.isotope do + itemSelector : '.field.option' + layoutMode : 'masonry' + masonry : columnWidth : 10 + # itemPositionDataEnabled : true + # animationEngine : 'jquery' + + addOne: -> + # console.log "#this.addOne!" + view = @__super__.addOne ... + view.on 'change:collapse render', @render + view + +# }}} + + + + +/** + * Represents a Graph, including its charting options, dataset, annotations, and all + * other settings for both its content and presentation. + */ +GraphModel = exports.GraphModel = Backbone.Model.extend do # {{{ + ctorName : 'GraphModel' + urlRoot : '/graphs' + + initialize : -> + name = @get 'name' + if name and not (@id or @has 'id') + @id = @attributes.id = _.underscored name + + defaults : -> + { + name : 'Kraken Graph' + dataset : '/data/page_views_by_language.csv' + options : {} + } + + toString: -> "#{@ctorName}(id=#{@id}, name=#{@get 'name'}, dataset=#{@get 'dataset'})" +# }}} + + +GraphView = exports.GraphView = BaseView.extend do # {{{ + ctorName : 'GraphView' + tagName : 'section' + className : 'graph' + template : require 'kraken/template/graph' + + events: + 'keypress form.options .value' : 'onKeypress' + 'submit form.options' : 'onSubmit' + + + initialize : (o={}) -> + @model or= new GraphModel + BaseView::initialize ... + # console.log "#this.initialize!" + + @model.on 'change', @render, this + @model.on 'destroy', @remove, this + + @build() + @viewport = @$el.find '.viewport' + + @scaffold = new GraphOptionsScaffold + @$el.find 'fieldset' .append @scaffold.el + @scaffold.collection.reset that if o.graph_spec + + setTimeout do + ~> @render() + 50 + + + chartOptions: (values) -> + # Handle @chartOptions k, v + if arguments.length > 1 + [k, v] = arguments + values = { "#k": v } + + options = @scaffold.collection + if values + for k, v in values + options.get(k)?.setValue v + this + else + options.values() + + render: -> + options = @chartOptions() + w = options.width or= @scaffold.get 'width' .getValue() or 480 + h = options.height or= @scaffold.get 'height' .getValue() or 320 + @viewport.css { width:w, height:h } + + # Remove old style, as it confuses dygraph after options update + @viewport.attr 'style', '' + + console.log "#this" + console.log do + " .viewport.{ width=%s, height=%s, style=%s }" + @viewport.css('width') + @viewport.css('height') + @viewport.attr 'style' + @viewport + console.log ' .options:', JSON.stringify options + + # @chart?.destroy() + unless @chart + @chart = new Dygraph do + @viewport.0 + @model.get 'dataset' + options + else + @chart.updateOptions options + @chart.resize w, h + + this + + onKeypress: (evt) -> + $(evt.target).submit() if evt.keyCode is 13 + + onSubmit: -> + console.log "#this.onSubmit!" + @render() + false + + toString: -> "#{@ctorName}(#{@model})" +# }}} + + + diff --git a/lib/main.co b/lib/main.co index ce96a23..7d6f33b 100644 --- a/lib/main.co +++ b/lib/main.co @@ -1,7 +1,7 @@ {_, op} = require 'kraken/util' { Field, FieldList, BaseView, FieldView, Scaffold } = require 'kraken/scaffold' -{ GraphView, GraphModel, +{ GraphView, GraphModel, TagSet, GraphOption, GraphOptionList, GraphOptionView, GraphOptionsScaffold, } = require 'kraken/graph' diff --git a/www/modules.yaml b/www/modules.yaml index 8de244f..1e0b1b7 100644 --- a/www/modules.yaml +++ b/www/modules.yaml @@ -42,7 +42,11 @@ all: - model - view - index - - graph + - graph: + - model + - view + - index + # - suffix: .js # paths: -- 1.7.0.4