--- /dev/null
+_ = require 'kraken/underscore'
+op = require 'kraken/util/op'
+{EventEmitter} = require 'events'
+{Parsers, ParserMixin} = require 'kraken/util/parser'
+
+
+/**
+ * @class Specification for an option.
+ */
+class exports.ChartOption
+ SPEC_KEYS : <[ name type default desc tags examples ]>
+
+ name : null
+ type : 'String'
+ default : null
+ desc : ''
+ tags : null
+ examples : null
+
+
+ (@library, @spec) ->
+ throw new Error('Each ChartOption requires a name!') unless @spec.name
+
+ for k of @SPEC_KEYS
+ v = @spec[k]
+ @[k] = v if v?
+ @tags or= []
+ @parse = @library.getParser @type
+
+ parse : Parsers.parseString
+
+ isDefault: (v) ->
+ @default is v
+
+ toString: -> "(#{@name}: #{@type})"
+
+
+/**
+ * @class Abstraction of a charting library, encapsulating its logic and options.
+ */
+class exports.ChartLibrary extends EventEmitter
+ /**
+ * Ordered ChartOption objects.
+ * @type ChartOption[]
+ */
+ options_ordered : null
+
+ /**
+ * Map of option name to ChartOption objects.
+ * @type { name:ChartOption, ... }
+ */
+ options : null
+
+
+
+ /**
+ * @constructor
+ * @param {String} name Library name.
+ * @param {Array} options List of options objects, each specifying the
+ * name, type, default, description (etc) of a chart library option.
+ */
+ (@name, options) ->
+ @options_ordered = _.map options, (opt) ~> new ChartOption this, opt
+ @options = _.synthesize @options, -> [it.name, it]
+
+
+ /**
+ * @returns {ChartOption} Get an option's spec by name.
+ */
+ get: (name, def) ->
+ @options[name] or def
+
+ /**
+ * @returns {Array} List of values found at the given attr on each
+ * option spec object.
+ */
+ pluck: (attr) ->
+ _.pluck @spec, attr
+
+ /**
+ * @returns {Object} An object, mapping from option.name to the
+ * result of the supplied function.
+ */
+ map: (fn, context=this) ->
+ _.synthesize @spec, ~> [it.name, fn.call(context, it, it.name, this)]
+
+
+ /**
+ * @returns {Boolean} Whether the supplied value is the same as
+ * the default value for the given key.
+ */
+ isDefault: (name, value) ->
+ @get name .isDefault value
+
+
+ serialize: (v, k) ->
+ # if v!?
+ # v = ''
+ if _.isBoolean v
+ v = Number v
+ else if _.isObject v
+ v = JSON.stringify v
+ String v
+
+
+ /**
+ * When implementing a ChartLibrary, you can add or override parsers
+ * merely by subclassing.
+ */
+ this:: import ParserMixin::
+
+ getParserFor: (name) ->
+ @getParser @get(name).type
+
+
+
+
+
--- /dev/null
+_ = require 'kraken/underscore'
+{ ChartLibrary, ChartOption,
+} = require 'kraken/chart/chart-library'
+
+
+class exports.DygraphsLibrary extends ChartLibrary
+
+ (name, options) ->
+ super ...
+
+ render: ->
+ ...
+
--- /dev/null
+library = require 'kraken/chart/chart-library'
+dygraphs = require 'kraken/chart/dygraphs-library'
+exports import library import dygraphs
if match = /\/graph\/(?!view)([^\/?]+)/i.exec loc
data.slug = match[1]
- vis = root.vis = new VisModel data
+ vis = root.vis = new VisModel data, {+parse}
graph = root.graph = new VisView do
graph_spec : root.CHART_OPTIONS_SPEC
model : vis
_.reduce do
o
(acc, [k, v], idx) ->
- if k and filter(v, k)
+ if k and (not filter or filter(v, k))
acc[k] = v
acc
{}
* @param {Function} [fn=I] Transformation function. Defaults to the identity transform.
* @param {Function} [filter=defined] Optional filter function. If omitted, will
* exclude `undefined` and `null` values.
+ * @param {Object} [context=o] Function context.
* @return {Object} Transformed result.
*/
- synthesize : (o, fn=I, filter=defined) ->
- _array.generate _.map(o, fn), filter
+ synthesize : (o, fn=I, filter=defined, context) ->
+ _array.generate _.map(o, fn, context), filter
/**
--- /dev/null
+_ = require 'kraken/underscore'
+
+/**
+ * @class A mapping of key-value pairs supporting key-aliases.
+ */
+class AliasDict
+
+ /**
+ * Data store.
+ * @type Object
+ * @private
+ */
+ _data : null
+
+ /**
+ * Mapping from keys to an array of [potentially nested] alias-keys.
+ * @type Object<String, Array<String>>
+ * @private
+ */
+ _aliases : null
+
+
+ /**
+ * @constructor
+ */
+ ->
+ @_data = {}
+ @_aliases = {}
+ @extend ...
+
+
+ /**
+ * @returns {Number} Number of real keys in the Dict.
+ */
+ size : ->
+ _.keys @_data .length
+
+ /**
+ * @returns {AliasDict} A copy of the AliasDict, including aliases as well as data.
+ */
+ clone: ->
+ d = new AliasDict @_data
+ _.each @_aliases, (v, k) ->
+ d.setAlias k, v.slice()
+ d
+
+
+
+ ### Value Accessors ###
+
+ /**
+ * @returns {Boolean} Whether there is a value at the given key.
+ */
+ has : (key) ->
+ (@get key)?
+
+ /**
+ * @returns {*} Ignores aliases, returning the value at key or `undefined`.
+ */
+ getValue : (key) ->
+ prop = _.getNested @_data, key
+ prop.value if prop?
+
+ get : (key, def) ->
+ aliases = @_aliases[key] or [key]
+ val = aliases.reduce do
+ (val, alias) ->
+ return val if val? is not undefined
+ prop = _.getNested @_data, alias
+ prop.value if prop?
+ undefined
+
+ if val is not undefined
+ val
+ else
+ def
+
+ set : (key, val) ->
+ _.setNested @_data, key, val, true
+ val
+
+ del : (key) ->
+ prop = _.getNestedMeta key
+ if prop
+ delete prop.obj[prop.key]
+ prop.value
+
+
+
+ ### Alias Methods ###
+
+ hasAlias : (key) ->
+ @_aliases[key]?
+
+ getAlias : (key, def=[]) ->
+ @_aliases[key] or def
+
+ setAlias: (key, aliases) ->
+ @_aliases[key] = if _.isArray aliases then aliases else [aliases]
+ this
+
+ addAlias : (key, ...aliases) ->
+ @_aliases[key] = _.flatten @getAlias(key, [key]).concat(aliases)
+ this
+
+ delAlias : (key) ->
+ delete @_aliases[key]
+
+
+
+ ### Collection Methods ###
+
+ toObject: ->
+ _.clone @_data
+
+ keys: ->
+ _.keys @_data
+
+ values: ->
+ _.values @_data
+
+ extend : (...args) ->
+ for o of args
+ for k,v in o then @set k, v
+ this
+
+ reduce : (fn, acc, context=this) ->
+ _.reduce @_data, fn, acc, context
+
+ map : (fn, context=this) ->
+ _.map @_data, fn, context
+
+ filter: (fn, context=this) ->
+ _.filter @_data, fn, context
+
+ each : (fn, context=this) ->
+ _.each @_data, fn, context
+ this
+
+ invoke : (name, ...args) ->
+ _.invoke @_data, name, ...args
+
+ pluck : (attr) ->
+ _.pluck @_data, attr
+
+ find: (fn, context=this) ->
+ _.find @_data, fn, context
+
+
+ toString: ->
+ Cls = @.constructor
+ "#{Cls.displayName or Cls.name}()"
+
+
+module.exports = exports = AliasDict
+
+
+
/**
* @returns {Array<Class>} The list of all superclasses for this class or object.
*/
- getSuperClasses: function getSuperClasses(cls)
- cls .= constructor if cls and typeof cls is not 'function'
- if superclass = cls?.__super__?.constructor
+ getSuperClasses: function getSuperClasses(Cls)
+ Cls .= constructor if cls and typeof Cls is not 'function'
+ if superclass = Cls?.__super__?.constructor
[superclass].concat getSuperClasses superclass
else
[]
--- /dev/null
+_ = require 'kraken/underscore'
+
+/**
+ * @class A mapping of key-value pairs supporting lookup fallback across multiple objects.
+ */
+class Cascade
+
+ /**
+ * List of objects for lookups.
+ * @type Array
+ * @private
+ */
+ _dicts : null
+
+
+ /**
+ * @constructor
+ */
+ ->
+ @_dicts = []
+ @extend ...
+
+
+ /**
+ * @returns {Number} Number of real keys in the Dict.
+ */
+ size : ->
+ _.keys @_dicts .length
+
+ /**
+ * @returns {Cascade} A copy of the dict, including fallbacks as well as data.
+ */
+ clone: ->
+ d = new Cascade
+ _.each @_dicts, (v, k) ->
+ d.setAlias k, v.slice()
+ d
+
+
+
+ ### Value Accessors ###
+
+ /**
+ * @returns {Boolean} Whether there is a value at the given key.
+ */
+ has : (key) ->
+ (@get key, null)?
+
+ /**
+ * @returns {*} Ignores aliases, returning the value at key or `undefined`.
+ */
+ getValue : (key) ->
+ prop = _.getNested @_dicts, key
+ prop.value if prop?
+
+ get : (key, def) ->
+ aliases = @_aliases[key] or [key]
+ val = aliases.reduce do
+ (val, alias) ->
+ return val if val? is not undefined
+ prop = _.getNested @_dicts, alias
+ prop.value if prop?
+ undefined
+
+ if val is not undefined
+ val
+ else
+ def
+
+ set : (key, val) ->
+ _.setNested @_dicts, key, val, true
+ val
+
+ del : (key) ->
+ prop = _.getNestedMeta key
+ if prop
+ delete prop.obj[prop.key]
+ prop.value
+
+
+
+
+ ### Collection Methods ###
+
+ toObject: ->
+ _.extend {}, ...@_dicts
+
+ # XXX: Merge keys from all objects?
+ keys: ->
+ _.keys @_data
+
+ values: ->
+ _.values @_data
+
+ extend : (...args) ->
+ for o of args
+ for k,v in o then @set k, v
+ this
+
+ reduce : (fn, acc, context=this) ->
+ _.reduce @_data, fn, acc, context
+
+ map : (fn, context=this) ->
+ _.map @_data, fn, context
+
+ filter: (fn, context=this) ->
+ _.filter @_data, fn, context
+
+ each : (fn, context=this) ->
+ _.each @_data, fn, context
+ this
+
+ invoke : (name, ...args) ->
+ _.invoke @_data, name, ...args
+
+ pluck : (attr) ->
+ _.pluck @_data, attr
+
+ find: (fn, context=this) ->
+ _.find @_data, fn, context
+
+
+
+ toString: ->
+ Cls = @.constructor
+ "#{Cls.displayName or Cls.name}()"
+
+
+
+module.exports = exports = Cascade
# HashSet = require 'kraken/util/hashset'
# BitString = require 'kraken/util/bitstring'
# {crc32} = require 'kraken/util/crc'
+parser = require 'kraken/util/parser'
## Debug
o
-exports import { root, _, op, backbone }
-# exports import { root, _, op, HashSet, BitString, crc32, }
+exports import { root, _, op, backbone, parser, }
+# exports import { root, _, op, HashSet, BitString, crc32, parser, }
--- /dev/null
+_ = require 'kraken/underscore'
+op = require 'kraken/util/op'
+
+
+/**
+ * @namespace Parsers by type.
+ */
+Parsers = exports.Parsers =
+
+ parseBoolean: (v) ->
+ op.toBool v
+
+ parseInteger: (v) ->
+ r = op.toInt v
+ unless isNaN r then r else null
+
+ parseFloat: (v) ->
+ r = op.toFloat v
+ unless isNaN r then r else null
+
+ parseString: (v) ->
+ if v? then op.toStr v else null
+
+ parseArray: (v) ->
+ if v then op.toObject v else null
+
+ parseObject: (v) ->
+ if v then op.toObject v else null
+
+ parseFunction: (v) ->
+ if v and _.startswith String(v), 'function'
+ try eval "(#v)" catch err then null
+ else
+ null
+
+
+
+class exports.ParserMixin
+ this:: import Parsers
+
+
+ parse: (v, type) ->
+ @getParser(type)(v)
+
+ getParser: (type='String') ->
+ # If this is a known type and we have a parser for it, return that
+ fn = @["parse#type"]
+ return fn if typeof fn is 'function'
+
+ # Handle compound/optional types
+ # XXX: handle 'or' by returning an array of parsers?
+ type = _ String(type).toLowerCase()
+ for t of <[ Integer Float Boolean Object Array Function ]>
+ if type.startsWith t.toLowerCase()
+ return @["parse#t"]
+ @defaultParser or @parseString
+
+ getParserFromExample: (v) ->
+ return null unless v?
+ type = typeof v
+
+ if type is not 'object'
+ @getParser type
+ else if _.isArray v
+ @getParser 'Array'
+ else
+ @getParser 'Object'
+
+
+
+
- util:
- op
- backbone
+ - parser
- index
- base
+ - chart:
+ - chart-library
+ - dygraphs-library
+ - index
- template:
- graph.jade
- graph-option.jade