lib
authordsc <david.schoonover@gmail.com>
Mon, 20 Feb 2012 02:14:39 +0000 (18:14 -0800)
committerdsc <david.schoonover@gmail.com>
Mon, 20 Feb 2012 02:14:39 +0000 (18:14 -0800)
Scaffold now connected to graph, and re-renders on submit.

12 files changed:
kraken-ui.tmproj
lib/graph.co [new file with mode: 0644]
lib/main.co
lib/scaffold.co [new file with mode: 0644]
lib/server/server.co
lib/underscore/underscore.array.co
lib/util/op.co
msc/dygraph-options/data.yaml
www/css/graph.styl
www/dygraph-options.json
www/graph.jade
www/layout.jade

index f6a473c..39eb08a 100644 (file)
@@ -14,7 +14,9 @@
                        <key>regexFileFilter</key>
                        <string>!(?x: lib/version.js|package\.json |license\.md|paver-minilib\.zip|\.([oa]|dylib|fla|py[co]|mako\.py|pch|cm[oix]|cache|(xcode)proj)|(fish(_read)?_history|fishd\..*)|/Icon\r|/svn-commit(\.[2-9])?\.tmp|/\.(?!(git|svn|npm)ignore|htaccess|gitmodules)[^/]*)$</string>
                        <key>regexFolderFilter</key>
-                       <string>!.*/(?x: data | static/vendor|www/js/kraken-ui | var|target|node_modules|html-template|bin-debug | \.((?!git-hooks)[^/]+) | .*\.(egg-info|framework|app|(pbx?|xcode)proj|xcode|bundle) | CVS|_darcs|_MTN|\{arch\}|blib | .*~\.nib )$</string>
+                       <string>!.*/(?x: tmp/dygraphs | data | static/vendor|www/js/kraken-ui | var|target|node_modules|html-template|bin-debug | \.((?!git-hooks)[^/]+) | .*\.(egg-info|framework|app|(pbx?|xcode)proj|xcode|bundle) | CVS|_darcs|_MTN|\{arch\}|blib | .*~\.nib )$</string>
+                       <key>selected</key>
+                       <true/>
                        <key>sourceDirectory</key>
                        <string></string>
                </dict>
@@ -25,8 +27,6 @@
                        <true/>
                        <key>name</key>
                        <string>source</string>
-                       <key>selected</key>
-                       <true/>
                </dict>
        </array>
        <key>fileHierarchyDrawerWidth</key>
@@ -38,7 +38,7 @@
                        <key>caret</key>
                        <dict>
                                <key>column</key>
-                               <integer>8</integer>
+                               <integer>11</integer>
                                <key>line</key>
                                <integer>10</integer>
                        </dict>
