this
update: ->
- new DataBinding this .update @toTemplateLocals()
- @trigger 'update', this
+ new DataBinding this .update locals = @toTemplateLocals()
+ @trigger 'update', this, locals
this
it
update: (@data) ->
- for key, val in @data
+ for key, val in _.collapseObject(@data)
@updateBinding key, val
this
*
* @param {Number} n Number to format.
* @param {Number} [digits=2] Number of digits after the decimal to always display.
- * @returns {String} Formatted number.
- */
- numberFormatter: (n, digits=2) ->
- for [suffix, d] of [['B', 1000000000], ['M', 1000000], ['K', 1000], ['', NaN]]
+ * @param {Boolean} [abbrev=true] Expand number suffixes if false.
+ * @returns {Object} Formatted number parts.
+ */
+ numberFormatter: (n, digits=2, abbrev=true) ->
+ suffixes = do
+ if abbrev
+ [['B', 1000000000], ['M', 1000000], ['K', 1000], ['', NaN]]
+ else
+ [['Billion', 1000000000], ['Million', 1000000], ['', NaN]]
+
+ for [suffix, d] of suffixes
break if isNaN d
if n >= d
n = n / d
parts = s.split '.'
whole = _.rchop parts[0], 3 .join ','
fraction = '.' + parts.slice(1).join '.'
- { n, digits, whole, fraction, suffix }
+ { n, digits, whole, fraction, suffix, toString: ->
+ "#{@whole}#{@fraction}#{if abbrev then '' else ' '}#{@suffix}"
+ }
### }}}
### Rendering {{{
- update: ->
- locals = @toTemplateLocals()
- @$ '.graph-name a' .text(locals.name or '')
- @$ '.graph-desc' .html jade.filters.markdown locals.desc or ''
- this
-
render: ->
return this unless @ready and not @isRendering
@wait()
# Deal with everything else
setter.call this, values, opts
+ getCalloutData: ->
+ # For now, always operate on the first Metric
+ return unless m = @dataset.metrics.at 0
+
+ data = m.getData()
+ dates = m.getDateColumn()
+
+ # trim cols to match the right timespan
+ len = Math.min data.length, dates.length
+ data .= slice(data.length - len) if data.length < len
+ dates .= slice(dates.length - len) if dates.length < len
+
+ # Calc index offsets
+ latest = len - 1
+ last_month = latest - 1
+ last_year = latest - 12
+
+ callout =
+ latest : data[latest]
+ month :
+ dates : [ dates[last_month], dates[latest] ]
+ value : [ data[last_month], data[latest], data[latest] - data[last_month] ]
+ year :
+ dates : [ dates[last_year], dates[latest] ]
+ value : [ data[last_year], data[latest], data[latest] - data[last_year] ]
### Chart Option Accessors {{{
toTemplateLocals: ->
attrs = _.extend {}, @model.attributes
+ attrs.desc = jade.filters.markdown that if attrs.desc
+ attrs.notes = jade.filters.markdown that if attrs.notes
delete attrs.options
- { @model, view:this, @graph_id, slug:'', name:'', desc:'' } import attrs
+ delete attrs.callout
+
+ if callout = @model.getCalloutData()
+ {year:yoy, month:mom} = callout
+ attrs.callout =
+ latest : @chartType.numberFormatter(callout.latest, 2, false).toString()
+ year :
+ dates : yoy.dates.map( -> moment(it).format('MMM YY') ).join(' — ')
+ value : ( 100 * yoy.value[2] / yoy.value[0] ).toFixed(2) + '%'
+ month :
+ dates : mom.dates.map( -> moment(it).format('MMM YY') ).join(' — ')
+ value : ( 100 * mom.value[2] / mom.value[0] ).toFixed(2) + '%'
+
+ { @model, @graph_id, view:this, slug:'', name:'', desc:'', callout: {
+ latest : '',
+ year : { dates:'', value:'' },
+ month : { dates:'', value:'' } }
+ } import attrs
/**
.graph-name-row.page-header.row-fluid
h2.graph-name
- a(id="graph-title", href="#{model.toLink()}") #{name}
+ a(id="graph-title", href="#{model.toLink()}", data-bind='name') #{name}
+ .callout
+ .latest-metric(data-bind='callout.latest') #{callout.latest}
+ .metric-change.year-over-year
+ span.dates(data-bind='callout.year.dates') #{callout.year.dates}
+ span.value(data-bind='callout.year.value') #{callout.year.value}
+ .metric-change.month-over-month
+ span.dates(data-bind='callout.month.dates') #{callout.month.dates}
+ span.value(data-bind='callout.month.value') #{callout.month.value}
.graph-viewport-row.row-fluid
.viewport
.graph-legend
.graph-details-row.row
- .span7.offset1.graph-desc
+ .span7.offset1.graph-desc(data-bind='desc')
!= jade.filters.markdown(desc)
.graph-details-row.row
| Export
.graph-details-row.row
- .span6.offset1.graph-notes
+ .span6.offset1.graph-notes(data-bind='notes')
!= jade.filters.markdown(notes)
position relative
/* * * * Chart & Viewport * * * {{{ */
+ .callout
+ absolute top 0 right 1em
+ z-index 100
+ width 200px
+ padding 0 0 2em 4em
+ font 11px/1.5 "helvetica neue", helvetica, arial, sans-serif
+ border-radius 5px
+ background-color white
+ text-align right
+
+ .latest-metric
+ font-size 18px
+ line-height 36px
+ .metric-change
+ // line-height 24px
+ span
+ display inline-block
+ width 100px
+
.graph-legend
- position absolute
+ absolute top 2em right 1em
z-index 100
- top 1em
- right 1em
width 200px
padding 1em
min-height 320px
overflow hidden
+ .callout
+ absolute top 0 right 1em
+ z-index 100
+ width 200px
+ padding 0 0 2em 4em
+ font 12px/1.5 "helvetica neue", helvetica, arial, sans-serif
+ border-radius 5px
+ background-color white
+ text-align right
+
+ .latest-metric
+ font-size 18px
+ line-height 36px
+ .metric-change
+ // line-height 24px
+ span
+ display inline-block
+ width 100px
+
.graph-legend
- position absolute
+ absolute top 2em right 1em
z-index 100
- top 1em
- right 1em
width 200px
padding 1em
/* * * * Graph Details & Info Pane * * * {{{ */
.graph-name-row
+ // Accommodate the callout box
+ padding-right 272px
margin 0 0 1em
min-height 38px
+
input.graph-name
display block
absolute top 0 left 0
height 25px
padding 6px
border-color $light
+
.graph-info-pane
.row-fluid
.half.control-group
.help-block
font-size 11px
line-height 1.3
+
/* }}} */