Adds redraw and save buttons.
authordsc <dsc@wikimedia.org>
Thu, 8 Mar 2012 01:46:04 +0000 (17:46 -0800)
committerdsc <dsc@wikimedia.org>
Thu, 8 Mar 2012 01:46:04 +0000 (17:46 -0800)
kraken-ui.tmproj [deleted file]
lib/base.co
lib/server/server.co
lib/template/graph.jade
lib/vis/vis-view.co
www/css/graph.styl
www/css/layout.styl

diff --git a/kraken-ui.tmproj b/kraken-ui.tmproj
deleted file mode 100644 (file)
index 66b50f6..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-       <key>currentDocument</key>
-       <string>lib/main.co</string>
-       <key>documents</key>
-       <array>
-               <dict>
-                       <key>expanded</key>
-                       <true/>
-                       <key>name</key>
-                       <string>kraken-ui</string>
-                       <key>regexFileFilter</key>
-                       <string>!(?x: lib/version.js|package\.json |license\.md | \.(png|jpg|gif|ico|psd|ai|zip|tar|gz | [oa]|dylib|fla|py[co]|mako\.py|pch|cm[oix]|cache|(tm|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: tmp/dygraphs | data | static/(vendor|tmp)|www/js/kraken | 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>
-               <dict>
-                       <key>children</key>
-                       <array/>
-                       <key>expanded</key>
-                       <true/>
-                       <key>name</key>
-                       <string>source</string>
-               </dict>
-       </array>
-       <key>fileHierarchyDrawerWidth</key>
-       <integer>217</integer>
-       <key>metaData</key>
-       <dict>
-               <key>lib/main.co</key>
-               <dict>
-                       <key>caret</key>
-                       <dict>
-                               <key>column</key>
-                               <integer>11</integer>
-                               <key>line</key>
-                               <integer>10</integer>
-                       </dict>
-                       <key>firstVisibleColumn</key>
-                       <integer>0</integer>
-                       <key>firstVisibleLine</key>
-                       <integer>0</integer>
-               </dict>
-               <key>www/layout.jade</key>
-               <dict>
-                       <key>caret</key>
-                       <dict>
-                               <key>column</key>
-                               <integer>63</integer>
-                               <key>line</key>
-                               <integer>7</integer>
-                       </dict>
-                       <key>firstVisibleColumn</key>
-                       <integer>0</integer>
-                       <key>firstVisibleLine</key>
-                       <integer>0</integer>
-               </dict>
-       </dict>
-       <key>openDocuments</key>
-       <array>
-               <string>lib/main.co</string>
-               <string>www/layout.jade</string>
-       </array>
-       <key>showFileHierarchyDrawer</key>
-       <false/>
-       <key>windowFrame</key>
-       <string>{{0, 0}, {1342, 1058}}</string>
-</dict>
-</plist>
index 602ce71..27788f4 100644 (file)
@@ -158,6 +158,10 @@ BaseView = exports.BaseView = Backbone.View.extend do # {{{
     toString : -> "#{@ctorName}(model=#{@model})"
 
 
-# }}}
+# Proxy model methods
+<[ get set unset toJSON toKV toURL ]>
+    .forEach (methodname) ->
+        BaseView::[methodname] = -> @model[methodname].apply @model, arguments
 
+# }}}
 
index 2b0edb7..4bf161b 100755 (executable)
@@ -107,9 +107,9 @@ app.configure ->
     app.use express.static STATIC
     
     # Serve directory listings
-    app.use express.directory WWW
-    app.use express.directory VAR
-    app.use express.directory STATIC
+    # app.use express.directory WWW
+    # app.use express.directory VAR
+    # app.use express.directory STATIC
     
     app.use express.errorHandler do
         dumpExceptions : true
index df5272d..9a3dd6b 100644 (file)
@@ -11,41 +11,45 @@ section.graph(id=graph_id)
             .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")
 
index 8e3e86c..c72e94e 100644 (file)
@@ -19,14 +19,26 @@ _ = require 'kraken/underscore'
  * - 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'
@@ -42,24 +54,22 @@ VisView = exports.VisView = BaseView.extend do # {{{
         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'
         
@@ -67,9 +77,7 @@ VisView = exports.VisView = BaseView.extend do # {{{
         @$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}
@@ -78,17 +86,22 @@ VisView = exports.VisView = BaseView.extend do # {{{
         _.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)
@@ -109,13 +122,12 @@ VisView = exports.VisView = BaseView.extend do # {{{
                     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
         
@@ -140,40 +152,29 @@ VisView = exports.VisView = BaseView.extend do # {{{
     
     
     
-    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
@@ -183,13 +184,7 @@ VisView = exports.VisView = BaseView.extend do # {{{
         #     @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: ->
@@ -200,6 +195,57 @@ VisView = exports.VisView = BaseView.extend do # {{{
         @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
     
@@ -216,14 +262,7 @@ VisView = exports.VisView = BaseView.extend do # {{{
         @render()
         false
     
-    toJSON: ->
-        @model.toJSON()
-    
-    toKV: ->
-        @model.toKV.apply @model, arguments
-    
-    toURL: ->
-        @model.toURL()
+    # }}}
     
     toString: -> "#{@ctorName}(#{@model})"
 # }}}
index 2835b6c..ebf30a3 100644 (file)
@@ -7,17 +7,7 @@ section.graph
     *
         position relative
     
-    .nav
-        li h3
-            line-height 14px
-            margin 2px
-            padding 8px 12px
-            border-radius 5px
-        li
-            margin-right 4px
-    .tab-content
-        padding 0.5em
-    
+    /* * * *  Chart & Viewport  * * * {{{ */
     .graph-label
         position absolute
         z-index 100
@@ -40,7 +30,33 @@ section.graph
         margin-bottom 1.5em
         overflow hidden
     
+    /* }}} */
+    
+    
+    /* * * *  Subnav & Tabs  * * * {{{ */
+    .graph-settings.tabbable
+        .nav
+            li h3
+                line-height 14px
+                margin 2px
+                padding 8px 12px
+                border-radius 5px
+            li
+                margin-right 4px
+        
+        .tab-content
+            padding 0.5em
+    
+    .graph-controls
+        float right
+        
+        input[type="button"]
+            min-width 5em
+            margin 0 0.5em
+            text-align center
+    
     
+    /* * * *  Graph Details  * * * {{{ */
     form.details
         position relative
         
@@ -66,7 +82,10 @@ section.graph
         .help-block
             font-size 11px
             line-height 1.3
+    /* }}} */
+    
     
+    /* * * *  Chart Options  * * * {{{ */
     .options fieldset
         border 0px
     
@@ -192,6 +211,8 @@ section.graph
                 background-color $bg_color
         
         /* }}} */
+        
+    /* }}} End Chart Options */
 
 
 
index f4c23a0..244ed1b 100644 (file)
@@ -3,6 +3,9 @@
 @import 'text'
 
 
+.clearer
+    clearfix()
+
 
 header, footer, #content
     position relative
@@ -44,7 +47,7 @@ header, footer, #content
             color white
             background #AF2A31
 
-nav
+nav#main-nav
     absolute top right 16px
     height 100%