diff --git a/lib/graph.co b/lib/graph.co
new file mode 100644 (file)
index 0000000..97c534d
--- /dev/null
@@ -0,0 +1,96 @@
+_ = require 'kraken/underscore'
+{ Field, FieldList, FieldView, Scaffold
+} = require 'kraken/scaffold'
+
+
+GraphModel = exports.GraphModel = Backbone.Model.extend do
+    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 : {}
+        }
+    
+
+
+Graph = exports.Graph = Backbone.View.extend do
+    tagName : 'section'
+    className : 'graph'
+    
+    events:
+        'keypress input'       : 'onKeypress'
+        'submit form.settings' : 'onSubmit'
+    
+    
+    initialize : (o={}) ->
+        @model or= new GraphModel
+        
+        @$el.data { model:@model, view:this }
+        @model.on 'change',  @render, this
+        @model.on 'destroy', @remove, this
+        
+        @viewport = @$el.find '.viewport'
+        @scaffold = new Scaffold do
+            el: @$el.find 'form.settings'
+        @scaffold.collection.reset CHART_OPTIONS_SPEC
+        
+        @render()
+    
+    
+    chartOptions: (values) ->
+        options = @scaffold.collection
+        
+        # @chartOptions 'label', 'label value!'
+        if arguments.length > 1
+            [k, v] = arguments
+            values = { "#k": v }
+        
+        if values
+            for k, v in values
+                options.get(k)?.setValue v
+            this
+        else
+            options.values()
+    
+    render: ->
+        @viewport.empty()
+        
+        # Remove old style, as it confuses dygraph after options update
+        @viewport.attr 'style', ''
+        console.log do 
+            'viewport.{ width=%s, height=%s, style=%s }'
+            @viewport.css('width')
+            @viewport.css('height')
+            @viewport.attr 'style'
+        console.log 'options:', JSON.stringify @chartOptions()
+        
+        @chart?.destroy()
+        @chart = new Dygraph do
+            @viewport.0
+            'data/page_views_by_language.csv'
+            @chartOptions()
+    
+    onKeypress: (evt) ->
+        $(evt.target).submit() if evt.keyCode is 13
+    
+    onSubmit: ->
+        console.log 'Graph.onSubmit!'
+        @render()
+        false
+    
+    toString: ->
+        "Graph()"
+
+
+
+
+    
+    
+
index ccb2e39..6e54704 100644 (file)
@@ -1,11 +1,22 @@
-g = null
+_       = require 'kraken/underscore'
+{ Field, FieldList, FieldView, Scaffold
+}       = require 'kraken/scaffold'
+{Graph} = require 'kraken/graph'
 
+
+
+
+root = do -> this
 main = ->
-    g := new Dygraph do
-          $ '.graph' .eq 0 .find '.viewport' .0
-          'data/page_views_by_language.csv'
-          # {'logscale': true}
+    console.log 'main()'
+    
+    # root.g = new Dygraph do
+    #       $ '.graph' .eq 0 .find '.viewport' .0
+    #       'data/page_views_by_language.csv'
+    #       # {'logscale': true}
     
+    graph = root.graph = new Graph do
+        el : $ 'section.graph' .eq 0
     
 
 jQuery main
