From: David Schoonover Date: Tue, 22 May 2012 15:52:56 +0000 (-0700) Subject: Fixes metric CRUD in data-ui. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=cef8a9aea92fd58ba5cc45642c5309fc9ec2a0d1;p=kraken-ui.git Fixes metric CRUD in data-ui. --- diff --git a/lib/base/base-view.co b/lib/base/base-view.co index a8d3ec9..16fcd57 100644 --- a/lib/base/base-view.co +++ b/lib/base/base-view.co @@ -111,7 +111,7 @@ BaseView = exports.BaseView = Backbone.View.extend mixinBase do # {{{ removeAllSubviews: -> @subviews.forEach @removeSubview, this - @subviews = new ViewList + # @subviews = new ViewList this @@ -232,12 +232,12 @@ BaseView = exports.BaseView = Backbone.View.extend mixinBase do # {{{ /* * * * Events * * * */ - bubbleEvent: (evt) -> + bubbleEventDown: (evt) -> @invokeSubviews 'trigger', ...arguments this - redispatch: (evt) -> - @trigger ...arguments + redispatch: (evt, ...args) -> + @trigger evt, this, ...args this onlyOnReturn: (fn, ...args) -> diff --git a/lib/chart/chart-option-view.co b/lib/chart/chart-option-view.co index 5a6f84f..1fde054 100644 --- a/lib/chart/chart-option-view.co +++ b/lib/chart/chart-option-view.co @@ -43,7 +43,8 @@ ChartOptionView = exports.ChartOptionView = BaseView.extend do # {{{ json = ChartOptionView.__super__.toTemplateLocals ... json.id or= _.camelize json.name json.value ?= '' - json.value = JSON.stringify v if v = json.value and (_.isArray(v) or _.isPlainObject(v)) + v = json.value + json.value = JSON.stringify(v) if v and ( _.isArray(v) or _.isPlainObject(v) ) json /** diff --git a/lib/chart/chart-type.co b/lib/chart/chart-type.co index 68357c2..aea28cd 100644 --- a/lib/chart/chart-type.co +++ b/lib/chart/chart-type.co @@ -70,7 +70,7 @@ class exports.ChartType extends ReadyEmitter * @type String * @readonly */ - CHART_SPEC_URL : null + SPEC_URL : null /** * Chart-type name. @@ -160,8 +160,9 @@ class exports.ChartType extends ReadyEmitter return this if @ready proto = @constructor:: jQuery.ajax do - url : @CHART_SPEC_URL + url : @SPEC_URL success : (spec) ~> + proto.spec = spec proto.options_ordered = spec proto.options = _.synthesize spec, -> [it.name, it] proto.ready = true @@ -351,6 +352,7 @@ class exports.ChartType extends ReadyEmitter data = @getData() options = @getDefaultOptions() import @transform @model, @view viewport = @getElementsForRole 'viewport' + return @lastChart unless data?.length and viewport?.length @lastChart = @renderChart data, viewport, options, @chart diff --git a/lib/chart/dygraphs.co b/lib/chart/dygraphs.co index dd95a64..49c3f86 100644 --- a/lib/chart/dygraphs.co +++ b/lib/chart/dygraphs.co @@ -5,7 +5,9 @@ _ = require 'kraken/util/underscore' class exports.DygraphsChartType extends ChartType __bind__ : <[ dygNumberFormatter dygNumberFormatterHTML ]> - CHART_SPEC_URL : '/schema/dygraph.json' + SPEC_URL : '/schema/dygraph.json' + + # NOTE: ChartType.register() must come AFTER `typeName` declaration. typeName : 'dygraphs' ChartType.register this @@ -93,6 +95,9 @@ class exports.DygraphsChartType extends ChartType { width, height } + /** + * Resizes the HTML viewport. + */ resizeViewport: -> size = @determineSize() @getElementsForRole 'viewport' .css size diff --git a/lib/chart/type/d3-bar.co b/lib/chart/type/d3-bar.co new file mode 100644 index 0000000..e69de29 diff --git a/lib/chart/type/d3-geo.co b/lib/chart/type/d3-geo.co new file mode 100644 index 0000000..e69de29 diff --git a/lib/chart/type/d3-line.co b/lib/chart/type/d3-line.co new file mode 100644 index 0000000..e69de29 diff --git a/lib/chart/type/index.co b/lib/chart/type/index.co new file mode 100644 index 0000000..e69de29 diff --git a/lib/dataset/data-view.co b/lib/dataset/data-view.co index b947e9a..7201d84 100644 --- a/lib/dataset/data-view.co +++ b/lib/dataset/data-view.co @@ -40,7 +40,7 @@ DataView = exports.DataView = BaseView.extend do # {{{ @model.once 'ready', @onReady, this onReady: -> - console.log "#this.onReady! #{@model.metrics}" + # console.log "#this.onReady! #{@model.metrics}" dataset = @model @model.metrics.each @addMetric, this @dataset_view = new DataSetView {@model, @graph_id, dataset, @datasources} @@ -81,7 +81,7 @@ DataView = exports.DataView = BaseView.extend do # {{{ { @graph_id, @datasources } import attrs addMetric: (metric) -> - console.log "#this.addMetric!", metric + # console.log "#this.addMetric!", metric return metric if @metric_views.findByModel metric view = new MetricEditView {model:metric, @graph_id, dataset:@model, @datasources} .on 'metric-update', @onUpdateMetric, this @@ -91,7 +91,7 @@ DataView = exports.DataView = BaseView.extend do # {{{ metric removeMetric: (metric) -> - console.log "#this.removeMetric!", metric + # console.log "#this.removeMetric!", metric return unless view = @metric_views.findByModel metric @metric_views.remove view @removeSubview view @@ -114,7 +114,7 @@ DataView = exports.DataView = BaseView.extend do # {{{ @$el.css 'min-height', newMinHeight onUpdateMetric: -> - console.log "#this.onUpdateMetric!" + # console.log "#this.onUpdateMetric!" @trigger 'metric-change', @model, this @render() diff --git a/lib/dataset/dataset-model.co b/lib/dataset/dataset-model.co index c797e44..dfe8aa9 100644 --- a/lib/dataset/dataset-model.co +++ b/lib/dataset/dataset-model.co @@ -42,6 +42,8 @@ DataSet = exports.DataSet = BaseModel.extend do # {{{ BaseModel::initialize ... @set 'metrics', @metrics, {+silent} @on 'change:metrics', @onMetricChange, this + # @metrics.on 'add remove reset', ~> + # @trigger 'change:metrics', @metrics, this load: (opts={}) -> @@ -51,17 +53,15 @@ DataSet = exports.DataSet = BaseModel.extend do # {{{ unless @metrics.length return @triggerReady() - console.log "#this.load()..." + # console.log "#this.load()..." @wait() @loading = true @trigger 'load', this Seq @metrics.models .parEach_ (next, metric) -> metric.once 'ready', next.ok .load() - # .parEach_ (next, metric) -> - # metric.on 'load-data-success', next.ok .loadData() .seq ~> - console.log "#{this}.load() complete!" + # console.log "#{this}.load() complete!" @loading = false @unwait() # terminates the `load` wait @triggerReady() @@ -107,7 +107,11 @@ DataSet = exports.DataSet = BaseModel.extend do # {{{ */ getData: -> return [] unless @ready - _.zip ...@getColumns() + columns = @getColumns() + if columns?.length + _.zip ...columns + else + [] /** * @returns {Array} List of all columns (including date column). @@ -149,13 +153,13 @@ DataSet = exports.DataSet = BaseModel.extend do # {{{ newMetric: -> index = @metrics.length @metrics.add m = new Metric { index, color:ColorBrewer.Spectral[11][index] } - # @get 'metrics' .push m.attributes + m.on 'ready', ~> @trigger 'metric-data-loaded', this, m # @trigger 'change:metrics', this, @metrics, 'metrics' # @trigger 'change', this, @metrics, 'metrics' m onMetricChange: -> - console.log "#this.onMetricChange! ready=#{@ready}" + # console.log "#this.onMetricChange! ready=#{@ready}" @resetReady() @load() diff --git a/lib/dataset/dataset-view.co b/lib/dataset/dataset-view.co index 34d7ff7..bc8189f 100644 --- a/lib/dataset/dataset-view.co +++ b/lib/dataset/dataset-view.co @@ -13,8 +13,9 @@ DataSetView = exports.DataSetView = BaseView.extend do # {{{ template : require 'kraken/template/dataset' events: - 'click .new-metric-button' : 'newMetric' - 'click .metrics .dataset-metric' : 'selectMetric' + 'click .new-metric-button' : 'onNewMetric' + 'click .delete-metric-button' : 'onDeleteMetric' + 'click .metrics .dataset-metric' : 'selectMetric' views_by_cid : {} active_view : null @@ -32,30 +33,27 @@ DataSetView = exports.DataSetView = BaseView.extend do # {{{ @model.metrics .on 'add', @addMetric, this .on 'remove', @removeMetric, this + .on 'change', @onMetricChange, this .on 'reset', @addAllMetrics, this - newMetric: -> - console.log "#this.newMetric!" - # triggers 'add' on @model.metrics - @model.newMetric() - false - addMetric: (metric) -> - console.log "#this.addMetric!", metric + # console.log "#this.addMetric!", metric if @views_by_cid[metric.cid] @removeSubview that delete @views_by_cid[metric.cid] view = @addSubview new DataSetMetricView {model:metric, @graph_id} @views_by_cid[metric.cid] = view - # @$ '.metrics' .append view.render().el @trigger 'add-metric', metric, view, this @render() view removeMetric: (metric) -> - console.log "#this.removeMetric!", metric + if metric instanceof [jQuery.Event, Event] + metric = @getMetricForElement metric.target + # console.log "#this.removeMetric!", metric + return unless metric if view = @views_by_cid[metric.cid] @removeSubview view delete @views_by_cid[metric.cid] @@ -63,24 +61,46 @@ DataSetView = exports.DataSetView = BaseView.extend do # {{{ view addAllMetrics: -> - console.log "#this.addAllMetrics! --> #{@model.metrics}" + # console.log "#this.addAllMetrics! --> #{@model.metrics}" @removeAllSubviews() @model.metrics.each @addMetric, this this + selectMetric: (metric) -> if metric instanceof [jQuery.Event, Event] - metric = $ metric.currentTarget .data 'model' - view = @active_view = @views_by_cid[metric.cid] + metric = @getMetricForElement metric.target # console.log "#this.selectMetric!", metric + return unless metric + view = @active_view = @views_by_cid[metric.cid] @$ '.metrics .dataset-metric' .removeClass 'metric-active' view.$el.addClass 'metric-active' - view.$el.find '.activity-arrow' .css 'font-size', 2+view.$el.height() + view.$ '.activity-arrow' .css 'font-size', 2+view.$el.height() @trigger 'select-metric', metric, view, this this + onMetricChange: (metric) -> + return unless view = @views_by_cid[metric?.cid] + view.$ '.activity-arrow:visible' .css 'font-size', 2+view.$el.height() + + onNewMetric: -> + # console.log "#this.newMetric!" + # triggers 'add' on @model.metrics + @model.newMetric() + false + + onDeleteMetric: (evt) -> + metric = @getMetricForElement evt.target + # console.log "#this.onDeleteMetric!", metric + # Triggers a 'remove' event, which in turn calls `removeMetric()` + @model.metrics.remove metric + false + + + getMetricForElement: (el) -> + $ el .parents '.dataset-metric' .eq(0).data 'model' # }}} diff --git a/lib/dataset/datasource-model.co b/lib/dataset/datasource-model.co index 75bf5e0..19f6bfc 100644 --- a/lib/dataset/datasource-model.co +++ b/lib/dataset/datasource-model.co @@ -129,6 +129,7 @@ DataSource = exports.DataSource = BaseModel.extend do # {{{ console.log "#this.onLoadDataSuccess #{@data}" @unwait() @trigger 'load-data-success', this + @triggerReady() onLoadDataError: (jqXHR, txtStatus, err) -> console.error "#this Error loading data! -- #msg: #{err or ''}" diff --git a/lib/dataset/datasource-ui-view.co b/lib/dataset/datasource-ui-view.co index 0a69ba1..f40c69b 100644 --- a/lib/dataset/datasource-ui-view.co +++ b/lib/dataset/datasource-ui-view.co @@ -33,7 +33,7 @@ DataSourceUIView = exports.DataSourceUIView = BaseView.extend do # {{{ toTemplateLocals: -> locals = @model.toJSON() - locals import {@graph_id, @dataset, @datasources} + locals import {@graph_id, @dataset, @datasources, cid:@model.cid} ds = @model.source hasSource = @model.get('source_id')? and ds diff --git a/lib/dataset/metric-edit-view.co b/lib/dataset/metric-edit-view.co index 4b5be60..f1b0fef 100644 --- a/lib/dataset/metric-edit-view.co +++ b/lib/dataset/metric-edit-view.co @@ -64,7 +64,7 @@ MetricEditView = exports.MetricEditView = BaseView.extend do # {{{ this onAttach: -> - console.log "#this.onAttach!" + # console.log "#this.onAttach!" @$ '.color-swatch' .data 'color', @model.get 'color' .colorpicker() diff --git a/lib/dataset/metric-model.co b/lib/dataset/metric-model.co index 6f3b311..2757b5b 100644 --- a/lib/dataset/metric-model.co +++ b/lib/dataset/metric-model.co @@ -129,7 +129,7 @@ Metric = exports.Metric = BaseModel.extend do # {{{ * attempt to graph unconfigured crap. */ isOk: -> - @source # and _.every @get('timespan'), op.ok + @source?.ready # and _.every @get('timespan'), op.ok # }}} diff --git a/lib/graph/graph-edit-view.co b/lib/graph/graph-edit-view.co index 1b70b4d..aa260f5 100644 --- a/lib/graph/graph-edit-view.co +++ b/lib/graph/graph-edit-view.co @@ -21,7 +21,10 @@ root = do -> this * - Chart options, using ChartOptionScaffold */ GraphEditView = exports.GraphEditView = GraphView.extend do # {{{ - __bind__ : <[ wait unwait onScaffoldChange onFirstClickRenderOptionsTab onFirstClickRenderDataTab ]> + __bind__ : <[ + wait unwait onChartTypeReady onScaffoldChange + onFirstClickRenderOptionsTab onFirstClickRenderDataTab + ]> className : 'graph-edit graph' template : require 'kraken/template/graph-edit' @@ -53,9 +56,9 @@ GraphEditView = exports.GraphEditView = GraphView.extend do # {{{ ### Chart Options Tab, Scaffold @scaffold = @addSubview new ChartOptionScaffold - @scaffold.collection.reset that if o.graph_spec - @scaffold.on 'change', @onScaffoldChange - @chartOptions @model.getOptions(), {+silent} + # @scaffold.collection.reset that if o.graph_spec + # @scaffold.on 'change', @onScaffoldChange + @chartType.on 'ready', @onChartTypeReady ### Graph Data UI @@ -70,10 +73,14 @@ GraphEditView = exports.GraphEditView = GraphView.extend do # {{{ @$el.on 'click', '.graph-data-tab', @onFirstClickRenderDataTab @$el.on 'click', '.graph-options-tab', @onFirstClickRenderOptionsTab - # Kick off model load chain @loadData() + onChartTypeReady: -> + @scaffold.collection.reset @chartType.options_ordered + @scaffold.on 'change', @onScaffoldChange + @chartOptions @model.getOptions(), {+silent} + onReady: -> return if @ready console.log "(#this via GraphEditView).ready!" @@ -84,6 +91,11 @@ GraphEditView = exports.GraphEditView = GraphView.extend do # {{{ @chartOptions @model.getOptions(), {+silent} @render() + @model.dataset.metrics + .on 'add remove change', @render, this + @model + .on 'metric-data-loaded', @render, this + # fix up the spinner element once the DOM is settled _.delay @checkWaiting, 50 diff --git a/lib/graph/graph-list-view.co b/lib/graph/graph-list-view.co index 6ce2b93..952845c 100644 --- a/lib/graph/graph-list-view.co +++ b/lib/graph/graph-list-view.co @@ -27,7 +27,7 @@ GraphListView = exports.GraphListView = BaseView.extend do # {{{ initialize : -> @model = @collection or= new GraphList BaseView::initialize ... - console.log "#this.initialize!" + # console.log "#this.initialize!" toTemplateLocals: -> locals = BaseView::toTemplateLocals ... diff --git a/lib/graph/graph-model.co b/lib/graph/graph-model.co index 2f96a1b..0a2e02e 100644 --- a/lib/graph/graph-model.co +++ b/lib/graph/graph-model.co @@ -96,7 +96,9 @@ Graph = exports.Graph = BaseModel.extend do # {{{ # Insert submodels in place of JSON @dataset = new DataSet {id:@id, ...@get 'data'} - # .on 'change', @onDataSetChange, this + .on 'change', @onDataSetChange, this + .on 'metric-data-loaded', (dataset, metric) ~> + @trigger 'metric-data-loaded', this, metric @set 'data', @dataset, {+silent} @trigger 'init', this @@ -112,7 +114,7 @@ Graph = exports.Graph = BaseModel.extend do # {{{ # Fetch model if .seq_ (next) ~> return next.ok() if @isNew() - console.log "#{this}.fetch()..." + # console.log "#{this}.fetch()..." @wait() @fetch do error : @unwaitAnd (err) ~> @@ -144,7 +146,7 @@ Graph = exports.Graph = BaseModel.extend do # {{{ # Done! .seq ~> - console.log "#{this}.load() complete!" + # console.log "#{this}.load() complete!" @loading = false @unwait() # terminates the `load` wait @triggerReady() @@ -169,7 +171,7 @@ Graph = exports.Graph = BaseModel.extend do # {{{ unless metric.source console.warn "#{this}.loadData() -- Skipping metric #metric with invalid source!", metric return next.ok() - metric.source.on 'load-data-success', next.ok .loadData() + metric.source.once 'load-data-success', next.ok .loadData() .seq ~> console.log "#{this}.loadData() complete!" @loading = false @@ -182,7 +184,9 @@ Graph = exports.Graph = BaseModel.extend do # {{{ onDataSetChange: -> console.log "#this.onDataSetChange!" - @set 'data', @dataset, {+silent} + # @set 'data', @dataset, {+silent} + # @trigger 'change:data', this, @dataset, 'data' + @trigger 'change', this, @dataset, 'data' ### Accessors @@ -246,21 +250,26 @@ Graph = exports.Graph = BaseModel.extend do # {{{ @trigger "change:options.#key", this, value, key, opts unless opts.silent if changed and not opts.silent - @trigger "change:options", this, options, 'options', opts + @trigger "change:options", this, options, 'options', opts + @trigger "change", this, options, 'options', opts this unsetOption: (key, opts={}) -> unless @optionCascade.unset(key) is void or opts.silent + options = @get 'options' @trigger "change:options.#key", this, void, key, opts - @trigger "change:options", this, @get('options'), 'options', opts + @trigger "change:options", this, options, 'options', opts + @trigger "change", this, options, 'options', opts this inheritOption: (key, opts={}) -> old = @getOption(key) @optionCascade.inherit(key) unless @getOption(key) is old or opts.silent + options = @get 'options' @trigger "change:options.#key", this, void, key, opts - @trigger "change:options", this, @get('options'), 'options', opts + @trigger "change:options", this, options, 'options', opts + @trigger "change:options", this, options, 'options', opts this getOptions: (opts={}) -> diff --git a/lib/main-display.co b/lib/main-display.co index 22de261..104a63b 100644 --- a/lib/main-display.co +++ b/lib/main-display.co @@ -24,7 +24,7 @@ main = -> # Bind to URL changes History.Adapter.bind window, 'statechange', -> - console.log 'StateChange!\n\n', String(root.location), '\n\n' + # console.log 'StateChange!\n\n', String(root.location), '\n\n' @@ -69,6 +69,6 @@ Seq([ <[ CHART_OPTIONS_SPEC /schema/dygraph.json ]> next.ok() error : (err) -> console.error err .seq -> - console.log 'All data loaded!' + # console.log 'All data loaded!' jQuery main diff --git a/lib/main-edit.co b/lib/main-edit.co index 16e4151..ee463f3 100644 --- a/lib/main-edit.co +++ b/lib/main-edit.co @@ -26,7 +26,7 @@ main = -> # Bind to URL changes History.Adapter.bind window, 'statechange', -> - console.log 'StateChange!\n\n', String(root.location), '\n\n' + # console.log 'StateChange!\n\n', String(root.location), '\n\n' diff --git a/lib/scaffold/scaffold-view.co b/lib/scaffold/scaffold-view.co index 2a44f93..4c187f9 100644 --- a/lib/scaffold/scaffold-view.co +++ b/lib/scaffold/scaffold-view.co @@ -33,7 +33,7 @@ FieldView = exports.FieldView = BaseView.extend do # {{{ current = @model.getValue() return if _.isEqual val, current - console.log "#this.onChange( #current -> #val )" + # console.log "#this.onChange( #current -> #val )" @model.setValue val, {+silent} @trigger 'change', this diff --git a/lib/template/dataset-metric.jade b/lib/template/dataset-metric.jade index 6209be4..0fd3b8f 100644 --- a/lib/template/dataset-metric.jade +++ b/lib/template/dataset-metric.jade @@ -3,6 +3,6 @@ tr.dataset-metric(class=viewClasses) td.col-source(data-bind="source") #{source} td.col-time(data-bind="timespan", data-bind-escape="false") #{timespan} td.col-actions - a.delete-metric-button.close(href="#") × + a.delete-metric-button.control.close(href="#") × .activity-arrow: div.inner diff --git a/lib/template/dataset.jade b/lib/template/dataset.jade index 289d571..94e5db0 100644 --- a/lib/template/dataset.jade +++ b/lib/template/dataset.jade @@ -19,7 +19,6 @@ section.dataset-ui.dataset: div.inner th.col-times Timespan th.col-actions Actions tbody.metrics(data-subview="DataSetMetricView") - //- DataSetMetricViews attach here diff --git a/lib/template/datasource-ui.jade b/lib/template/datasource-ui.jade index 94d6dbb..a934936 100644 --- a/lib/template/datasource-ui.jade +++ b/lib/template/datasource-ui.jade @@ -1,6 +1,6 @@ -section.datasource-ui(class="datasource-ui-#{source_id}") +section.datasource-ui(class="datasource-ui-#{cid}") - section.datasource-summary(data-toggle="collapse", data-target="##{graph_id} .datasource-ui.datasource-ui-#{source_id} .datasource-selector") + section.datasource-summary(data-toggle="collapse", data-target="##{graph_id} .datasource-ui.datasource-ui-#{cid} .datasource-selector") i.expand-datasource-ui-button.icon-chevron-down i.collapse-datasource-ui-button.icon-chevron-up diff --git a/lib/template/datasource.jade b/lib/template/datasource.jade index eec762a..6320e72 100644 --- a/lib/template/datasource.jade +++ b/lib/template/datasource.jade @@ -1,6 +1,7 @@ - var ds = source.attributes +- var id = ds.id || source.cid - var activeClass = (source_id === ds.id ? 'active' : '') -.datasource-source.tab-pane(class="datasource-source-#{ds.id} #{activeClass}") +.datasource-source.tab-pane(class="datasource-source-#{id} #{activeClass}") .datasource-source-details.well .source-name #{ds.name} .source-id #{ds.id} diff --git a/lib/util/backbone.co b/lib/util/backbone.co index e8a0daf..e3d5d28 100644 --- a/lib/util/backbone.co +++ b/lib/util/backbone.co @@ -56,8 +56,11 @@ _backbone = do if Cls.__superclass__ superclass = that else - Cls = Cls.constructor unless typeof Cls is 'function' - superclass = Cls.__super__?.constructor + Cls = Cls.constructor if typeof Cls is not 'function' + if Cls.__super__?.constructor + superclass = that + else if Cls::constructor is not Cls + superclass if superclass [superclass].concat getSuperClasses superclass diff --git a/lib/util/formatters.co b/lib/util/formatters.co new file mode 100644 index 0000000..b30650c --- /dev/null +++ b/lib/util/formatters.co @@ -0,0 +1,64 @@ +moment = require 'moment' + +{ _, op, +} = require 'kraken/util' + + +_fmt = do + + /** + * Formats a date for display on an axis: `MM/YYYY` + * @param {Date} d Date to format. + * @returns {String} + */ + axisDateFormatter: (d) -> + moment(d).format 'MM/YYYY' + + /** + * Formats a date for display in the legend: `DD MMM YYYY` + * @param {Date} d Date to format. + * @returns {String} + */ + dateFormatter: (d) -> + moment(d).format 'DD MMM YYYY' + + /** + * Formats a number for display, first dividing by the greatest suffix + * of {B = Billions, M = Millions, K = Thousands} that results in a + * absolute value greater than 0, and then rounding to `digits` using + * `result.toFixed(digits)`. + * + * @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]] + break if isNaN d + if n >= d + n = n / d + break + s = n.toFixed(digits) + parts = s.split '.' + whole = _.rchop parts[0], 3 .join ',' + fraction = '.' + parts.slice(1).join '.' + { n, digits, whole, fraction, suffix } + + + numberFormatter: (n, digits=2) -> + { whole, fraction, suffix } = _fmt._numberFormatter n, digits + "#whole#fraction#suffix" + + numberFormatterHTML: (n, digits=2) -> + { whole, fraction, suffix } = _fmt._numberFormatter n, digits + # coco will trim the whitespace + " + #whole + #fraction + #suffix + " + + + + +exports = _fmt diff --git a/lib/util/underscore/object.co b/lib/util/underscore/object.co index 4e49c46..6f07576 100644 --- a/lib/util/underscore/object.co +++ b/lib/util/underscore/object.co @@ -63,7 +63,7 @@ _obj = do */ remove: (obj, v) -> values = [].slice.call arguments, 1 - if _.isArray obj + if _.isArray(obj) or obj instanceof Array for v of values idx = obj.indexOf v obj.splice idx, 1 if idx is not -1