-{"options":{"animatedZooms":true,"avoidMinZero":false,"axis":null,"axisLabelColor":"#666666","axisLabelFontSize":14,"axisLabelFormatter":null,"axisLabelWidth":50,"axisLineColor":"#AAAAAA","axisLineWidth":0.3,"axisTickSize":3,"colorSaturation":1,"colorValue":0.5,"colors":["#FF0097","#EF8158","#83BB32","#182B53","#4596FF","#553DC9","#AD3238","#00FFBC","#F1D950"],"connectSeparatedPoints":false,"customBars":false,"dateWindow":null,"delimiter":",","digitsAfterDecimal":2,"displayAnnotations":false,"drawPoints":true,"drawXAxis":true,"drawXGrid":true,"drawYAxis":true,"drawYGrid":true,"errorBars":false,"file":null,"fillAlpha":0.15,"fillGraph":false,"fractions":false,"gridLineColor":"#D8D8D8","gridLineWidth":0.3,"hideOverlayOnMouseOut":true,"highlightCircleSize":4,"includeZero":false,"interactionModel":null,"isZoomedIgnoreProgrammaticZoom":false,"labels":null,"labelsDiv":null,"labelsDivStyles":null,"labelsDivWidth":250,"labelsKMB":true,"labelsKMG2":false,"labelsSeparateLines":true,"labelsShowZeroValues":true,"legend":"always","logscale":true,"maxNumberWidth":30,"panEdgeFraction":null,"pixelsPerLabel":null,"pixelsPerXLabel":null,"pixelsPerYLabel":null,"pointSize":1,"rangeSelectorHeight":40,"rangeSelectorPlotFillColor":"#A7B1C4","rangeSelectorPlotStrokeColor":"#808FAB","rightGap":20,"rollPeriod":1,"showLabelsOnHighlight":true,"showRangeSelector":false,"showRoller":false,"sigFigs":null,"sigma":2,"stackedGraph":false,"stepPlot":false,"strokePattern":null,"strokeWidth":4,"ticker":null,"title":null,"titleHeight":18,"valueFormatter":null,"valueRange":null,"visibility":null,"wilsonInterval":true,"xAxisHeight":null,"xAxisLabelFormatter":null,"xAxisLabelWidth":55,"xLabelHeight":18,"xValueFormatter":null,"xValueParser":null,"xlabel":null,"y2label":null,"yAxisLabelFormatter":null,"yAxisLabelWidth":50,"yLabelWidth":18,"yValueFormatter":null,"ylabel":null},"slug":"ohai","name":"Ohai","desc":"","dataset":"/data/datasources/enwp_articles_created.csv","width":"auto","height":320,"chartType":"dygraphs","parents":["root"],"id":"ohai"}
\ No newline at end of file
+{
+ "id": "ohai",
+ "name": "ohai",
+ "slug": "ohai",
+ "desc": "",
+ "dataset": "/data/datasources/rc/rc_new_article_count.csv",
+ "width": "auto",
+ "height": 320,
+ "parents": [
+ "root"
+ ],
+ "chartType": "dygraphs",
+ "options": {
+ "animatedZooms": true,
+ "avoidMinZero": false,
+ "axis": null,
+ "axisLabelColor": "#666666",
+ "axisLabelFontSize": 14,
+ "axisLabelFormatter": null,
+ "axisLabelWidth": 50,
+ "axisLineColor": "#AAAAAA",
+ "axisLineWidth": 0.3,
+ "axisTickSize": 3,
+ "colorSaturation": 1,
+ "colorValue": 0.5,
+ "colors": [
+ "#FF0097",
+ "#EF8158",
+ "#83BB32",
+ "#182B53",
+ "#4596FF",
+ "#553DC9",
+ "#AD3238",
+ "#00FFBC",
+ "#F1D950"
+ ],
+ "connectSeparatedPoints": false,
+ "customBars": false,
+ "dateWindow": null,
+ "delimiter": ",",
+ "digitsAfterDecimal": 2,
+ "displayAnnotations": false,
+ "drawPoints": true,
+ "drawXAxis": true,
+ "drawXGrid": true,
+ "drawYAxis": true,
+ "drawYGrid": true,
+ "errorBars": false,
+ "file": null,
+ "fillAlpha": 0.15,
+ "fillGraph": false,
+ "fractions": false,
+ "gridLineColor": "#D8D8D8",
+ "gridLineWidth": 0.3,
+ "hideOverlayOnMouseOut": true,
+ "highlightCircleSize": 4,
+ "includeZero": false,
+ "interactionModel": null,
+ "isZoomedIgnoreProgrammaticZoom": false,
+ "labels": null,
+ "labelsDiv": null,
+ "labelsDivStyles": null,
+ "labelsDivWidth": 250,
+ "labelsKMB": true,
+ "labelsKMG2": false,
+ "labelsSeparateLines": true,
+ "labelsShowZeroValues": true,
+ "legend": "always",
+ "logscale": true,
+ "maxNumberWidth": 30,
+ "panEdgeFraction": null,
+ "pixelsPerLabel": null,
+ "pixelsPerXLabel": null,
+ "pixelsPerYLabel": null,
+ "pointSize": 1,
+ "rangeSelectorHeight": 40,
+ "rangeSelectorPlotFillColor": "#A7B1C4",
+ "rangeSelectorPlotStrokeColor": "#808FAB",
+ "rightGap": 20,
+ "rollPeriod": 1,
+ "showLabelsOnHighlight": true,
+ "showRangeSelector": false,
+ "showRoller": false,
+ "sigFigs": null,
+ "sigma": 2,
+ "stackedGraph": false,
+ "stepPlot": false,
+ "strokePattern": null,
+ "strokeWidth": 4,
+ "ticker": null,
+ "title": null,
+ "titleHeight": 18,
+ "valueFormatter": null,
+ "valueRange": null,
+ "visibility": null,
+ "wilsonInterval": true,
+ "xAxisHeight": null,
+ "xAxisLabelFormatter": null,
+ "xAxisLabelWidth": 55,
+ "xLabelHeight": 18,
+ "xValueFormatter": null,
+ "xValueParser": null,
+ "xlabel": null,
+ "y2label": null,
+ "yAxisLabelFormatter": null,
+ "yAxisLabelWidth": 50,
+ "yLabelWidth": 18,
+ "yValueFormatter": null,
+ "ylabel": null
+ }
+}
screen -dr kraken
./lib/server/server.co | tee -a logs/kraken.log
+
+## Deployer Config
+
+ set -xg KRAKEN_DEPLOY_HOST dsc@reportcard2.pmtpa.wmflabs
+ set -xg KRAKEN_DEPLOY_PATH /srv/reportcard/kraken-ui/
+ set -xg KRAKEN_DEV_HOST master.reportcard.wmflabs.org
-/**
- * @class Base model, extending Backbone.Model, used by scaffold and others.
- * @extends Backbone.Model
- */
-BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{
- ctorName : 'BaseModel'
+BaseBackboneMixin = exports.BaseBackboneMixin =
- # A list of method-names to bind on initialize; set this on a subclass to override.
+ initialize: ->
+ @__apply_bind__()
+
+
+ ### Auto-Bound methods
+
+ /**
+ * A list of method-names to bind on `initialize`; set this on a subclass to override.
+ * @type Array<String>
+ */
__bind__ : []
+ /**
+ * Applies the contents of `__bind__`.
+ */
+ __apply_bind__: ->
+ names = _ @pluckSuperAndSelf '__bind__' .chain().flatten().compact().unique().value()
+ _.bindAll this, ...names if names.length
+
+
+
+ ### Synchronization
+
+ /**
+ * Count of outstanding tasks.
+ * @type Number
+ */
+ waitingOn : 0
+
+
+ /**
+ * Increment the waiting task counter.
+ * @returns {this}
+ */
+ wait: ->
+ count = @waitingOn
+ @waitingOn += 1
+ console.log "#this.wait! #count --> #{@waitingOn}"
+ @trigger('start-waiting', this) if count is 0 and @waitingOn > 0
+ this
+
+ /**
+ * Decrement the waiting task counter.
+ * @returns {this}
+ */
+ unwait: ->
+ count = @waitingOn
+ @waitingOn -= 1
+ console.warn "#this.unwait! #{@waitingOn} < 0" if @waitingOn < 0
+ console.log "#this.unwait! #count --> #{@waitingOn}"
+ @trigger('stop-waiting', this) if @waitingOn is 0 and count > 0
+ this
+
+ /**
+ * @param {Function} fn Function to wrap.
+ * @returns {Function} A function wrapping the passed function with a call
+ * to `unwait()`, then delegating with current context and arguments.
+ */
+ unwaitAnd: (fn) ->
+ self = this
+ ->
+ console.log "#self.unwaitAnd( function #{fn.name or fn.displayName}() )"
+ self.unwait()
+ fn ...
+
+
+mixinBase = exports.mixinBase = (body) ->
+ _.clone(BaseBackboneMixin) import body
+
+
+/**
+ * @class Base model, extending Backbone.Model, used by scaffold and others.
+ * @extends Backbone.Model
+ */
+BaseModel = exports.BaseModel = Backbone.Model.extend mixinBase do # {{{
constructor : function BaseModel
@__class__ = @constructor
@__superclass__ = @..__super__.constructor
+ @waitingOn = 0
Backbone.Model ...
@trigger 'create', this
- initialize: ->
- _.bindAll this, ...@__bind__ if @__bind__.length
+
### Accessors
#
+
+
+
### Serialization
serialize: (v) ->
toURL: ->
"?#{@toKV ...}"
- toString: -> "#{@ctorName}(id=#{@id})"
+ toString: -> "#{@..name or @..displayName}(cid=#{@cid}, id=#{@id})"
# Class Methods
BaseModel import do
-
/**
* Factory method which constructs an instance of this model from a string of KV-pairs.
* This is a class method inherited by models which extend {BaseModel}.
* @class Base collection, extending Backbone.Collection, used by scaffold and others.
* @extends Backbone.Collection
*/
-BaseList = exports.BaseList = Backbone.Collection.extend do # {{{
- ctorName : 'BaseList'
-
- # A list of method-names to bind on initialize; set this on a subclass to override.
- __bind__ : []
-
+BaseList = exports.BaseList = Backbone.Collection.extend mixinBase do # {{{
constructor : function BaseList
@__class__ = @constructor
@__superclass__ = @..__super__.constructor
+ @waitingOn = 0
Backbone.Collection ...
@trigger 'create', this
- initialize : ->
- _.bindAll this, ...@__bind__ if @__bind__.length
+ ### Serialization
toKVPairs: ->
_.collapseObject @toJSON()
toURL: (item_delim='&', kv_delim='=') ->
"?#{@toKV ...}"
- toString: -> "#{@ctorName}(length=#{@length})"
+ toString: -> "#{@..name or @..displayName}(length=#{@length})"
# }}}
* @class Base view, extending Backbone.View, used by scaffold and others.
* @extends Backbone.View
*/
-BaseView = exports.BaseView = Backbone.View.extend do # {{{
- ctorName : 'BaseView'
-
- /**
- * A list of method-names to bind on initialize; set this on a subclass to override.
- * @type Array<String>
- */
- __bind__ : []
+BaseView = exports.BaseView = Backbone.View.extend mixinBase do # {{{
+ tagName : 'section'
/**
- * @type Array<BaseView>
+ * Array of [view, selector]-pairs.
+ * @type Array<[BaseView, String]>
*/
subviews : []
constructor : function BaseView
@__class__ = @constructor
@__superclass__ = @..__super__.constructor
+ @waitingOn = 0
@subviews = []
Backbone.View ...
@trigger 'create', this
initialize: ->
- _.bindAll this, ...@__bind__ if @__bind__.length
+ @__apply_bind__()
@setModel @model
@build()
@model.on 'destroy', @remove, this
@model
+
+
+ ### Subviews
+
+ addSubview: (selector, view) ->
+ [view, selector] = [selector, null] unless view
+ @subviews.push [view, selector]
+ view
+
+ removeSubview: (view) ->
+ for [v, sel], idx of @subviews
+ if v is view
+ @subviews.splice(idx, 1)
+ return [v, sel]
+ null
+
+ hasSubview: (view) ->
+ _.any @subviews, ([v]) -> v is view
+
+ attachSubviews: ->
+ for [view, selector] of @subviews
+ return unless view
+ view.undelegateEvents()
+ return unless el = view.render()?.el
+ if selector
+ @$el.find selector .append el
+ else
+ @$el.append el
+ view.delegateEvents()
+ this
+
+
+ ### Rendering Chain
+
toTemplateLocals: ->
json = {value:v} = @model.toJSON()
if _.isArray(v) or _.isObject(v)
json.value = JSON.stringify v
- { $, _, op, @model, view:this } import json
+ json
$template: (locals={}) ->
- $ @template @toTemplateLocals() import locals
+ $ @template do
+ { $, _, op, @model, view:this } import @toTemplateLocals() import locals
build: ->
return this unless @template
-
outer = @$template()
@$el.html outer.html()
.attr do
id : outer.attr 'id'
class : outer.attr('class')
-
+ @attachSubviews()
this
render: ->
@trigger 'render', this
this
+ renderSubviews: ->
+ _.invoke _.pluck(@subviews, 0), 'render'
+ this
+
+
+
+
+ ### UI Utilities
+
hide : -> @$el.hide(); this
show : -> @$el.show(); this
remove : -> @$el.remove(); this
# @$el.appendTo parent if parent?.length
# this
- toString : -> "#{@ctorName}(model=#{@model})"
+ toString : -> "#{@..name or @..displayName}(model=#{@model})"
# Proxy model methods
* @class Field with chart-option-specific handling for validation, parsing, tags, etc.
*/
ChartOption = exports.ChartOption = Field.extend do # {{{
- ctorName : 'ChartOption'
IGNORED_TAGS : <[ callback deprecated debugging ]>
* @class List of ChartOption fields.
*/
ChartOptionList = exports.ChartOptionList = FieldList.extend do # {{{
- ctorName : 'ChartOptionList'
model : ChartOption
*/
ChartOptionView = exports.ChartOptionView = FieldView.extend do # {{{
# __bind__ : <[ onClick ]>
- ctorName : 'ChartOptionView'
tagName : 'div'
className : 'field option'
template : require 'kraken/template/chart-option'
@$el.addClass 'collapsed' if @isCollapsed
this
+
+ /**
+ * Sets the state of `isCollapsed` and updates the UI. If the state changed,
+ * a `'change:collapse`` event will be fired.`
+ *
+ * @param {Boolean} [makeCollapsed=true] If true, set state to collapsed.
+ * @returns {Boolean} Whether the state changed.
+ */
+ collapse: (state=true) ->
+ state = !! state
+ @isCollapsed = @$el.hasClass 'collapsed'
+
+ return this if state is @isCollapsed
+ if state
+ @$el.addClass 'collapsed'
+ else
+ @$el.removeClass 'collapsed'
+ @isCollapsed = state
+ @trigger 'change:collapse', this, @isCollapsed
+ true
+
+ /**
+ * Toggles the collapsed state, updating the UI and firing a `'change:collapse'` event.
+ * @returns {this}
+ */
+ toggleCollapsed: ->
+ @collapse not @$el.hasClass 'collapsed'
+ 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.tog