\ No newline at end of file
diff --git a/lib/scaffold.co b/lib/scaffold.co
new file mode 100644 (file)
index 0000000..78f1f20
--- /dev/null
@@ -0,0 +1,179 @@
+_  = require 'kraken/underscore'
+op = require 'kraken/util/op'
+
+
+
+Field = exports.Field = Backbone.Model.extend do # {{{
+    idAttribute : 'name'
+    
+    initialize: ->
+        @set 'value', @get('default'), {+silent} if not @has 'value'
+    
+    defaults: ->
+        {
+            name     : ''
+            type     : 'String'
+            default  : null
+            desc     : ''
+            category : 'General'
+            tags     : []
+            examples : []
+        }
+    
+    getParser: (type) ->
+        type or= @get 'type'
+        t = _ type.toLowerCase()
+        
+        parser = op.toStr
+        if t.startsWith 'integer'
+            parser = op.toInt
+        if t.startsWith 'float'
+            parser = op.toFloat
+        if t.startsWith 'boolean'
+            parser = op.toBool
+        if t.startsWith 'object' or t.startsWith 'array'
+            parser = op.toObject
+        if t.startsWith 'function'
+            parser = (fn) -> eval "(#fn)"
+        
+        # TODO: handle 'or' by returning an array of parsers
+        parser
+    
+    
+    getValue: ->
+        @getParser() @get 'value'
+    
+    setValue: (v, options) ->
+        def = @get 'default'
+        if not v and def == null
+            val = null
+        else
+            val = @getParser()(v)
+        @set 'value', val, options
+    
+    clearValue: ->
+        @set 'value', @get('default')
+    
+    isDefault: ->
+        @get('value') is @get('default')
+    
+# }}}
+
+
+FieldList = exports.FieldList = Backbone.Collection.extend do # {{{
+    model : Field
+    
+    /**
+     * 
+     */
+    values: ->
+        _.synthesize do
+            @models.filter -> not it.isDefault()
+            -> [ it.get('name'), it.getValue() ]
+        
+        # @reduce do
+        #     (acc, field) ->
+        #         k = field.get 'name'
+        #         v = field.getValue()
+        #         if k and not field.isDefault() and v?
+        #             acc[k] = v
+        #         acc
+        #     {}
+    
+# }}}
+
+
+### Views
+
+FieldView = exports.FieldView = Backbone.View.extend do # {{{
+    tagName   : 'div'
+    className : 'field'
+    
+    events :
+      'blur .value'   : 'update'
+      'submit .value' : 'update'
+    
+    
+    
+    initialize: ->
+        @$el.data { model:@model, view:this }
+        @model.on 'change',  @render, this
+        @model.on 'destroy', @remove, this
+    
+    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: ->
+        return @remove() if @model.get 'hidden'
+        
+        name  = @model.get 'name'
+        id    = _.camelize name
+        label = _.humanize name
+        @$el.html """
+            <label class="name" for="#id">#label</label>
+            <input class="value" type="text" id="#id" name="#id">
+        """
+        # @$el.find '.value' .attr 'value', @model.get 'value'
+        @$el.find '.value' .val @model.get 'value'
+        this
+    
+    remove: ->
+        @$el.remove()
+        this
+    
+    clear: ->
+        @model.destroy()
+        this
+    
+    toString: ->
+        "FieldView(#{@model.id})"
+    
+# }}}
+
+
+# There are several special options that, if passed, will be attached directly to the view:
+#   model, collection, el, id, className, tagName, attributes
+
+Scaffold = exports.Scaffold = Backbone.View.extend do # {{{
+    tagName        : 'form'
+    className      : 'scaffold'
+    
+    collectionType : FieldList
+    subviewType    : FieldView
+    
+    
+    initialize: ->
+        _.bindAll this, 'addOne', 'addAll'
+        # @subviews = []
+        
+        CollectionType = @collectionType
+        @collection or= new CollectionType
+        @collection.on 'add',   @addOne
+        @collection.on 'reset', @addAll
+        # @collection.on 'all',   @render
+        
+        @$el.addClass @className
+            .data { model:@collection, view:this }
+            .attr { action:'/save', method:'get' }
+    
+    
+    addOne: (field) ->
+        SubviewType = @subviewType
+        view = new SubviewType model:field
+        # @subviews.push view
+        @$el.append view.render().el
+        view
+    
+    addAll: ->
+        # _.invoke @subviews, 'remove'
+        # @subviews = []
+        @collection.each @addOne
+        this
+    
+# }}}
+
index b99db7c..a7dc7ab 100755 (executable)
@@ -64,7 +64,7 @@ app.configure ->
     app.use compiler do
         src     : WWW
         dest    : VAR
-        enabled : <[ stylus coco ]>
+        enabled : <[ stylus coco pyyaml ]>
         options : stylus : { nib:true, include:"#WWW/css" }
         log_level : log_level
     
index 4031dcb..0d5f1d3 100644 (file)
@@ -17,7 +17,7 @@ _array = do
         _.reduce do
             o
             (acc, [k, v], idx) ->
-                if k and filter(v)
+                if k and filter(v, k)
                     acc[k] = v
                 acc
             {}
index 83a75bc..c7d3bfc 100644 (file)
@@ -53,10 +53,11 @@ module.exports = op =
     
     # type coercion (w/ limited parameters for mapping)
     parseBool   : parseBool
-    toBool      : (v) -> !! v
+    toBool      : parseBool
     toInt       : (v) -> parseInt v
     toFloat     : (v) -> parseFloat v
     toStr       : (v) -> String v
+    toObject    : (v) -> JSON.parse v
     
     # comparison
     cmp       : (x,y)  ->  if x < y then -1 else (if x > y then 1 else 0)
index d0579b9..1df4c49 100644 (file)
@@ -1,3 +1,4 @@
+
 -   name: width
     type: Integer
     default: 480
@@ -24,6 +25,7 @@
     - color-cycle
     - multi-scale
     - value-axis-formatters
+
 -   name: height
     type: Integer
     default: 320
@@ -45,6 +47,7 @@
     - color-cycle
     - multi-scale
     - value-axis-formatters
+
 -   name: annotationClickHandler
     type: function(annotation, point, dygraph, event)
     default: null
@@ -55,6 +58,7 @@
     - handler
     examples:
     - annotation
+
 -   name: annotationDblClickHandler
     type: function(annotation, point, dygraph, event)
     default: null
@@ -65,6 +69,7 @@
     - handler
     examples:
     - annotation
+
 -   name: annotationMouseOutHandler
     type: function(annotation, point, dygraph, event)
     default: null
@@ -75,6 +80,7 @@
     - handler
     examples:
     - annotation
+
 -   name: annotationMouseOverHandler
     type: function(annotation, point, dygraph, event)
     default: null
@@ -85,6 +91,7 @@
     - handler
     examples:
     - annotation
+
 -   name: displayAnnotations
     type: Boolean
     default: false
     - annotations
     examples:
     - annotation-gviz
+
 -   name: avoidMinZero
     type: Boolean
     default: false
     - axes
     examples:
     - avoidMinZero
+
 -   name: axis
     type: String or Object
     default: null
     - steps
     - two-axes-vr
     - value-axis-formatters
+
 -   name: axisLabelColor
     type: String
     default: black
     category: Axes
     tags:
     - axes
+
 -   name: axisLabelFontSize
     type: Integer
     default: 14
     category: Axes
     tags:
     - axes
+
 -   name: axisLabelFormatter
     type: function(number or Date, granularity, opts, dygraph)
-    default: Depends on the data type
+    default: null
     desc: Function to call to format the tick values that appear along an axis. This is usually set on
         a per-axis basis. The first parameter is either a number (for a numeric axis) or a Date object
         (for a date axis). The second argument specifies how fine-grained the axis is. For date axes,
     - x-axis-formatter
     - y-axis-formatter
     - value-axis-formatters
+
 -   name: axisLabelWidth
     type: Integer
     default: 50
     tags:
     - axes
     - labels
+
 -   name: axisLineColor
     type: String
     default: black
     - axes
     examples:
     - demo
+
 -   name: axisLineWidth
     type: Float
     default: 0.3
     category: Axes
     tags:
     - axes
+
 -   name: axisTickSize
     type: Number
     default: '3.0'
     category: Axes
     tags:
     - axes
+
 -   name: dateWindow
     type: Array of two Dates or numbers
-    default: Full range of the input is shown
+    default: null
     desc: Initially zoom in on a section of the graph. Is of the form [earliest, latest], where earliest/latest
         are milliseconds since epoch. If the data for the x-axis is numeric, the values in dateWindow
-        must also be numbers.
+        must also be numbers. By default, the full range of the input is shown.
     category: Axes
     tags:
     - axes
     - link-interaction
     - synchronize
     - zoom
+
 -   name: drawXAxis
     type: Boolean
     default: true
     - axes
     examples:
     - unboxed-spark
+
 -   name: drawYAxis
     type: Boolean
     default: true
     examples:
     - drawing
     - unboxed-spark
+
 -   name: includeZero
     type: Boolean
     default: false
     - no-range
     - numeric-gviz
     - small-range-zero
+
 -   name: logscale
     type: Boolean
     default: false
     examples:
     - logscale
     - stock
+
 -   name: panEdgeFraction
     type: Float
     default: null
     - interactive elements
     examples:
     - zoom
+
 -   name: pixelsPerLabel
     type: Integer
-    default: 60 (x-axis) or 30 (y-axes)
+    default: null
     desc: Number of pixels to require between each x- and y-label. Larger values will yield a sparser
-        axis with fewer ticks. This is set on a per-axis basis.
+        axis with fewer ticks. This is set on a per-axis basis. By default, values are 60 (x-axis) or 30 (y-axes).
     category: Axes
     tags:
     - axes
     - grid
     examples:
     - value-axis-formatters
+
 -   name: ticker
     type: "function(min, max, pixels, opts, dygraph, vals) -> [{v: ..., label: ...}, ...]"
-    default: Dygraph.dateTicker or Dygraph.numericTicks
+    default: null
     desc: This lets you specify an arbitrary function to generate tick marks on an axis. The tick marks
         are an array of (value, label) pairs. The built-in functions go to great lengths to choose good
         tick marks so, if you set this option, you'll most likely want to call one of them and modify
-        the result. See dygraph-tickers.js for an extensive discussion. This is set on a per-axis basis.
+        the result. By default, uses Dygraph.dateTicker or Dygraph.numericTicks, but see
+        dygraph-tickers.js for an extensive discussion. This is set on a per-axis basis.
     category: Axes
     tags:
     - axes
+
 -   name: valueRange
     type: Array of two numbers
-    default: Full range of the input is shown
+    default: null
     desc: Explicitly set the vertical range of the graph to [low, high]. This may be set on a per-axis
-        basis to define each y-axis separately.
+        basis to define each y-axis separately. By default, the full range of the input is shown.
     category: Axes
     tags:
     - axes
     - synchronize
     - zoom
     - two-axes-vr
+
 -   name: xAxisHeight
     type: Integer
     default: null
     category: Axes
     tags:
     - axes
+
 -   name: xAxisLabelWidth
     type: Integer
     default: 50
     examples:
     - x-axis-formatter
     - value-axis-formatters
+
 -   name: yAxisLabelWidth
     type: Integer
     default: 50
     - multi-scale
     - two-axes-vr
     - value-axis-formatters
+
 -   name: delimiter
     type: String
     default: ','
     category: CSV parsing
     tags:
     - csv parsing
+
 -   name: xValueParser
     type: function(str) -> number
-    default: parseFloat() or Date.parse()*
+    default: null
     desc: A function which parses x-values (i.e. the dependent series). Must return a number, even when
         the values are dates. In this case, millis since epoch are used. This is used primarily for parsing
         CSV data. *=Dygraphs is slightly more accepting in the dates which it will parse. See code for
     category: CSV parsing
     tags:
     - csv parsing
+
 -   name: clickCallback
     type: function(e, x, points)
     default: null
     - callbacks
     examples:
     - callback
+
 -   name: drawCallback
     type: function(dygraph, is_initial)
     default: null
     - is-zoomed-ignore-programmatic-zoom
     - synchronize
     - zoom
+
 -   name: highlightCallback
     type: function(event, x, points,row)
     default: null
     examples:
     - callback
     - crosshair
+
 -   name: underlayCallback
     type: function(canvas, area, dygraph)
     default: null
     - linear-regression-fractions
     - linear-regression
     - underlay-callback
+
 -   name: unhighlightCallback
     type: function(event)
     default: null
     examples:
     - callback
     - crosshair
+
 -   name: zoomCallback
     type: function(minDate, maxDate, yRanges)
     default: null
     - callback
     - is-zoomed-ignore-programmatic-zoom
     - zoom
+
 -   name: title
     type: String
     default: null
     - multi-scale
     - range-selector
     - temperature-sf-ny
+
 -   name: titleHeight
     type: Integer
     default: 18
     - chart labels
     examples:
     - styled-chart-labels
+
 -   name: xLabelHeight
     type: Integer
     default: 18
     category: Chart labels
     tags:
     - chart labels
+
 -   name: xlabel
     type: String
     default: null
     - demo
     - styled-chart-labels
     - multi-scale
+
 -   name: y2label
     type: String
     default: null
     examples:
     - two-axes
     - two-axes-vr
+
 -   name: yLabelWidth
     type: Integer
     default: 18
     category: Chart labels
     tags:
     - chart labels
+
 -   name: ylabel
     type: String
     default: null
     - range-selector
     - temperature-sf-ny
     - two-axes-vr
+
 -&n