StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
StackGenVis/frontend/node_modules/vega-parser/build/vega-parser.js

3855 lines
107 KiB

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vega-util'), require('vega-expression'), require('vega-functions'), require('vega-event-selector'), require('vega-scale'), require('vega-dataflow')) :
typeof define === 'function' && define.amd ? define(['exports', 'vega-util', 'vega-expression', 'vega-functions', 'vega-event-selector', 'vega-scale', 'vega-dataflow'], factory) :
(global = global || self, factory(global.vega = {}, global.vega, global.vega, global.vega, global.vega, global.vega, global.vega));
}(this, (function (exports, vegaUtil, vegaExpression, vegaFunctions, vegaEventSelector, vegaScale, vegaDataflow) { 'use strict';
function parseAutosize(spec, config) {
spec = spec || config.autosize;
return vegaUtil.isObject(spec)
? spec
: {type: spec || 'pad'};
}
function parsePadding(spec, config) {
spec = spec || config.padding;
return vegaUtil.isObject(spec)
? {
top: number(spec.top),
bottom: number(spec.bottom),
left: number(spec.left),
right: number(spec.right)
}
: paddingObject(number(spec));
}
function number(_) {
return +_ || 0;
}
function paddingObject(_) {
return {top: _, bottom: _, left: _, right: _};
}
var OUTER = 'outer',
OUTER_INVALID = ['value', 'update', 'init', 'react', 'bind'];
function outerError(prefix, name) {
vegaUtil.error(prefix + ' for "outer" push: ' + vegaUtil.stringValue(name));
}
function parseSignal(signal, scope) {
var name = signal.name;
if (signal.push === OUTER) {
// signal must already be defined, raise error if not
if (!scope.signals[name]) outerError('No prior signal definition', name);
// signal push must not use properties reserved for standard definition
OUTER_INVALID.forEach(function(prop) {
if (signal[prop] !== undefined) outerError('Invalid property ', prop);
});
} else {
// define a new signal in the current scope
var op = scope.addSignal(name, signal.value);
if (signal.react === false) op.react = false;
if (signal.bind) scope.addBinding(name, signal.bind);
}
}
function parseExpression(expr, scope, preamble) {
var params = {}, ast, gen;
// parse the expression to an abstract syntax tree (ast)
try {
expr = vegaUtil.isString(expr) ? expr : (vegaUtil.stringValue(expr) + '');
ast = vegaExpression.parse(expr);
} catch (err) {
vegaUtil.error('Expression parse error: ' + expr);
}
// analyze ast function calls for dependencies
ast.visit(function visitor(node) {
if (node.type !== vegaExpression.CallExpression) return;
var name = node.callee.name,
visit = vegaFunctions.codegenParams.visitors[name];
if (visit) visit(name, node.arguments, scope, params);
});
// perform code generation
gen = vegaFunctions.codeGenerator(ast);
// collect signal dependencies
gen.globals.forEach(function(name) {
var signalName = vegaFunctions.SignalPrefix + name;
if (!vegaUtil.hasOwnProperty(params, signalName) && scope.getSignal(name)) {
params[signalName] = scope.signalRef(name);
}
});
// return generated expression code and dependencies
return {
$expr: preamble ? preamble + 'return(' + gen.code + ');' : gen.code,
$fields: gen.fields,
$params: params
};
}
function Entry(type, value, params, parent) {
this.id = -1;
this.type = type;
this.value = value;
this.params = params;
if (parent) this.parent = parent;
}
function entry(type, value, params, parent) {
return new Entry(type, value, params, parent);
}
function operator(value, params) {
return entry('operator', value, params);
}
// -----
function ref(op) {
var ref = {$ref: op.id};
// if operator not yet registered, cache ref to resolve later
if (op.id < 0) (op.refs = op.refs || []).push(ref);
return ref;
}
function fieldRef(field, name) {
return name ? {$field: field, $name: name} : {$field: field};
}
var keyFieldRef = fieldRef('key');
function compareRef(fields, orders) {
return {$compare: fields, $order: orders};
}
function keyRef(fields, flat) {
var ref = {$key: fields};
if (flat) ref.$flat = true;
return ref;
}
// -----
var Ascending = 'ascending';
var Descending = 'descending';
function sortKey(sort) {
return !vegaUtil.isObject(sort) ? ''
: (sort.order === Descending ? '-' : '+')
+ aggrField(sort.op, sort.field);
}
function aggrField(op, field) {
return (op && op.signal ? '$' + op.signal : op || '')
+ (op && field ? '_' : '')
+ (field && field.signal ? '$' + field.signal : field || '');
}
// -----
var Scope = 'scope';
var View = 'view';
function isSignal(_) {
return _ && _.signal;
}
function isExpr(_) {
return _ && _.expr;
}
function hasSignal(_) {
if (isSignal(_)) return true;
if (vegaUtil.isObject(_)) for (var key in _) {
if (hasSignal(_[key])) return true;
}
return false;
}
function value(specValue, defaultValue) {
return specValue != null ? specValue : defaultValue;
}
function deref(v) {
return v && v.signal || v;
}
var Timer = 'timer';
function parseStream(stream, scope) {
var method = stream.merge ? mergeStream
: stream.stream ? nestedStream
: stream.type ? eventStream
: vegaUtil.error('Invalid stream specification: ' + vegaUtil.stringValue(stream));
return method(stream, scope);
}
function eventSource(source) {
return source === Scope ? View : (source || View);
}
function mergeStream(stream, scope) {
var list = stream.merge.map(s => parseStream(s, scope)),
entry = streamParameters({merge: list}, stream, scope);
return scope.addStream(entry).id;
}
function nestedStream(stream, scope) {
var id = parseStream(stream.stream, scope),
entry = streamParameters({stream: id}, stream, scope);
return scope.addStream(entry).id;
}
function eventStream(stream, scope) {
var id, entry;
if (stream.type === Timer) {
id = scope.event(Timer, stream.throttle);
stream = {between: stream.between, filter: stream.filter};
} else {
id = scope.event(eventSource(stream.source), stream.type);
}
entry = streamParameters({stream: id}, stream, scope);
return Object.keys(entry).length === 1
? id
: scope.addStream(entry).id;
}
function streamParameters(entry, stream, scope) {
var param = stream.between;
if (param) {
if (param.length !== 2) {
vegaUtil.error('Stream "between" parameter must have 2 entries: ' + vegaUtil.stringValue(stream));
}
entry.between = [
parseStream(param[0], scope),
parseStream(param[1], scope)
];
}
param = stream.filter ? [].concat(stream.filter) : [];
if (stream.marktype || stream.markname || stream.markrole) {
// add filter for mark type, name and/or role
param.push(filterMark(stream.marktype, stream.markname, stream.markrole));
}
if (stream.source === Scope) {
// add filter to limit events from sub-scope only
param.push('inScope(event.item)');
}
if (param.length) {
entry.filter = parseExpression('(' + param.join(')&&(') + ')').$expr;
}
if ((param = stream.throttle) != null) {
entry.throttle = +param;
}
if ((param = stream.debounce) != null) {
entry.debounce = +param;
}
if (stream.consume) {
entry.consume = true;
}
return entry;
}
function filterMark(type, name, role) {
var item = 'event.item';
return item
+ (type && type !== '*' ? '&&' + item + '.mark.marktype===\'' + type + '\'' : '')
+ (role ? '&&' + item + '.mark.role===\'' + role + '\'' : '')
+ (name ? '&&' + item + '.mark.name===\'' + name + '\'' : '');
}
var preamble = 'var datum=event.item&&event.item.datum;';
function parseUpdate(spec, scope, target) {
var events = spec.events,
update = spec.update,
encode = spec.encode,
sources = [],
entry = {target: target};
if (!events) {
vegaUtil.error('Signal update missing events specification.');
}
// interpret as an event selector string
if (vegaUtil.isString(events)) {
events = vegaEventSelector.selector(events, scope.isSubscope() ? Scope : View);
}
// separate event streams from signal updates
events = vegaUtil.array(events)
.filter(s => s.signal || s.scale ? (sources.push(s), 0) : 1);
// merge internal operator listeners
if (sources.length > 1) {
sources = [mergeSources(sources)];
}
// merge event streams, include as source
if (events.length) {
sources.push(events.length > 1 ? {merge: events} : events[0]);
}
if (encode != null) {
if (update) vegaUtil.error('Signal encode and update are mutually exclusive.');
update = 'encode(item(),' + vegaUtil.stringValue(encode) + ')';
}
// resolve update value
entry.update = vegaUtil.isString(update) ? parseExpression(update, scope, preamble)
: update.expr != null ? parseExpression(update.expr, scope, preamble)
: update.value != null ? update.value
: update.signal != null ? {
$expr: '_.value',
$params: {value: scope.signalRef(update.signal)}
}
: vegaUtil.error('Invalid signal update specification.');
if (spec.force) {
entry.options = {force: true};
}
sources.forEach(function(source) {
scope.addUpdate(vegaUtil.extend(streamSource(source, scope), entry));
});
}
function streamSource(stream, scope) {
return {
source: stream.signal ? scope.signalRef(stream.signal)
: stream.scale ? scope.scaleRef(stream.scale)
: parseStream(stream, scope)
};
}
function mergeSources(sources) {
return {
signal: '['
+ sources.map(s => s.scale ? 'scale("' + s.scale + '")' : s.signal)
+ ']'
};
}
function parseSignalUpdates(signal, scope) {
var op = scope.getSignal(signal.name),
expr = signal.update;
if (signal.init) {
if (expr) {
vegaUtil.error('Signals can not include both init and update expressions.');
} else {
expr = signal.init;
op.initonly = true;
}
}
if (expr) {
expr = parseExpression(expr, scope);
op.update = expr.$expr;
op.params = expr.$params;
}
if (signal.on) {
signal.on.forEach(function(_) {
parseUpdate(_, scope, op.id);
});
}
}
function transform(name) {
return function(params, value, parent) {
return entry(name, value, params || undefined, parent);
};
}
var Aggregate = transform('aggregate');
var AxisTicks = transform('axisticks');
var Bound = transform('bound');
var Collect = transform('collect');
var Compare = transform('compare');
var DataJoin = transform('datajoin');
var Encode = transform('encode');
var Expression = transform('expression');
var Facet = transform('facet');
var Field = transform('field');
var Key = transform('key');
var LegendEntries = transform('legendentries');
var Load = transform('load');
var Mark = transform('mark');
var MultiExtent = transform('multiextent');
var MultiValues = transform('multivalues');
var Overlap = transform('overlap');
var Params = transform('params');
var PreFacet = transform('prefacet');
var Projection = transform('projection');
var Proxy = transform('proxy');
var Relay = transform('relay');
var Render = transform('render');
var Scale = transform('scale');
var Sieve = transform('sieve');
var SortItems = transform('sortitems');
var ViewLayout = transform('viewlayout');
var Values = transform('values');
var FIELD_REF_ID = 0;
var MULTIDOMAIN_SORT_OPS = {min: 'min', max: 'max', count: 'sum'};
function initScale(spec, scope) {
var type = spec.type || 'linear';
if (!vegaScale.isValidScaleType(type)) {
vegaUtil.error('Unrecognized scale type: ' + vegaUtil.stringValue(type));
}
scope.addScale(spec.name, {
type: type,
domain: undefined
});
}
function parseScale(spec, scope) {
var params = scope.getScale(spec.name).params,
key;
params.domain = parseScaleDomain(spec.domain, spec, scope);
if (spec.range != null) {
params.range = parseScaleRange(spec, scope, params);
}
if (spec.interpolate != null) {
parseScaleInterpolate(spec.interpolate, params);
}
if (spec.nice != null) {
params.nice = parseScaleNice(spec.nice);
}
if (spec.bins != null) {
params.bins = parseScaleBins(spec.bins, scope);
}
for (key in spec) {
if (vegaUtil.hasOwnProperty(params, key) || key === 'name') continue;
params[key] = parseLiteral(spec[key], scope);
}
}
function parseLiteral(v, scope) {
return !vegaUtil.isObject(v) ? v
: v.signal ? scope.signalRef(v.signal)
: vegaUtil.error('Unsupported object: ' + vegaUtil.stringValue(v));
}
function parseArray(v, scope) {
return v.signal
? scope.signalRef(v.signal)
: v.map(v => parseLiteral(v, scope));
}
function dataLookupError(name) {
vegaUtil.error('Can not find data set: ' + vegaUtil.stringValue(name));
}
// -- SCALE DOMAIN ----
function parseScaleDomain(domain, spec, scope) {
if (!domain) {
if (spec.domainMin != null || spec.domainMax != null) {
vegaUtil.error('No scale domain defined for domainMin/domainMax to override.');
}
return; // default domain
}
return domain.signal ? scope.signalRef(domain.signal)
: (vegaUtil.isArray(domain) ? explicitDomain
: domain.fields ? multipleDomain
: singularDomain)(domain, spec, scope);
}
function explicitDomain(domain, spec, scope) {
return domain.map(function(v) {
return parseLiteral(v, scope);
});
}
function singularDomain(domain, spec, scope) {
var data = scope.getData(domain.data);
if (!data) dataLookupError(domain.data);
return vegaScale.isDiscrete(spec.type)
? data.valuesRef(scope, domain.field, parseSort(domain.sort, false))
: vegaScale.isQuantile(spec.type) ? data.domainRef(scope, domain.field)
: data.extentRef(scope, domain.field);
}
function multipleDomain(domain, spec, scope) {
var data = domain.data,
fields = domain.fields.reduce(function(dom, d) {
d = vegaUtil.isString(d) ? {data: data, field: d}
: (vegaUtil.isArray(d) || d.signal) ? fieldRef$1(d, scope)
: d;
dom.push(d);
return dom;
}, []);
return (vegaScale.isDiscrete(spec.type) ? ordinalMultipleDomain
: vegaScale.isQuantile(spec.type) ? quantileMultipleDomain
: numericMultipleDomain)(domain, scope, fields);
}
function fieldRef$1(data, scope) {
var name = '_:vega:_' + (FIELD_REF_ID++),
coll = Collect({});
if (vegaUtil.isArray(data)) {
coll.value = {$ingest: data};
} else if (data.signal) {
var code = 'setdata(' + vegaUtil.stringValue(name) + ',' + data.signal + ')';
coll.params.input = scope.signalRef(code);
}
scope.addDataPipeline(name, [coll, Sieve({})]);
return {data: name, field: 'data'};
}
function ordinalMultipleDomain(domain, scope, fields) {
var sort = parseSort(domain.sort, true),
counts, p, a, c, v;
// get value counts for each domain field
counts = fields.map(function(f) {
var data = scope.getData(f.data);
if (!data) dataLookupError(f.data);
return data.countsRef(scope, f.field, sort);
});
// aggregate the results from each domain field
p = {groupby: keyFieldRef, pulse: counts};
if (sort) {
a = sort.op || 'count';
v = sort.field ? aggrField(a, sort.field) : 'count';
p.ops = [MULTIDOMAIN_SORT_OPS[a]];
p.fields = [scope.fieldRef(v)];
p.as = [v];
}
a = scope.add(Aggregate(p));
// collect aggregate output
c = scope.add(Collect({pulse: ref(a)}));
// extract values for combined domain
v = scope.add(Values({
field: keyFieldRef,
sort: scope.sortRef(sort),
pulse: ref(c)
}));
return ref(v);
}
function parseSort(sort, multidomain) {
if (sort) {
if (!sort.field && !sort.op) {
if (vegaUtil.isObject(sort)) sort.field = 'key';
else sort = {field: 'key'};
} else if (!sort.field && sort.op !== 'count') {
vegaUtil.error('No field provided for sort aggregate op: ' + sort.op);
} else if (multidomain && sort.field) {
if (sort.op && !MULTIDOMAIN_SORT_OPS[sort.op]) {
vegaUtil.error('Multiple domain scales can not be sorted using ' + sort.op);
}
}
}
return sort;
}
function quantileMultipleDomain(domain, scope, fields) {
// get value arrays for each domain field
var values = fields.map(function(f) {
var data = scope.getData(f.data);
if (!data) dataLookupError(f.data);
return data.domainRef(scope, f.field);
});
// combine value arrays
return ref(scope.add(MultiValues({values: values})));
}
function numericMultipleDomain(domain, scope, fields) {
// get extents for each domain field
var extents = fields.map(function(f) {
var data = scope.getData(f.data);
if (!data) dataLookupError(f.data);
return data.extentRef(scope, f.field);
});
// combine extents
return ref(scope.add(MultiExtent({extents: extents})));
}
// -- SCALE BINS -----
function parseScaleBins(v, scope) {
return v.signal || vegaUtil.isArray(v)
? parseArray(v, scope)
: scope.objectProperty(v);
}
// -- SCALE NICE -----
function parseScaleNice(nice) {
return vegaUtil.isObject(nice)
? {
interval: parseLiteral(nice.interval),
step: parseLiteral(nice.step)
}
: parseLiteral(nice);
}
// -- SCALE INTERPOLATION -----
function parseScaleInterpolate(interpolate, params) {
params.interpolate = parseLiteral(interpolate.type || interpolate);
if (interpolate.gamma != null) {
params.interpolateGamma = parseLiteral(interpolate.gamma);
}
}
// -- SCALE RANGE -----
function parseScaleRange(spec, scope, params) {
var range = spec.range,
config = scope.config.range;
if (range.signal) {
return scope.signalRef(range.signal);
} else if (vegaUtil.isString(range)) {
if (config && vegaUtil.hasOwnProperty(config, range)) {
spec = vegaUtil.extend({}, spec, {range: config[range]});
return parseScaleRange(spec, scope, params);
} else if (range === 'width') {
range = [0, {signal: 'width'}];
} else if (range === 'height') {
range = vegaScale.isDiscrete(spec.type)
? [0, {signal: 'height'}]
: [{signal: 'height'}, 0];
} else {
vegaUtil.error('Unrecognized scale range value: ' + vegaUtil.stringValue(range));
}
} else if (range.scheme) {
params.scheme = vegaUtil.isArray(range.scheme)
? parseArray(range.scheme, scope)
: parseLiteral(range.scheme, scope);
if (range.extent) params.schemeExtent = parseArray(range.extent, scope);
if (range.count) params.schemeCount = parseLiteral(range.count, scope);
return;
} else if (range.step) {
params.rangeStep = parseLiteral(range.step, scope);
return;
} else if (vegaScale.isDiscrete(spec.type) && !vegaUtil.isArray(range)) {
return parseScaleDomain(range, spec, scope);
} else if (!vegaUtil.isArray(range)) {
vegaUtil.error('Unsupported range type: ' + vegaUtil.stringValue(range));
}
return range.map(v => (vegaUtil.isArray(v) ? parseArray : parseLiteral)(v, scope));
}
function parseProjection(proj, scope) {
var config = scope.config.projection || {},
params = {};
for (var name in proj) {
if (name === 'name') continue;
params[name] = parseParameter(proj[name], name, scope);
}
// apply projection defaults from config
for (name in config) {
if (params[name] == null) {
params[name] = parseParameter(config[name], name, scope);
}
}
scope.addProjection(proj.name, params);
}
function parseParameter(_, name, scope) {
return vegaUtil.isArray(_) ? _.map(function(_) { return parseParameter(_, name, scope); })
: !vegaUtil.isObject(_) ? _
: _.signal ? scope.signalRef(_.signal)
: name === 'fit' ? _
: vegaUtil.error('Unsupported parameter object: ' + vegaUtil.stringValue(_));
}
const Top = 'top';
const Left = 'left';
const Right = 'right';
const Bottom = 'bottom';
const Center = 'center';
const Vertical = 'vertical';
const Start = 'start';
const Middle = 'middle';
const End = 'end';
const Index = 'index';
const Label = 'label';
const Offset = 'offset';
const Perc = 'perc';
const Perc2 = 'perc2';
const Size = 'size';
const Value = 'value';
const GuideLabelStyle = 'guide-label';
const GuideTitleStyle = 'guide-title';
const GroupTitleStyle = 'group-title';
const GroupSubtitleStyle = 'group-subtitle';
const Symbols = 'symbol';
const Gradient = 'gradient';
const Discrete = 'discrete';
// Encoding channels supported by legends
// In priority order of 'canonical' scale
const LegendScales = [
'size',
'shape',
'fill',
'stroke',
'strokeWidth',
'strokeDash',
'opacity'
];
const Skip = {
name: 1,
style: 1,
interactive: 1
};
const zero = {value: 0};
const one = {value: 1};
var Skip$1 = vegaUtil.toSet(['rule']),
Swap = vegaUtil.toSet(['group', 'image', 'rect']);
function adjustSpatial(encode, marktype) {
var code = '';
if (Skip$1[marktype]) return code;
if (encode.x2) {
if (encode.x) {
if (Swap[marktype]) {
code += 'if(o.x>o.x2)$=o.x,o.x=o.x2,o.x2=$;';
}
code += 'o.width=o.x2-o.x;';
} else {
code += 'o.x=o.x2-(o.width||0);';
}
}
if (encode.xc) {
code += 'o.x=o.xc-(o.width||0)/2;';
}
if (encode.y2) {
if (encode.y) {
if (Swap[marktype]) {
code += 'if(o.y>o.y2)$=o.y,o.y=o.y2,o.y2=$;';
}
code += 'o.height=o.y2-o.y;';
} else {
code += 'o.y=o.y2-(o.height||0);';
}
}
if (encode.yc) {
code += 'o.y=o.yc-(o.height||0)/2;';
}
return code;
}
function color(enc, scope, params, fields) {
function color(type, x, y, z) {
var a = entry$1(null, x, scope, params, fields),
b = entry$1(null, y, scope, params, fields),
c = entry$1(null, z, scope, params, fields);
return 'this.' + type + '(' + [a, b, c].join(',') + ').toString()';
}
return (enc.c) ? color('hcl', enc.h, enc.c, enc.l)
: (enc.h || enc.s) ? color('hsl', enc.h, enc.s, enc.l)
: (enc.l || enc.a) ? color('lab', enc.l, enc.a, enc.b)
: (enc.r || enc.g || enc.b) ? color('rgb', enc.r, enc.g, enc.b)
: null;
}
function expression(code, scope, params, fields) {
var expr = parseExpression(code, scope);
expr.$fields.forEach(function(name) { fields[name] = 1; });
vegaUtil.extend(params, expr.$params);
return expr.$expr;
}
function field(ref, scope, params, fields) {
return resolve(vegaUtil.isObject(ref) ? ref : {datum: ref}, scope, params, fields);
}
function resolve(ref, scope, params, fields) {
var object, level, field;
if (ref.signal) {
object = 'datum';
field = expression(ref.signal, scope, params, fields);
} else if (ref.group || ref.parent) {
level = Math.max(1, ref.level || 1);
object = 'item';
while (level-- > 0) {
object += '.mark.group';
}
if (ref.parent) {
field = ref.parent;
object += '.datum';
} else {
field = ref.group;
}
} else if (ref.datum) {
object = 'datum';
field = ref.datum;
} else {
vegaUtil.error('Invalid field reference: ' + vegaUtil.stringValue(ref));
}
if (!ref.signal) {
if (vegaUtil.isString(field)) {
fields[field] = 1; // TODO review field tracking?
field = vegaUtil.splitAccessPath(field).map(vegaUtil.stringValue).join('][');
} else {
field = resolve(field, scope, params, fields);
}
}
return object + '[' + field + ']';
}
function property(property, scope, params, fields) {
return vegaUtil.isObject(property)
? '(' + entry$1(null, property, scope, params, fields) + ')'
: property;
}
function scale(enc, value, scope, params, fields) {
var scale = getScale(enc.scale, scope, params, fields),
interp, func, flag;
if (enc.range != null) {
// pull value from scale range
interp = +enc.range;
func = scale + '.range()';
value = (interp === 0) ? (func + '[0]')
: '($=' + func + ',' + ((interp === 1) ? '$[$.length-1]'
: '$[0]+' + interp + '*($[$.length-1]-$[0])') + ')';
} else {
// run value through scale and/or pull scale bandwidth
if (value !== undefined) value = scale + '(' + value + ')';
if (enc.band && (flag = hasBandwidth(enc.scale, scope))) {
func = scale + '.bandwidth';
if (enc.band.signal) {
interp = func + '()*' + property(enc.band, scope, params, fields);
} else {
interp = +enc.band;
interp = func + '()' + (interp===1 ? '' : '*' + interp);
}
// if we don't know the scale type, check for bandwidth
if (flag < 0) interp = '(' + func + '?' + interp + ':0)';
value = (value ? value + '+' : '') + interp;
if (enc.extra) {
// include logic to handle extraneous elements
value = '(datum.extra?' + scale + '(datum.extra.value):' + value + ')';
}
}
if (value == null) value = '0';
}
return value;
}
function hasBandwidth(name, scope) {
if (!vegaUtil.isString(name)) return -1;
var type = scope.scaleType(name);
return type === 'band' || type === 'point' ? 1 : 0;
}
function getScale(name, scope, params, fields) {
var scaleName;
if (vegaUtil.isString(name)) {
// direct scale lookup; add scale as parameter
scaleName = vegaFunctions.ScalePrefix + name;
if (!vegaUtil.hasOwnProperty(params, scaleName)) {
params[scaleName] = scope.scaleRef(name);
}
scaleName = vegaUtil.stringValue(scaleName);
} else {
// indirect scale lookup; add all scales as parameters
for (scaleName in scope.scales) {
params[vegaFunctions.ScalePrefix + scaleName] = scope.scaleRef(scaleName);
}
scaleName = vegaUtil.stringValue(vegaFunctions.ScalePrefix) + '+'
+ (name.signal
? '(' + expression(name.signal, scope, params, fields) + ')'
: field(name, scope, params, fields));
}
return '_[' + scaleName + ']';
}
function gradient(enc, scope, params, fields) {
return 'this.gradient('
+ getScale(enc.gradient, scope, params, fields)
+ ',' + vegaUtil.stringValue(enc.start)
+ ',' + vegaUtil.stringValue(enc.stop)
+ ',' + vegaUtil.stringValue(enc.count)
+ ')';
}
function entry$1(channel, enc, scope, params, fields) {
if (enc.gradient != null) {
return gradient(enc, scope, params, fields);
}
var value = enc.signal ? expression(enc.signal, scope, params, fields)
: enc.color ? color(enc.color, scope, params, fields)
: enc.field != null ? field(enc.field, scope, params, fields)
: enc.value !== undefined ? vegaUtil.stringValue(enc.value)
: undefined;
if (enc.scale != null) {
value = scale(enc, value, scope, params, fields);
}
if (value === undefined) {
value = null;
}
if (enc.exponent != null) {
value = 'Math.pow(' + value + ','
+ property(enc.exponent, scope, params, fields) + ')';
}
if (enc.mult != null) {
value += '*' + property(enc.mult, scope, params, fields);
}
if (enc.offset != null) {
value += '+' + property(enc.offset, scope, params, fields);
}
if (enc.round) {
value = 'Math.round(' + value + ')';
}
return value;
}
function set(obj, key, value) {
const o = obj + '[' + vegaUtil.stringValue(key) + ']';
return `$=${value};if(${o}!==$)${o}=$,m=1;`;
}
function rule(channel, rules, scope, params, fields) {
var code = '';
rules.forEach(function(rule) {
var value = entry$1(channel, rule, scope, params, fields);
code += rule.test
? expression(rule.test, scope, params, fields) + '?' + value + ':'
: value;
});
// if no else clause, terminate with null (vega/vega#1366)
if (vegaUtil.peek(code) === ':') {
code += 'null';
}
return set('o', channel, code);
}
function parseEncode(encode, marktype, params, scope) {
var fields = {},
code = 'var o=item,datum=o.datum,m=0,$;',
channel, enc, value;
for (channel in encode) {
enc = encode[channel];
if (vegaUtil.isArray(enc)) { // rule
code += rule(channel, enc, scope, params, fields);
} else {
value = entry$1(channel, enc, scope, params, fields);
code += set('o', channel, value);
}
}
code += adjustSpatial(encode, marktype);
code += 'return m;';
return {
$expr: code,
$fields: Object.keys(fields),
$output: Object.keys(encode)
};
}
var MarkRole = 'mark';
var FrameRole = 'frame';
var ScopeRole = 'scope';
var AxisRole = 'axis';
var AxisDomainRole = 'axis-domain';
var AxisGridRole = 'axis-grid';
var AxisLabelRole = 'axis-label';
var AxisTickRole = 'axis-tick';
var AxisTitleRole = 'axis-title';
var LegendRole = 'legend';
var LegendBandRole = 'legend-band';
var LegendEntryRole = 'legend-entry';
var LegendGradientRole = 'legend-gradient';
var LegendLabelRole = 'legend-label';
var LegendSymbolRole = 'legend-symbol';
var LegendTitleRole = 'legend-title';
var TitleRole = 'title';
var TitleTextRole = 'title-text';
var TitleSubtitleRole = 'title-subtitle';
function encoder(_) {
return vegaUtil.isObject(_) && !vegaUtil.isArray(_) ? vegaUtil.extend({}, _) : {value: _};
}
function addEncode(object, name, value, set) {
if (value != null) {
if (vegaUtil.isObject(value) && !vegaUtil.isArray(value)) {
object.update[name] = value;
} else {
object[set || 'enter'][name] = {value: value};
}
return 1;
} else {
return 0;
}
}
function addEncoders(object, enter, update) {
for (let name in enter) {
addEncode(object, name, enter[name]);
}
for (let name in update) {
addEncode(object, name, update[name], 'update');
}
}
function extendEncode(encode, extra, skip) {
for (var name in extra) {
if (skip && vegaUtil.hasOwnProperty(skip, name)) continue;
encode[name] = vegaUtil.extend(encode[name] || {}, extra[name]);
}
return encode;
}
function encoders(encode, type, role, style, scope, params) {
var enc, key;
params = params || {};
params.encoders = {$encode: (enc = {})};
encode = applyDefaults(encode, type, role, style, scope.config);
for (key in encode) {
enc[key] = parseEncode(encode[key], type, params, scope);
}
return params;
}
function applyDefaults(encode, type, role, style, config) {
var defaults = {}, enter = {}, update, key, skip, props;
// ignore legend and axis
if (role == 'legend' || String(role).indexOf('axis') === 0) {
role = null;
}
// resolve mark config
props = role === FrameRole ? config.group
: (role === MarkRole) ? vegaUtil.extend({}, config.mark, config[type])
: null;
for (key in props) {
// do not apply defaults if relevant fields are defined
skip = has(key, encode)
|| (key === 'fill' || key === 'stroke')
&& (has('fill', encode) || has('stroke', encode));
if (!skip) applyDefault(defaults, key, props[key]);
}
// resolve styles, apply with increasing precedence
vegaUtil.array(style).forEach(function(name) {
var props = config.style && config.style[name];
for (var key in props) {
if (!has(key, encode)) {
applyDefault(defaults, key, props[key]);
}
}
});
encode = vegaUtil.extend({}, encode); // defensive copy
for (key in defaults) {
props = defaults[key];
if (props.signal) {
(update = update || {})[key] = props;
} else {
enter[key] = props;
}
}
encode.enter = vegaUtil.extend(enter, encode.enter);
if (update) encode.update = vegaUtil.extend(update, encode.update);
return encode;
}
function applyDefault(defaults, key, value) {
defaults[key] = value && value.signal
? {signal: value.signal}
: {value: value};
}
function has(key, encode) {
return encode && (
(encode.enter && encode.enter[key]) ||
(encode.update && encode.update[key])
);
}
function guideMark(type, role, style, key, dataRef, encode, extras) {
return {
type: type,
name: extras ? extras.name : undefined,
role: role,
style: (extras && extras.style) || style,
key: key,
from: dataRef,
interactive: !!(extras && extras.interactive),
encode: extendEncode(encode, extras, Skip)
};
}
function lookup(spec, config) {
const _ = (name, dflt) => value(spec[name], value(config[name], dflt));
_.isVertical = s => Vertical === value(
spec.direction,
config.direction || (s ? config.symbolDirection : config.gradientDirection)
);
_.gradientLength = () => value(
spec.gradientLength,
config.gradientLength || config.gradientWidth
);
_.gradientThickness = () => value(
spec.gradientThickness,
config.gradientThickness || config.gradientHeight
);
_.entryColumns = () => value(
spec.columns,
value(config.columns, +_.isVertical(true))
);
return _;
}
function getEncoding(name, encode) {
var v = encode && (
(encode.update && encode.update[name]) ||
(encode.enter && encode.enter[name])
);
return v && v.signal ? v : v ? v.value : null;
}
function getStyle(name, scope, style) {
var s = scope.config.style[style];
return s && s[name];
}
function anchorExpr(s, e, m) {
return `item.anchor === "${Start}" ? ${s} : item.anchor === "${End}" ? ${e} : ${m}`;
}
const alignExpr = anchorExpr(
vegaUtil.stringValue(Left),
vegaUtil.stringValue(Right),
vegaUtil.stringValue(Center)
);
function tickBand(_) {
let v = _('tickBand'),
offset = _('tickOffset'),
band, extra;
if (!v) {
// if no tick band entry, fall back on other properties
band = _('bandPosition');
extra = _('tickExtra');
} else if (v.signal) {
// if signal, augment code to interpret values
band = {signal: `(${v.signal})==='extent'?1:0.5`};
extra = {signal: `(${v.signal})==='extent'?true:false`};
if (!vegaUtil.isObject(offset)) {
offset = {signal: `(${v.signal})==='extent'?0:${offset}`};
}
} else if (v === 'extent') {
// if constant, simply set values
band = 1;
extra = true;
offset = 0;
} else {
band = 0.5;
extra = false;
}
return {extra, band, offset};
}
var GroupMark = 'group';
var RectMark = 'rect';
var RuleMark = 'rule';
var SymbolMark = 'symbol';
var TextMark = 'text';
function legendGradient(spec, scale, config, userEncode) {
var _ = lookup(spec, config),
vertical = _.isVertical(),
thickness = _.gradientThickness(),
length = _.gradientLength(),
encode, enter, start, stop, width, height;
if (vertical) {
start = [0, 1];
stop = [0, 0];
width = thickness;
height = length;
} else {
start = [0, 0];
stop = [1, 0];
width = length;
height = thickness;
}
encode = {
enter: enter = {
opacity: zero,
x: zero,
y: zero,
width: encoder(width),
height: encoder(height)
},
update: vegaUtil.extend({}, enter, {
opacity: one,
fill: {gradient: scale, start: start, stop: stop}
}),
exit: {
opacity: zero
}
};
addEncoders(encode, {
stroke: _('gradientStrokeColor'),
strokeWidth: _('gradientStrokeWidth')
}, { // update
opacity: _('gradientOpacity')
});
return guideMark(RectMark, LegendGradientRole, null, undefined, undefined, encode, userEncode);
}
function legendGradientDiscrete(spec, scale, config, userEncode, dataRef) {
var _ = lookup(spec, config),
vertical = _.isVertical(),
thickness = _.gradientThickness(),
length = _.gradientLength(),
encode, enter, u, v, uu, vv, adjust = '';
vertical
? (u = 'y', uu = 'y2', v = 'x', vv = 'width', adjust = '1-')
: (u = 'x', uu = 'x2', v = 'y', vv = 'height');
enter = {
opacity: zero,
fill: {scale: scale, field: Value}
};
enter[u] = {signal: adjust + 'datum.' + Perc, mult: length};
enter[v] = zero;
enter[uu] = {signal: adjust + 'datum.' + Perc2, mult: length};
enter[vv] = encoder(thickness);
encode = {
enter: enter,
update: vegaUtil.extend({}, enter, {opacity: one}),
exit: {opacity: zero}
};
addEncoders(encode, {
stroke: _('gradientStrokeColor'),
strokeWidth: _('gradientStrokeWidth')
}, { // update
opacity: _('gradientOpacity')
});
return guideMark(RectMark, LegendBandRole, null, Value, dataRef, encode, userEncode);
}
const alignExpr$1 = `datum.${Perc}<=0?"${Left}":datum.${Perc}>=1?"${Right}":"${Center}"`,
baselineExpr = `datum.${Perc}<=0?"${Bottom}":datum.${Perc}>=1?"${Top}":"${Middle}"`;
function legendGradientLabels(spec, config, userEncode, dataRef) {
var _ = lookup(spec, config),
vertical = _.isVertical(),
thickness = encoder(_.gradientThickness()),
length = _.gradientLength(),
overlap = _('labelOverlap'),
separation = _('labelSeparation'),
encode, enter, update, u, v, adjust = '';
encode = {
enter: enter = {
opacity: zero
},
update: update = {
opacity: one,
text: {field: Label}
},
exit: {
opacity: zero
}
};
addEncoders(encode, {
fill: _('labelColor'),
fillOpacity: _('labelOpacity'),
font: _('labelFont'),
fontSize: _('labelFontSize'),
fontStyle: _('labelFontStyle'),
fontWeight: _('labelFontWeight'),
limit: value(spec.labelLimit, config.gradientLabelLimit)
});
if (vertical) {
enter.align = {value: 'left'};
enter.baseline = update.baseline = {signal: baselineExpr};
u = 'y'; v = 'x'; adjust = '1-';
} else {
enter.align = update.align = {signal: alignExpr$1};
enter.baseline = {value: 'top'};
u = 'x'; v = 'y';
}
enter[u] = update[u] = {signal: adjust + 'datum.' + Perc, mult: length};
enter[v] = update[v] = thickness;
thickness.offset = value(spec.labelOffset, config.gradientLabelOffset) || 0;
spec = guideMark(TextMark, LegendLabelRole, GuideLabelStyle, Value, dataRef, encode, userEncode);
if (overlap) {
spec.overlap = {
separation: separation,
method: overlap,
order: 'datum.' + Index
};
}
return spec;
}
function guideGroup(role, style, name, dataRef, interactive, encode, marks, layout) {
return {
type: GroupMark,
name: name,
role: role,
style: style,
from: dataRef,
interactive: interactive || false,
encode: encode,
marks: marks,
layout: layout
};
}
// userEncode is top-level, includes entries, symbols, labels
function legendSymbolGroups(spec, config, userEncode, dataRef, columns) {
var _ = lookup(spec, config),
entries = userEncode.entries,
interactive = !!(entries && entries.interactive),
name = entries ? entries.name : undefined,
height = _('clipHeight'),
symbolOffset = _('symbolOffset'),
valueRef = {data: 'value'},
encode = {},
xSignal = `${columns} ? datum.${Offset} : datum.${Size}`,
yEncode = height ? encoder(height) : {field: Size},
index = `datum.${Index}`,
ncols = `max(1, ${columns})`,
enter, update, labelOffset, symbols, labels, nrows, sort;
yEncode.mult = 0.5;
// -- LEGEND SYMBOLS --
encode = {
enter: enter = {
opacity: zero,
x: {signal: xSignal, mult: 0.5, offset: symbolOffset},
y: yEncode
},
update: update = {
opacity: one,
x: enter.x,
y: enter.y
},
exit: {
opacity: zero
}
};
var baseFill = null,
baseStroke = null;
if (!spec.fill) {
baseFill = config.symbolBaseFillColor;
baseStroke = config.symbolBaseStrokeColor;
}
addEncoders(encode, {
fill: _('symbolFillColor', baseFill),
shape: _('symbolType'),
size: _('symbolSize'),
stroke: _('symbolStrokeColor', baseStroke),
strokeDash: _('symbolDash'),
strokeDashOffset: _('symbolDashOffset'),
strokeWidth: _('symbolStrokeWidth')
}, { // update
opacity: _('symbolOpacity')
});
LegendScales.forEach(function(scale) {
if (spec[scale]) {
update[scale] = enter[scale] = {scale: spec[scale], field: Value};
}
});
symbols = guideMark(
SymbolMark, LegendSymbolRole, null,
Value, valueRef, encode, userEncode.symbols
);
if (height) symbols.clip = true;
// -- LEGEND LABELS --
labelOffset = encoder(symbolOffset);
labelOffset.offset = _('labelOffset');
encode = {
enter: enter = {
opacity: zero,
x: {signal: xSignal, offset: labelOffset},
y: yEncode
},
update: update = {
opacity: one,
text: {field: Label},
x: enter.x,
y: enter.y
},
exit: {
opacity: zero
}
};
addEncoders(encode, {
align: _('labelAlign'),
baseline: _('labelBaseline'),
fill: _('labelColor'),
fillOpacity: _('labelOpacity'),
font: _('labelFont'),
fontSize: _('labelFontSize'),
fontStyle: _('labelFontStyle'),
fontWeight: _('labelFontWeight'),
limit: _('labelLimit')
});
labels = guideMark(
TextMark, LegendLabelRole, GuideLabelStyle,
Value, valueRef, encode, userEncode.labels
);
// -- LEGEND ENTRY GROUPS --
encode = {
enter: {
noBound: {value: !height}, // ignore width/height in bounds calc
width: zero,
height: height ? encoder(height) : zero,
opacity: zero
},
exit: {opacity: zero},
update: update = {
opacity: one,
row: {signal: null},
column: {signal: null}
}
};
// annotate and sort groups to ensure correct ordering
if (_.isVertical(true)) {
nrows = `ceil(item.mark.items.length / ${ncols})`;
update.row.signal = `${index}%${nrows}`;
update.column.signal = `floor(${index} / ${nrows})`;
sort = {field: ['row', index]};
} else {
update.row.signal = `floor(${index} / ${ncols})`;
update.column.signal = `${index} % ${ncols}`;
sort = {field: index};
}
// handle zero column case (implies infinite columns)
update.column.signal = `${columns}?${update.column.signal}:${index}`;
// facet legend entries into sub-groups
dataRef = {facet: {data: dataRef, name: 'value', groupby: Index}};
spec = guideGroup(
ScopeRole, null, name, dataRef, interactive,
extendEncode(encode, entries, Skip), [symbols, labels]
);
spec.sort = sort;
return spec;
}
function legendSymbolLayout(spec, config) {
const _ = lookup(spec, config);
// layout parameters for legend entries
return {
align: _('gridAlign'),
columns: _.entryColumns(),
center: {
row: true,
column: false
},
padding: {
row: _('rowPadding'),
column: _('columnPadding')
}
};
}
// expression logic for align, anchor, angle, and baseline calculation
const isL = 'item.orient === "left"',
isR = 'item.orient === "right"',
isLR = `(${isL} || ${isR})`,
isVG = `datum.vgrad && ${isLR}`,
baseline = anchorExpr('"top"', '"bottom"', '"middle"'),
alignFlip = anchorExpr('"right"', '"left"', '"center"'),
exprAlign = `datum.vgrad && ${isR} ? (${alignFlip}) : (${isLR} && !(datum.vgrad && ${isL})) ? "left" : ${alignExpr}`,
exprAnchor = `item._anchor || (${isLR} ? "middle" : "start")`,
exprAngle = `${isVG} ? (${isL} ? -90 : 90) : 0`,
exprBaseline = `${isLR} ? (datum.vgrad ? (${isR} ? "bottom" : "top") : ${baseline}) : "top"`;
function legendTitle(spec, config, userEncode, dataRef) {
var _ = lookup(spec, config), encode;
encode = {
enter: {opacity: zero},
update: {
opacity: one,
x: {field: {group: 'padding'}},
y: {field: {group: 'padding'}}
},
exit: {opacity: zero}
};
addEncoders(encode, {
orient: _('titleOrient'),
_anchor: _('titleAnchor'),
anchor: {signal: exprAnchor},
angle: {signal: exprAngle},
align: {signal: exprAlign},
baseline: {signal: exprBaseline},
text: spec.title,
fill: _('titleColor'),
fillOpacity: _('titleOpacity'),
font: _('titleFont'),
fontSize: _('titleFontSize'),
fontStyle: _('titleFontStyle'),
fontWeight: _('titleFontWeight'),
limit: _('titleLimit'),
lineHeight: _('titleLineHeight')
}, { // require update
align: _('titleAlign'),
baseline: _('titleBaseline'),
});
return guideMark(TextMark, LegendTitleRole, GuideTitleStyle, null, dataRef, encode, userEncode);
}
function clip(clip, scope) {
var expr;
if (vegaUtil.isObject(clip)) {
if (clip.signal) {
expr = clip.signal;
} else if (clip.path) {
expr = 'pathShape(' + param(clip.path) + ')';
} else if (clip.sphere) {
expr = 'geoShape(' + param(clip.sphere) + ', {type: "Sphere"})';
}
}
return expr
? scope.signalRef(expr)
: !!clip;
}
function param(value) {
return vegaUtil.isObject(value) && value.signal
? value.signal
: vegaUtil.stringValue(value);
}
function getRole(spec) {
var role = spec.role || '';
return (!role.indexOf('axis') || !role.indexOf('legend') || !role.indexOf('title'))
? role
: spec.type === GroupMark ? ScopeRole : (role || MarkRole);
}
function definition(spec) {
return {
marktype: spec.type,
name: spec.name || undefined,
role: spec.role || getRole(spec),
zindex: +spec.zindex || undefined
};
}
function interactive(spec, scope) {
return spec && spec.signal ? scope.signalRef(spec.signal)
: spec === false ? false
: true;
}
/**
* Parse a data transform specification.
*/
function parseTransform(spec, scope) {
var def = vegaDataflow.definition(spec.type);
if (!def) vegaUtil.error('Unrecognized transform type: ' + vegaUtil.stringValue(spec.type));
var t = entry(def.type.toLowerCase(), null, parseParameters(def, spec, scope));
if (spec.signal) scope.addSignal(spec.signal, scope.proxy(t));
t.metadata = def.metadata || {};
return t;
}
/**
* Parse all parameters of a data transform.
*/
function parseParameters(def, spec, scope) {
var params = {}, pdef, i, n;
for (i=0, n=def.params.length; i<n; ++i) {
pdef = def.params[i];
params[pdef.name] = parseParameter$1(pdef, spec, scope);
}
return params;
}
/**
* Parse a data transform parameter.
*/
function parseParameter$1(def, spec, scope) {
var type = def.type,
value = spec[def.name];
if (type === 'index') {
return parseIndexParameter(def, spec, scope);
} else if (value === undefined) {
if (def.required) {
vegaUtil.error('Missing required ' + vegaUtil.stringValue(spec.type)
+ ' parameter: ' + vegaUtil.stringValue(def.name));
}
return;
} else if (type === 'param') {
return parseSubParameters(def, spec, scope);
} else if (type === 'projection') {
return scope.projectionRef(spec[def.name]);
}
return def.array && !isSignal(value)
? value.map(function(v) { return parameterValue(def, v, scope); })
: parameterValue(def, value, scope);
}
/**
* Parse a single parameter value.
*/
function parameterValue(def, value, scope) {
var type = def.type;
if (isSignal(value)) {
return isExpr$1(type) ? vegaUtil.error('Expression references can not be signals.')
: isField(type) ? scope.fieldRef(value)
: isCompare(type) ? scope.compareRef(value)
: scope.signalRef(value.signal);
} else {
var expr = def.expr || isField(type);
return expr && outerExpr(value) ? scope.exprRef(value.expr, value.as)
: expr && outerField(value) ? fieldRef(value.field, value.as)
: isExpr$1(type) ? parseExpression(value, scope)
: isData(type) ? ref(scope.getData(value).values)
: isField(type) ? fieldRef(value)
: isCompare(type) ? scope.compareRef(value)
: value;
}
}
/**
* Parse parameter for accessing an index of another data set.
*/
function parseIndexParameter(def, spec, scope) {
if (!vegaUtil.isString(spec.from)) {
vegaUtil.error('Lookup "from" parameter must be a string literal.');
}
return scope.getData(spec.from).lookupRef(scope, spec.key);
}
/**
* Parse a parameter that contains one or more sub-parameter objects.
*/
function parseSubParameters(def, spec, scope) {
var value = spec[def.name];
if (def.array) {
if (!vegaUtil.isArray(value)) { // signals not allowed!
vegaUtil.error('Expected an array of sub-parameters. Instead: ' + vegaUtil.stringValue(value));
}
return value.map(function(v) {
return parseSubParameter(def, v, scope);
});
} else {
return parseSubParameter(def, value, scope);
}
}
/**
* Parse a sub-parameter object.
*/
function parseSubParameter(def, value, scope) {
var params, pdef, k, i, n;
// loop over defs to find matching key
for (i=0, n=def.params.length; i<n; ++i) {
pdef = def.params[i];
for (k in pdef.key) {
if (pdef.key[k] !== value[k]) { pdef = null; break; }
}
if (pdef) break;
}
// raise error if matching key not found
if (!pdef) vegaUtil.error('Unsupported parameter: ' + vegaUtil.stringValue(value));
// parse params, create Params transform, return ref
params = vegaUtil.extend(parseParameters(pdef, value, scope), pdef.key);
return ref(scope.add(Params(params)));
}
// -- Utilities -----
function outerExpr(_) {
return _ && _.expr;
}
function outerField(_) {
return _ && _.field;
}
function isData(_) {
return _ === 'data';
}
function isExpr$1(_) {
return _ === 'expr';
}
function isField(_) {
return _ === 'field';
}
function isCompare(_) {
return _ === 'compare'
}
function parseData(from, group, scope) {
var facet, key, op, dataRef, parent;
// if no source data, generate singleton datum
if (!from) {
dataRef = ref(scope.add(Collect(null, [{}])));
}
// if faceted, process facet specification
else if (facet = from.facet) {
if (!group) vegaUtil.error('Only group marks can be faceted.');
// use pre-faceted source data, if available
if (facet.field != null) {
dataRef = parent = getDataRef(facet, scope);
} else {
// generate facet aggregates if no direct data specification
if (!from.data) {
op = parseTransform(vegaUtil.extend({
type: 'aggregate',
groupby: vegaUtil.array(facet.groupby)
}, facet.aggregate), scope);
op.params.key = scope.keyRef(facet.groupby);
op.params.pulse = getDataRef(facet, scope);
dataRef = parent = ref(scope.add(op));
} else {
parent = ref(scope.getData(from.data).aggregate);
}
key = scope.keyRef(facet.groupby, true);
}
}
// if not yet defined, get source data reference
if (!dataRef) {
dataRef = getDataRef(from, scope);
}
return {
key: key,
pulse: dataRef,
parent: parent
};
}
function getDataRef(from, scope) {
return from.$ref ? from
: from.data && from.data.$ref ? from.data
: ref(scope.getData(from.data).output);
}
function DataScope(scope, input, output, values, aggr) {
this.scope = scope; // parent scope object
this.input = input; // first operator in pipeline (tuple input)
this.output = output; // last operator in pipeline (tuple output)
this.values = values; // operator for accessing tuples (but not tuple flow)
// last aggregate in transform pipeline
this.aggregate = aggr;
// lookup table of field indices
this.index = {};
}
DataScope.fromEntries = function(scope, entries) {
var n = entries.length,
i = 1,
input = entries[0],
values = entries[n-1],
output = entries[n-2],
aggr = null;
if (input && input.type === 'load') {
input = entries[1];
}
// add operator entries to this scope, wire up pulse chain
scope.add(entries[0]);
for (; i<n; ++i) {
entries[i].params.pulse = ref(entries[i-1]);
scope.add(entries[i]);
if (entries[i].type === 'aggregate') aggr = entries[i];
}
return new DataScope(scope, input, output, values, aggr);
};
var prototype = DataScope.prototype;
prototype.countsRef = function(scope, field, sort) {
var ds = this,
cache = ds.counts || (ds.counts = {}),
k = fieldKey(field), v, a, p;
if (k != null) {
scope = ds.scope;
v = cache[k];
}
if (!v) {
p = {
groupby: scope.fieldRef(field, 'key'),
pulse: ref(ds.output)
};
if (sort && sort.field) addSortField(scope, p, sort);
a = scope.add(Aggregate(p));
v = scope.add(Collect({pulse: ref(a)}));
v = {agg: a, ref: ref(v)};
if (k != null) cache[k] = v;
} else if (sort && sort.field) {
addSortField(scope, v.agg.params, sort);
}
return v.ref;
};
function fieldKey(field) {
return vegaUtil.isString(field) ? field : null;
}
function addSortField(scope, p, sort) {
var as = aggrField(sort.op, sort.field), s;
if (p.ops) {
for (var i=0, n=p.as.length; i<n; ++i) {
if (p.as[i] === as) return;
}
} else {
p.ops = ['count'];
p.fields = [null];
p.as = ['count'];
}
if (sort.op) {
p.ops.push((s=sort.op.signal) ? scope.signalRef(s) : sort.op);
p.fields.push(scope.fieldRef(sort.field));
p.as.push(as);
}
}
function cache(scope, ds, name, optype, field, counts, index) {
var cache = ds[name] || (ds[name] = {}),
sort = sortKey(counts),
k = fieldKey(field), v, op;
if (k != null) {
scope = ds.scope;
k = k + (sort ? '|' + sort : '');
v = cache[k];
}
if (!v) {
var params = counts
? {field: keyFieldRef, pulse: ds.countsRef(scope, field, counts)}
: {field: scope.fieldRef(field), pulse: ref(ds.output)};
if (sort) params.sort = scope.sortRef(counts);
op = scope.add(entry(optype, undefined, params));
if (index) ds.index[field] = op;
v = ref(op);
if (k != null) cache[k] = v;
}
return v;
}
prototype.tuplesRef = function() {
return ref(this.values);
};
prototype.extentRef = function(scope, field) {
return cache(scope, this, 'extent', 'extent', field, false);
};
prototype.domainRef = function(scope, field) {
return cache(scope, this, 'domain', 'values', field, false);
};
prototype.valuesRef = function(scope, field, sort) {
return cache(scope, this, 'vals', 'values', field, sort || true);
};
prototype.lookupRef = function(scope, field) {
return cache(scope, this, 'lookup', 'tupleindex', field, false);
};
prototype.indataRef = function(scope, field) {
return cache(scope, this, 'indata', 'tupleindex', field, true, true);
};
function parseFacet(spec, scope, group) {
var facet = spec.from.facet,
name = facet.name,
data = getDataRef(facet, scope),
subscope, source, values, op;
if (!facet.name) {
vegaUtil.error('Facet must have a name: ' + vegaUtil.stringValue(facet));
}
if (!facet.data) {
vegaUtil.error('Facet must reference a data set: ' + vegaUtil.stringValue(facet));
}
if (facet.field) {
op = scope.add(PreFacet({
field: scope.fieldRef(facet.field),
pulse: data
}));
} else if (facet.groupby) {
op = scope.add(Facet({
key: scope.keyRef(facet.groupby),
group: ref(scope.proxy(group.parent)),
pulse: data
}));
} else {
vegaUtil.error('Facet must specify groupby or field: ' + vegaUtil.stringValue(facet));
}
// initialize facet subscope
subscope = scope.fork();
source = subscope.add(Collect());
values = subscope.add(Sieve({pulse: ref(source)}));
subscope.addData(name, new DataScope(subscope, source, source, values));
subscope.addSignal('parent', null);
// parse faceted subflow
op.params.subflow = {
$subflow: parseSpec(spec, subscope).toRuntime()
};
}
function parseSubflow(spec, scope, input) {
var op = scope.add(PreFacet({pulse: input.pulse})),
subscope = scope.fork();
subscope.add(Sieve());
subscope.addSignal('parent', null);
// parse group mark subflow
op.params.subflow = {
$subflow: parseSpec(spec, subscope).toRuntime()
};
}
function parseTrigger(spec, scope, name) {
var remove = spec.remove,
insert = spec.insert,
toggle = spec.toggle,
modify = spec.modify,
values = spec.values,
op = scope.add(operator()),
update, expr;
update = 'if(' + spec.trigger + ',modify("'
+ name + '",'
+ [insert, remove, toggle, modify, values]
.map(function(_) { return _ == null ? 'null' : _; })
.join(',')
+ '),0)';
expr = parseExpression(update, scope);
op.update = expr.$expr;
op.params = expr.$params;
}
function parseMark(spec, scope) {
var role = getRole(spec),
group = spec.type === GroupMark,
facet = spec.from && spec.from.facet,
layout = spec.layout || role === ScopeRole || role === FrameRole,
nested = role === MarkRole || layout || facet,
overlap = spec.overlap,
ops, op, input, store, enc, bound, render, sieve, name,
joinRef, markRef, encodeRef, layoutRef, boundRef;
// resolve input data
input = parseData(spec.from, group, scope);
// data join to map tuples to visual items
op = scope.add(DataJoin({
key: input.key || (spec.key ? fieldRef(spec.key) : undefined),
pulse: input.pulse,
clean: !group
}));
joinRef = ref(op);
// collect visual items
op = store = scope.add(Collect({pulse: joinRef}));
// connect visual items to scenegraph
op = scope.add(Mark({
markdef: definition(spec),
interactive: interactive(spec.interactive, scope),
clip: clip(spec.clip, scope),
context: {$context: true},
groups: scope.lookup(),
parent: scope.signals.parent ? scope.signalRef('parent') : null,
index: scope.markpath(),
pulse: ref(op)
}));
markRef = ref(op);
// add visual encoders
op = enc = scope.add(Encode(encoders(
spec.encode, spec.type, role, spec.style, scope,
{mod: false, pulse: markRef}
)));
// monitor parent marks to propagate changes
op.params.parent = scope.encode();
// add post-encoding transforms, if defined
if (spec.transform) {
spec.transform.forEach(function(_) {
const tx = parseTransform(_, scope),
md = tx.metadata;
if (md.generates || md.changes) {
vegaUtil.error('Mark transforms should not generate new data.');
}
if (!md.nomod) enc.params.mod = true; // update encode mod handling
tx.params.pulse = ref(op);
scope.add(op = tx);
});
}
// if item sort specified, perform post-encoding
if (spec.sort) {
op = scope.add(SortItems({
sort: scope.compareRef(spec.sort),
pulse: ref(op)
}));
}
encodeRef = ref(op);
// add view layout operator if needed
if (facet || layout) {
layout = scope.add(ViewLayout({
layout: scope.objectProperty(spec.layout),
legends: scope.legends,
mark: markRef,
pulse: encodeRef
}));
layoutRef = ref(layout);
}
// compute bounding boxes
bound = scope.add(Bound({mark: markRef, pulse: layoutRef || encodeRef}));
boundRef = ref(bound);
// if group mark, recurse to parse nested content
if (group) {
// juggle layout & bounds to ensure they run *after* any faceting transforms
if (nested) { ops = scope.operators; ops.pop(); if (layout) ops.pop(); }
scope.pushState(encodeRef, layoutRef || boundRef, joinRef);
facet ? parseFacet(spec, scope, input) // explicit facet
: nested ? parseSubflow(spec, scope, input) // standard mark group
: parseSpec(spec, scope); // guide group, we can avoid nested scopes
scope.popState();
if (nested) { if (layout) ops.push(layout); ops.push(bound); }
}
// if requested, add overlap removal transform
if (overlap) {
boundRef = parseOverlap(overlap, boundRef, scope);
}
// render / sieve items
render = scope.add(Render({pulse: boundRef}));
sieve = scope.add(Sieve({pulse: ref(render)}, undefined, scope.parent()));
// if mark is named, make accessible as reactive geometry
// add trigger updates if defined
if (spec.name != null) {
name = spec.name;
scope.addData(name, new DataScope(scope, store, render, sieve));
if (spec.on) spec.on.forEach(function(on) {
if (on.insert || on.remove || on.toggle) {
vegaUtil.error('Marks only support modify triggers.');
}
parseTrigger(on, scope, name);
});
}
}
function parseOverlap(overlap, source, scope) {
var method = overlap.method,
bound = overlap.bound,
sep = overlap.separation, tol;
var params = {
separation: isSignal(sep) ? scope.signalRef(sep.signal) : sep,
method: isSignal(method) ? scope.signalRef(method.signal) : method,
pulse: source
};
if (overlap.order) {
params.sort = scope.compareRef({field: overlap.order});
}
if (bound) {
tol = bound.tolerance;
params.boundTolerance = isSignal(tol) ? scope.signalRef(tol.signal) : +tol;
params.boundScale = scope.scaleRef(bound.scale);
params.boundOrient = bound.orient;
}
return ref(scope.add(Overlap(params)));
}
function parseLegend(spec, scope) {
var config = scope.config.legend,
encode = spec.encode || {},
legendEncode = encode.legend || {},
name = legendEncode.name || undefined,
interactive = legendEncode.interactive,
style = legendEncode.style,
_ = lookup(spec, config),
entryEncode, entryLayout, params, children,
type, datum, dataRef, entryRef, group;
// resolve 'canonical' scale name
var scale = LegendScales.reduce(function(a, b) { return a || spec[b]; }, 0);
if (!scale) vegaUtil.error('Missing valid scale for legend.');
// resolve legend type (symbol, gradient, or discrete gradient)
type = legendType(spec, scope.scaleType(scale));
// single-element data source for legend group
datum = {
title: spec.title != null,
type: type,
vgrad: type !== 'symbol' && _.isVertical()
};
dataRef = ref(scope.add(Collect(null, [datum])));
// encoding properties for legend group
legendEncode = extendEncode(
buildLegendEncode(_, config), legendEncode, Skip
);
// encoding properties for legend entry sub-group
entryEncode = {enter: {x: {value: 0}, y: {value: 0}}};
// data source for legend values
entryRef = ref(scope.add(LegendEntries(params = {
type: type,
scale: scope.scaleRef(scale),
count: scope.objectProperty(_('tickCount')),
limit: scope.property(_('symbolLimit')),
values: scope.objectProperty(spec.values),
minstep: scope.property(spec.tickMinStep),
formatType: scope.property(spec.formatType),
formatSpecifier: scope.property(spec.format)
})));
// continuous gradient legend
if (type === Gradient) {
children = [
legendGradient(spec, scale, config, encode.gradient),
legendGradientLabels(spec, config, encode.labels, entryRef)
];
// adjust default tick count based on the gradient length
params.count = params.count || scope.signalRef(
`max(2,2*floor((${deref(_.gradientLength())})/100))`
);
}
// discrete gradient legend
else if (type === Discrete) {
children = [
legendGradientDiscrete(spec, scale, config, encode.gradient, entryRef),
legendGradientLabels(spec, config, encode.labels, entryRef)
];
}
// symbol legend
else {
// determine legend symbol group layout
entryLayout = legendSymbolLayout(spec, config);
children = [
legendSymbolGroups(spec, config, encode, entryRef, deref(entryLayout.columns))
];
// pass symbol size information to legend entry generator
params.size = sizeExpression(spec, scope, children[0].marks);
}
// generate legend marks
children = [
guideGroup(LegendEntryRole, null, null, dataRef, interactive,
entryEncode, children, entryLayout)
];
// include legend title if defined
if (datum.title) {
children.push(legendTitle(spec, config, encode.title, dataRef));
}
// build legend specification
group = guideGroup(LegendRole, style, name, dataRef, interactive, legendEncode, children);
if (spec.zindex) group.zindex = spec.zindex;
// parse legend specification
return parseMark(group, scope);
}
function legendType(spec, scaleType) {
var type = spec.type || Symbols;
if (!spec.type && scaleCount(spec) === 1 && (spec.fill || spec.stroke)) {
type = vegaScale.isContinuous(scaleType) ? Gradient
: vegaScale.isDiscretizing(scaleType) ? Discrete
: Symbols;
}
return type !== Gradient ? type
: vegaScale.isDiscretizing(scaleType) ? Discrete
: Gradient;
}
function scaleCount(spec) {
return LegendScales.reduce(function(count, type) {
return count + (spec[type] ? 1 : 0);
}, 0);
}
function buildLegendEncode(_, config) {
var encode = {enter: {}, update: {}};
addEncoders(encode, {
orient: _('orient'),
offset: _('offset'),
padding: _('padding'),
titlePadding: _('titlePadding'),
cornerRadius: _('cornerRadius'),
fill: _('fillColor'),
stroke: _('strokeColor'),
strokeWidth: config.strokeWidth,
strokeDash: config.strokeDash,
x: _('legendX'),
y: _('legendY'),
});
return encode;
}
function sizeExpression(spec, scope, marks) {
var size = deref(getChannel('size', spec, marks)),
strokeWidth = deref(getChannel('strokeWidth', spec, marks)),
fontSize = deref(getFontSize(marks[1].encode, scope, GuideLabelStyle));
return parseExpression(
`max(ceil(sqrt(${size})+${strokeWidth}),${fontSize})`,
scope
);
}
function getChannel(name, spec, marks) {
return spec[name]
? `scale("${spec[name]}",datum)`
: getEncoding(name, marks[0].encode);
}
function getFontSize(encode, scope, style) {
return getEncoding('fontSize', encode) || getStyle('fontSize', scope, style);
}
const angleExpr = `item.orient==="${Left}"?-90:item.orient==="${Right}"?90:0`;
function parseTitle(spec, scope) {
spec = vegaUtil.isString(spec) ? {text: spec} : spec;
var _ = lookup(spec, scope.config.title),
encode = spec.encode || {},
userEncode = encode.group || {},
name = userEncode.name || undefined,
interactive = userEncode.interactive,
style = userEncode.style,
children = [],
dataRef, group;
// single-element data source for group title
dataRef = ref(scope.add(Collect(null, [{}])));
// include title text
children.push(buildTitle(spec, _, titleEncode(spec), dataRef));
// include subtitle text
if (spec.subtitle) {
children.push(buildSubTitle(spec, _, encode.subtitle, dataRef));
}
// build title specification
group = guideGroup(TitleRole, style, name, dataRef, interactive,
groupEncode(_, userEncode), children);
if (spec.zindex) group.zindex = spec.zindex;
// parse title specification
return parseMark(group, scope);
}
// provide backwards-compatibility for title custom encode;
// the top-level encode block has been *deprecated*.
function titleEncode(spec) {
const encode = spec.encode;
return (encode && encode.title) || vegaUtil.extend({
name: spec.name,
interactive: spec.interactive,
style: spec.style
}, encode);
}
function groupEncode(_, userEncode) {
var encode = {enter: {}, update: {}};
addEncoders(encode, {
orient: _('orient'),
anchor: _('anchor'),
align: {signal: alignExpr},
angle: {signal: angleExpr},
limit: _('limit'),
frame: _('frame'),
offset: _('offset') || 0,
padding: _('subtitlePadding')
});
return extendEncode(encode, userEncode, Skip);
}
function buildTitle(spec, _, userEncode, dataRef) {
var zero = {value: 0},
text = spec.text,
encode = {
enter: {opacity: zero},
update: {opacity: {value: 1}},
exit: {opacity: zero}
};
addEncoders(encode, {
text: text,
align: {signal: 'item.mark.group.align'},
angle: {signal: 'item.mark.group.angle'},
limit: {signal: 'item.mark.group.limit'},
baseline: 'top',
dx: _('dx'),
dy: _('dy'),
fill: _('color'),
font: _('font'),
fontSize: _('fontSize'),
fontStyle: _('fontStyle'),
fontWeight: _('fontWeight'),
lineHeight: _('lineHeight')
}, { // update
align: _('align'),
angle: _('angle'),
baseline: _('baseline')
});
return guideMark(TextMark, TitleTextRole, GroupTitleStyle,
null, dataRef, encode, userEncode);
}
function buildSubTitle(spec, _, userEncode, dataRef) {
var zero = {value: 0},
text = spec.subtitle,
encode = {
enter: {opacity: zero},
update: {opacity: {value: 1}},
exit: {opacity: zero}
};
addEncoders(encode, {
text: text,
align: {signal: 'item.mark.group.align'},
angle: {signal: 'item.mark.group.angle'},
limit: {signal: 'item.mark.group.limit'},
baseline: 'top',
dx: _('dx'),
dy: _('dy'),
fill: _('subtitleColor'),
font: _('subtitleFont'),
fontSize: _('subtitleFontSize'),
fontStyle: _('subtitleFontStyle'),
fontWeight: _('subtitleFontWeight'),
lineHeight: _('subtitleLineHeight')
}, { // update
align: _('align'),
angle: _('angle'),
baseline: _('baseline')
});
return guideMark(TextMark, TitleSubtitleRole, GroupSubtitleStyle,
null, dataRef, encode, userEncode);
}
function parseData$1(data, scope) {
var transforms = [];
if (data.transform) {
data.transform.forEach(function(tx) {
transforms.push(parseTransform(tx, scope));
});
}
if (data.on) {
data.on.forEach(function(on) {
parseTrigger(on, scope, data.name);
});
}
scope.addDataPipeline(data.name, analyze(data, scope, transforms));
}
/**
* Analyze a data pipeline, add needed operators.
*/
function analyze(data, scope, ops) {
var output = [],
source = null,
modify = false,
generate = false,
upstream, i, n, t, m;
if (data.values) {
// hard-wired input data set
if (hasSignal(data.values) || hasSignal(data.format)) {
// if either values or format has signal, use dynamic loader
output.push(load(scope, data));
output.push(source = collect());
} else {
// otherwise, ingest upon dataflow init
output.push(source = collect({
$ingest: data.values,
$format: data.format
}));
}
} else if (data.url) {
// load data from external source
if (hasSignal(data.url) || hasSignal(data.format)) {
// if either url or format has signal, use dynamic loader
output.push(load(scope, data));
output.push(source = collect());
} else {
// otherwise, request load upon dataflow init
output.push(source = collect({
$request: data.url,
$format: data.format
}));
}
} else if (data.source) {
// derives from one or more other data sets
source = upstream = vegaUtil.array(data.source).map(function(d) {
return ref(scope.getData(d).output);
});
output.push(null); // populate later
}
// scan data transforms, add collectors as needed
for (i=0, n=ops.length; i<n; ++i) {
t = ops[i];
m = t.metadata;
if (!source && !m.source) {
output.push(source = collect());
}
output.push(t);
if (m.generates) generate = true;
if (m.modifies && !generate) modify = true;
if (m.source) source = t;
else if (m.changes) source = null;
}
if (upstream) {
n = upstream.length - 1;
output[0] = Relay({
derive: modify,
pulse: n ? upstream : upstream[0]
});
if (modify || n) {
// collect derived and multi-pulse tuples
output.splice(1, 0, collect());
}
}
if (!source) output.push(collect());
output.push(Sieve({}));
return output;
}
function collect(values) {
var s = Collect({}, values);
s.metadata = {source: true};
return s;
}
function load(scope, data) {
return Load({
url: data.url ? scope.property(data.url) : undefined,
async: data.async ? scope.property(data.async) : undefined,
values: data.values ? scope.property(data.values) : undefined,
format: scope.objectProperty(data.format)
});
}
function axisConfig(spec, scope) {
var config = scope.config,
orient = spec.orient,
xy = (orient === Top || orient === Bottom) ? config.axisX : config.axisY,
or = config['axis' + orient[0].toUpperCase() + orient.slice(1)],
band = scope.scaleType(spec.scale) === 'band' && config.axisBand;
return (xy || or || band)
? vegaUtil.extend({}, config.axis, xy, or, band)
: config.axis;
}
function axisDomain(spec, config, userEncode, dataRef) {
var _ = lookup(spec, config),
orient = spec.orient,
encode, enter, update, u, u2, v;
encode = {
enter: enter = {opacity: zero},
update: update = {opacity: one},
exit: {opacity: zero}
};
addEncoders(encode, {
stroke: _('domainColor'),
strokeDash: _('domainDash'),
strokeDashOffset: _('domainDashOffset'),
strokeWidth: _('domainWidth'),
strokeOpacity: _('domainOpacity')
});
if (orient === Top || orient === Bottom) {
u = 'x';
v = 'y';
} else {
u = 'y';
v = 'x';
}
u2 = u + '2';
enter[v] = zero;
update[u] = enter[u] = position(spec, 0);
update[u2] = enter[u2] = position(spec, 1);
return guideMark(RuleMark, AxisDomainRole, null, null, dataRef, encode, userEncode);
}
function position(spec, pos) {
return {scale: spec.scale, range: pos};
}
function axisGrid(spec, config, userEncode, dataRef, band) {
var _ = lookup(spec, config),
orient = spec.orient,
vscale = spec.gridScale,
sign = (orient === Left || orient === Top) ? 1 : -1,
offset = offsetValue(spec.offset, sign),
encode, enter, exit, update, tickPos, u, v, v2, s;
encode = {
enter: enter = {opacity: zero},
update: update = {opacity: one},
exit: exit = {opacity: zero}
};
addEncoders(encode, {
stroke: _('gridColor'),
strokeDash: _('gridDash'),
strokeDashOffset: _('gridDashOffset'),
strokeOpacity: _('gridOpacity'),
strokeWidth: _('gridWidth')
});
tickPos = {
scale: spec.scale,
field: Value,
band: band.band,
extra: band.extra,
offset: band.offset,
round: _('tickRound')
};
if (orient === Top || orient === Bottom) {
u = 'x';
v = 'y';
s = 'height';
} else {
u = 'y';
v = 'x';
s = 'width';
}
v2 = v + '2';
update[u] = enter[u] = exit[u] = tickPos;
if (vscale) {
update[v] = enter[v] = {scale: vscale, range: 0, mult: sign, offset: offset};
update[v2] = enter[v2] = {scale: vscale, range: 1, mult: sign, offset: offset};
} else {
update[v] = enter[v] = {value: 0, offset: offset};
update[v2] = enter[v2] = {signal: s, mult: sign, offset: offset};
}
return guideMark(RuleMark, AxisGridRole, null, Value, dataRef, encode, userEncode);
}
function offsetValue(offset, sign) {
if (sign === 1) ; else if (!vegaUtil.isObject(offset)) {
offset = sign * (offset || 0);
} else {
var entry = offset = vegaUtil.extend({}, offset);
while (entry.mult != null) {
if (!vegaUtil.isObject(entry.mult)) {
entry.mult *= sign;
return offset;
} else {
entry = entry.mult = vegaUtil.extend({}, entry.mult);
}
}
entry.mult = sign;
}
return offset;
}
function axisTicks(spec, config, userEncode, dataRef, size, band) {
var _ = lookup(spec, config),
orient = spec.orient,
sign = (orient === Left || orient === Top) ? -1 : 1,
encode, enter, exit, update, tickSize, tickPos;
encode = {
enter: enter = {opacity: zero},
update: update = {opacity: one},
exit: exit = {opacity: zero}
};
addEncoders(encode, {
stroke: _('tickColor'),
strokeDash: _('tickDash'),
strokeDashOffset: _('tickDashOffset'),
strokeOpacity: _('tickOpacity'),
strokeWidth: _('tickWidth')
});
tickSize = encoder(size);
tickSize.mult = sign;
tickPos = {
scale: spec.scale,
field: Value,
band: band.band,
extra: band.extra,
offset: band.offset,
round: _('tickRound')
};
if (orient === Top || orient === Bottom) {
update.y = enter.y = zero;
update.y2 = enter.y2 = tickSize;
update.x = enter.x = exit.x = tickPos;
} else {
update.x = enter.x = zero;
update.x2 = enter.x2 = tickSize;
update.y = enter.y = exit.y = tickPos;
}
return guideMark(RuleMark, AxisTickRole, null, Value, dataRef, encode, userEncode);
}
function flushExpr(scale, threshold, a, b, c) {
return {
signal: 'flush(range("' + scale + '"), '
+ 'scale("' + scale + '", datum.value), '
+ threshold + ',' + a + ',' + b + ',' + c + ')'
};
}
function axisLabels(spec, config, userEncode, dataRef, size, band) {
var _ = lookup(spec, config),
orient = spec.orient,
sign = (orient === Left || orient === Top) ? -1 : 1,
isXAxis = (orient === Top || orient === Bottom),
scale = spec.scale,
flush = deref(_('labelFlush')),
flushOffset = deref(_('labelFlushOffset')),
flushOn = flush === 0 || !!flush,
labelAlign = _('labelAlign'),
labelBaseline = _('labelBaseline'),
encode, enter, tickSize, tickPos, align, baseline, offset,
bound, overlap, separation;
tickSize = encoder(size);
tickSize.mult = sign;
tickSize.offset = encoder(_('labelPadding') || 0);
tickSize.offset.mult = sign;
tickPos = {
scale: scale,
field: Value,
band: 0.5,
offset: band.offset
};
if (isXAxis) {
align = labelAlign || (flushOn
? flushExpr(scale, flush, '"left"', '"right"', '"center"')
: 'center');
baseline = labelBaseline || (orient === Top ? 'bottom' : 'top');
offset = !labelAlign;
} else {
align = labelAlign || (orient === Right ? 'left' : 'right');
baseline = labelBaseline || (flushOn
? flushExpr(scale, flush, '"top"', '"bottom"', '"middle"')
: 'middle');
offset = !labelBaseline;
}
offset = offset && flushOn && flushOffset
? flushExpr(scale, flush, '-(' + flushOffset + ')', flushOffset, 0)
: null;
encode = {
enter: enter = {
opacity: zero,
x: isXAxis ? tickPos : tickSize,
y: isXAxis ? tickSize : tickPos
},
update: {
opacity: one,
text: {field: Label},
x: enter.x,
y: enter.y
},
exit: {
opacity: zero,
x: enter.x,
y: enter.y
}
};
addEncoders(encode, {
[isXAxis ? 'dx' : 'dy']: offset,
align: align,
baseline: baseline,
angle: _('labelAngle'),
fill: _('labelColor'),
fillOpacity: _('labelOpacity'),
font: _('labelFont'),
fontSize: _('labelFontSize'),
fontWeight: _('labelFontWeight'),
fontStyle: _('labelFontStyle'),
limit: _('labelLimit')
});
bound = _('labelBound');
overlap = _('labelOverlap');
separation = _('labelSeparation');
spec = guideMark(TextMark, AxisLabelRole, GuideLabelStyle, Value, dataRef, encode, userEncode);
// if overlap method or bound defined, request label overlap removal
if (overlap || bound) {
spec.overlap = {
separation: separation,
method: overlap,
order: 'datum.index',
bound: bound ? {scale: scale, orient: orient, tolerance: bound} : null
};
}
return spec;
}
function axisTitle(spec, config, userEncode, dataRef) {
var _ = lookup(spec, config),
orient = spec.orient,
sign = (orient === Left || orient === Top) ? -1 : 1,
horizontal = (orient === Top || orient === Bottom),
encode, enter, update, titlePos;
encode = {
enter: enter = {
opacity: zero,
anchor: encoder(_('titleAnchor')),
align: {signal: alignExpr}
},
update: update = vegaUtil.extend({}, enter, {
opacity: one,
text: encoder(spec.title)
}),
exit: {
opacity: zero
}
};
titlePos = {
signal: `lerp(range("${spec.scale}"), ${anchorExpr(0, 1, 0.5)})`
};
if (horizontal) {
update.x = titlePos;
enter.angle = {value: 0};
enter.baseline = {value: orient === Top ? 'bottom' : 'top'};
} else {
update.y = titlePos;
enter.angle = {value: sign * 90};
enter.baseline = {value: 'bottom'};
}
addEncoders(encode, {
angle: _('titleAngle'),
baseline: _('titleBaseline'),
fill: _('titleColor'),
fillOpacity: _('titleOpacity'),
font: _('titleFont'),
fontSize: _('titleFontSize'),
fontStyle: _('titleFontStyle'),
fontWeight: _('titleFontWeight'),
limit: _('titleLimit'),
lineHeight: _('titleLineHeight')
}, { // require update
align: _('titleAlign')
});
!addEncode(encode, 'x', _('titleX'), 'update')
&& !horizontal && !has('x', userEncode)
&& (encode.enter.auto = {value: true});
!addEncode(encode, 'y', _('titleY'), 'update')
&& horizontal && !has('y', userEncode)
&& (encode.enter.auto = {value: true});
return guideMark(TextMark, AxisTitleRole, GuideTitleStyle, null, dataRef, encode, userEncode);
}
function parseAxis(spec, scope) {
var config = axisConfig(spec, scope),
encode = spec.encode || {},
axisEncode = encode.axis || {},
name = axisEncode.name || undefined,
interactive = axisEncode.interactive,
style = axisEncode.style,
_ = lookup(spec, config),
band = tickBand(_),
datum, dataRef, ticksRef, size, group, children;
// single-element data source for axis group
datum = {
orient: spec.orient,
ticks: !!_('ticks'),
labels: !!_('labels'),
grid: !!_('grid'),
domain: !!_('domain'),
title: spec.title != null,
translate: _('translate')
};
dataRef = ref(scope.add(Collect({}, [datum])));
// encoding properties for axis group item
axisEncode = extendEncode({
update: {
offset: encoder(_('offset') || 0),
position: encoder(value(spec.position, 0)),
titlePadding: encoder(_('titlePadding')),
minExtent: encoder(_('minExtent')),
maxExtent: encoder(_('maxExtent')),
range: {signal: `abs(span(range("${spec.scale}")))`}
}
}, encode.axis, Skip);
// data source for axis ticks
ticksRef = ref(scope.add(AxisTicks({
scale: scope.scaleRef(spec.scale),
extra: scope.property(band.extra),
count: scope.objectProperty(spec.tickCount),
values: scope.objectProperty(spec.values),
minstep: scope.property(spec.tickMinStep),
formatType: scope.property(spec.formatType),
formatSpecifier: scope.property(spec.format)
})));
// generate axis marks
children = [];
// include axis gridlines if requested
if (datum.grid) {
children.push(axisGrid(spec, config, encode.grid, ticksRef, band));
}
// include axis ticks if requested
if (datum.ticks) {
size = _('tickSize');
children.push(axisTicks(spec, config, encode.ticks, ticksRef, size, band));
}
// include axis labels if requested
if (datum.labels) {
size = datum.ticks ? size : 0;
children.push(axisLabels(spec, config, encode.labels, ticksRef, size, band));
}
// include axis domain path if requested
if (datum.domain) {
children.push(axisDomain(spec, config, encode.domain, dataRef));
}
// include axis title if defined
if (datum.title) {
children.push(axisTitle(spec, config, encode.title, dataRef));
}
// build axis specification
group = guideGroup(AxisRole, style, name, dataRef, interactive, axisEncode, children);
if (spec.zindex) group.zindex = spec.zindex;
// parse axis specification
return parseMark(group, scope);
}
function parseSpec(spec, scope, preprocessed) {
var signals = vegaUtil.array(spec.signals),
scales = vegaUtil.array(spec.scales);
// parse signal definitions, if not already preprocessed
if (!preprocessed) signals.forEach(_ => parseSignal(_, scope));
// parse cartographic projection definitions
vegaUtil.array(spec.projections).forEach(_ => parseProjection(_, scope));
// initialize scale references
scales.forEach(_ => initScale(_, scope));
// parse data sources
vegaUtil.array(spec.data).forEach(_ => parseData$1(_, scope));
// parse scale definitions
scales.forEach(_ => parseScale(_, scope));
// parse signal updates
(preprocessed || signals).forEach(_ => parseSignalUpdates(_, scope));
// parse axis definitions
vegaUtil.array(spec.axes).forEach(_ => parseAxis(_, scope));
// parse mark definitions
vegaUtil.array(spec.marks).forEach(_ => parseMark(_, scope));
// parse legend definitions
vegaUtil.array(spec.legends).forEach(_ => parseLegend(_, scope));
// parse title, if defined
if (spec.title) parseTitle(spec.title, scope);
// parse collected lambda (anonymous) expressions
scope.parseLambdas();
return scope;
}
var defined = vegaUtil.toSet(['width', 'height', 'padding', 'autosize']);
function parseView(spec, scope) {
var config = scope.config,
op, input, encode, parent, root, signals;
scope.background = spec.background || config.background;
scope.eventConfig = config.events;
root = ref(scope.root = scope.add(operator()));
scope.addSignal('width', spec.width || 0);
scope.addSignal('height', spec.height || 0);
scope.addSignal('padding', parsePadding(spec.padding, config));
scope.addSignal('autosize', parseAutosize(spec.autosize, config));
scope.legends = scope.objectProperty(config.legend && config.legend.layout);
// parse signal definitions, including config entries
signals = addSignals(scope, spec.signals, config.signals);
// Store root group item
input = scope.add(Collect());
// Encode root group item
encode = extendEncode({
enter: { x: {value: 0}, y: {value: 0} },
update: { width: {signal: 'width'}, height: {signal: 'height'} }
}, spec.encode);
encode = scope.add(Encode(
encoders(encode, GroupMark, FrameRole, spec.style, scope, {pulse: ref(input)}))
);
// Perform view layout
parent = scope.add(ViewLayout({
layout: scope.objectProperty(spec.layout),
legends: scope.legends,
autosize: scope.signalRef('autosize'),
mark: root,
pulse: ref(encode)
}));
scope.operators.pop();
// Parse remainder of specification
scope.pushState(ref(encode), ref(parent), null);
parseSpec(spec, scope, signals);
scope.operators.push(parent);
// Bound / render / sieve root item
op = scope.add(Bound({mark: root, pulse: ref(parent)}));
op = scope.add(Render({pulse: ref(op)}));
op = scope.add(Sieve({pulse: ref(op)}));
// Track metadata for root item
scope.addData('root', new DataScope(scope, input, input, op));
return scope;
}
function addSignals(scope, signals, config) {
// signals defined in the spec take priority
vegaUtil.array(signals).forEach(_ => {
if (!defined[_.name]) parseSignal(_, scope);
});
if (!config) return signals;
const out = vegaUtil.array(signals).slice();
// add config signals if not already defined
vegaUtil.array(config).forEach(_ => {
if (!scope.hasOwnSignal(_.name)) {
parseSignal(_, scope);
out.push(_);
}
});
return out;
}
function Scope$1(config) {
this.config = config;
this.bindings = [];
this.field = {};
this.signals = {};
this.lambdas = {};
this.scales = {};
this.events = {};
this.data = {};
this.streams = [];
this.updates = [];
this.operators = [];
this.background = null;
this.eventConfig = null;
this._id = 0;
this._subid = 0;
this._nextsub = [0];
this._parent = [];
this._encode = [];
this._lookup = [];
this._markpath = [];
}
function Subscope(scope) {
this.config = scope.config;
this.legends = scope.legends;
this.field = Object.create(scope.field);
this.signals = Object.create(scope.signals);
this.lambdas = Object.create(scope.lambdas);
this.scales = Object.create(scope.scales);
this.events = Object.create(scope.events);
this.data = Object.create(scope.data);
this.streams = [];
this.updates = [];
this.operators = [];
this._id = 0;
this._subid = ++scope._nextsub[0];
this._nextsub = scope._nextsub;
this._parent = scope._parent.slice();
this._encode = scope._encode.slice();
this._lookup = scope._lookup.slice();
this._markpath = scope._markpath;
}
var prototype$1 = Scope$1.prototype = Subscope.prototype;
// ----
prototype$1.fork = function() {
return new Subscope(this);
};
prototype$1.isSubscope = function() {
return this._subid > 0;
};
prototype$1.toRuntime = function() {
this.finish();
return {
background: this.background,
operators: this.operators,
streams: this.streams,
updates: this.updates,
bindings: this.bindings,
eventConfig: this.eventConfig
};
};
prototype$1.id = function() {
return (this._subid ? this._subid + ':' : 0) + this._id++;
};
prototype$1.add = function(op) {
this.operators.push(op);
op.id = this.id();
// if pre-registration references exist, resolve them now
if (op.refs) {
op.refs.forEach(function(ref) { ref.$ref = op.id; });
op.refs = null;
}
return op;
};
prototype$1.proxy = function(op) {
var vref = op instanceof Entry ? ref(op) : op;
return this.add(Proxy({value: vref}));
};
prototype$1.addStream = function(stream) {
this.streams.push(stream);
stream.id = this.id();
return stream;
};
prototype$1.addUpdate = function(update) {
this.updates.push(update);
return update;
};
// Apply metadata
prototype$1.finish = function() {
var name, ds;
// annotate root
if (this.root) this.root.root = true;
// annotate signals
for (name in this.signals) {
this.signals[name].signal = name;
}
// annotate scales
for (name in this.scales) {
this.scales[name].scale = name;
}
// annotate data sets
function annotate(op, name, type) {
var data, list;
if (op) {
data = op.data || (op.data = {});
list = data[name] || (data[name] = []);
list.push(type);
}
}
for (name in this.data) {
ds = this.data[name];
annotate(ds.input, name, 'input');
annotate(ds.output, name, 'output');
annotate(ds.values, name, 'values');
for (var field in ds.index) {
annotate(ds.index[field], name, 'index:' + field);
}
}
return this;
};
// ----
prototype$1.pushState = function(encode, parent, lookup) {
this._encode.push(ref(this.add(Sieve({pulse: encode}))));
this._parent.push(parent);
this._lookup.push(lookup ? ref(this.proxy(lookup)) : null);
this._markpath.push(-1);
};
prototype$1.popState = function() {
this._encode.pop();
this._parent.pop();
this._lookup.pop();
this._markpath.pop();
};
prototype$1.parent = function() {
return vegaUtil.peek(this._parent);
};
prototype$1.encode = function() {
return vegaUtil.peek(this._encode);
};
prototype$1.lookup = function() {
return vegaUtil.peek(this._lookup);
};
prototype$1.markpath = function() {
var p = this._markpath;
return ++p[p.length-1];
};
// ----
prototype$1.fieldRef = function(field, name) {
if (vegaUtil.isString(field)) return fieldRef(field, name);
if (!field.signal) {
vegaUtil.error('Unsupported field reference: ' + vegaUtil.stringValue(field));
}
var s = field.signal,
f = this.field[s],
params;
if (!f) {
params = {name: this.signalRef(s)};
if (name) params.as = name;
this.field[s] = f = ref(this.add(Field(params)));
}
return f;
};
prototype$1.compareRef = function(cmp) {
function check(_) {
if (isSignal(_)) {
signal = true;
return scope.signalRef(_.signal);
} else if (isExpr(_)) {
signal = true;
return scope.exprRef(_.expr);
} else {
return _;
}
}
var scope = this,
signal = false,
fields = vegaUtil.array(cmp.field).map(check),
orders = vegaUtil.array(cmp.order).map(check);
return signal
? ref(this.add(Compare({fields: fields, orders: orders})))
: compareRef(fields, orders);
};
prototype$1.keyRef = function(fields, flat) {
function check(_) {
if (isSignal(_)) {
signal = true;
return ref(sig[_.signal]);
} else {
return _;
}
}
var sig = this.signals,
signal = false;
fields = vegaUtil.array(fields).map(check);
return signal
? ref(this.add(Key({fields: fields, flat: flat})))
: keyRef(fields, flat);
};
prototype$1.sortRef = function(sort) {
if (!sort) return sort;
// including id ensures stable sorting
var a = aggrField(sort.op, sort.field),
o = sort.order || Ascending;
return o.signal
? ref(this.add(Compare({
fields: a,
orders: this.signalRef(o.signal)
})))
: compareRef(a, o);
};
// ----
prototype$1.event = function(source, type) {
var key = source + ':' + type;
if (!this.events[key]) {
var id = this.id();
this.streams.push({
id: id,
source: source,
type: type
});
this.events[key] = id;
}
return this.events[key];
};
// ----
prototype$1.hasOwnSignal = function(name) {
return vegaUtil.hasOwnProperty(this.signals, name);
};
prototype$1.addSignal = function(name, value) {
if (this.hasOwnSignal(name)) {
vegaUtil.error('Duplicate signal name: ' + vegaUtil.stringValue(name));
}
var op = value instanceof Entry ? value : this.add(operator(value));
return this.signals[name] = op;
};
prototype$1.getSignal = function(name) {
if (!this.signals[name]) {
vegaUtil.error('Unrecognized signal name: ' + vegaUtil.stringValue(name));
}
return this.signals[name];
};
prototype$1.signalRef = function(s) {
if (this.signals[s]) {
return ref(this.signals[s]);
} else if (!vegaUtil.hasOwnProperty(this.lambdas, s)) {
this.lambdas[s] = this.add(operator(null));
}
return ref(this.lambdas[s]);
};
prototype$1.parseLambdas = function() {
var code = Object.keys(this.lambdas);
for (var i=0, n=code.length; i<n; ++i) {
var s = code[i],
e = parseExpression(s, this),
op = this.lambdas[s];
op.params = e.$params;
op.update = e.$expr;
}
};
prototype$1.property = function(spec) {
return spec && spec.signal ? this.signalRef(spec.signal) : spec;
};
prototype$1.objectProperty = function(spec) {
return (!spec || !vegaUtil.isObject(spec)) ? spec
: this.signalRef(spec.signal || propertyLambda(spec));
};
function propertyLambda(spec) {
return (vegaUtil.isArray(spec) ? arrayLambda : objectLambda)(spec);
}
function arrayLambda(array) {
var code = '[',
i = 0,
n = array.length,
value;
for (; i<n; ++i) {
value = array[i];
code += (i > 0 ? ',' : '')
+ (vegaUtil.isObject(value)
? (value.signal || propertyLambda(value))
: vegaUtil.stringValue(value));
}
return code + ']';
}
function objectLambda(obj) {
var code = '{',
i = 0,
key, value;
for (key in obj) {
value = obj[key];
code += (++i > 1 ? ',' : '')
+ vegaUtil.stringValue(key) + ':'
+ (vegaUtil.isObject(value)
? (value.signal || propertyLambda(value))
: vegaUtil.stringValue(value));
}
return code + '}';
}
prototype$1.exprRef = function(code, name) {
var params = {expr: parseExpression(code, this)};
if (name) params.expr.$name = name;
return ref(this.add(Expression(params)));
};
prototype$1.addBinding = function(name, bind) {
if (!this.bindings) {
vegaUtil.error('Nested signals do not support binding: ' + vegaUtil.stringValue(name));
}
this.bindings.push(vegaUtil.extend({signal: name}, bind));
};
// ----
prototype$1.addScaleProj = function(name, transform) {
if (vegaUtil.hasOwnProperty(this.scales, name)) {
vegaUtil.error('Duplicate scale or projection name: ' + vegaUtil.stringValue(name));
}
this.scales[name] = this.add(transform);
};
prototype$1.addScale = function(name, params) {
this.addScaleProj(name, Scale(params));
};
prototype$1.addProjection = function(name, params) {
this.addScaleProj(name, Projection(params));
};
prototype$1.getScale = function(name) {
if (!this.scales[name]) {
vegaUtil.error('Unrecognized scale name: ' + vegaUtil.stringValue(name));
}
return this.scales[name];
};
prototype$1.projectionRef =
prototype$1.scaleRef = function(name) {
return ref(this.getScale(name));
};
prototype$1.projectionType =
prototype$1.scaleType = function(name) {
return this.getScale(name).params.type;
};
// ----
prototype$1.addData = function(name, dataScope) {
if (vegaUtil.hasOwnProperty(this.data, name)) {
vegaUtil.error('Duplicate data set name: ' + vegaUtil.stringValue(name));
}
return (this.data[name] = dataScope);
};
prototype$1.getData = function(name) {
if (!this.data[name]) {
vegaUtil.error('Undefined data set name: ' + vegaUtil.stringValue(name));
}
return this.data[name];
};
prototype$1.addDataPipeline = function(name, entries) {
if (vegaUtil.hasOwnProperty(this.data, name)) {
vegaUtil.error('Duplicate data set name: ' + vegaUtil.stringValue(name));
}
return this.addData(name, DataScope.fromEntries(this, entries));
};
var defaultFont = 'sans-serif',
defaultSymbolSize = 30,
defaultStrokeWidth = 2,
defaultColor = '#4c78a8',
black = '#000',
gray = '#888',
lightGray = '#ddd';
/**
* Standard configuration defaults for Vega specification parsing.
* Users can provide their own (sub-)set of these default values
* by passing in a config object to the top-level parse method.
*/
function defaults() {
return {
// default padding around visualization
padding: 0,
// default for automatic sizing; options: 'none', 'pad', 'fit'
// or provide an object (e.g., {'type': 'pad', 'resize': true})
autosize: 'pad',
// default view background color
// covers the entire view component
background: null,
// default event handling configuration
// preventDefault for view-sourced event types except 'wheel'
events: {
defaults: {allow: ['wheel']}
},
// defaults for top-level group marks
// accepts mark properties (fill, stroke, etc)
// covers the data rectangle within group width/height
group: null,
// defaults for basic mark types
// each subset accepts mark properties (fill, stroke, etc)
mark: null,
arc: { fill: defaultColor },
area: { fill: defaultColor },
image: null,
line: {
stroke: defaultColor,
strokeWidth: defaultStrokeWidth
},
path: { stroke: defaultColor },
rect: { fill: defaultColor },
rule: { stroke: black },
shape: { stroke: defaultColor },
symbol: {
fill: defaultColor,
size: 64
},
text: {
fill: black,
font: defaultFont,
fontSize: 11
},
// style definitions
style: {
// axis & legend labels
'guide-label': {
fill: black,
font: defaultFont,
fontSize: 10
},
// axis & legend titles
'guide-title': {
fill: black,
font: defaultFont,
fontSize: 11,
fontWeight: 'bold'
},
// headers, including chart title
'group-title': {
fill: black,
font: defaultFont,
fontSize: 13,
fontWeight: 'bold'
},
// chart subtitle
'group-subtitle': {
fill: black,
font: defaultFont,
fontSize: 12
},
// defaults for styled point marks in Vega-Lite
point: {
size: defaultSymbolSize,
strokeWidth: defaultStrokeWidth,
shape: 'circle'
},
circle: {
size: defaultSymbolSize,
strokeWidth: defaultStrokeWidth
},
square: {
size: defaultSymbolSize,
strokeWidth: defaultStrokeWidth,
shape: 'square'
},
// defaults for styled group marks in Vega-Lite
cell: {
fill: 'transparent',
stroke: lightGray
}
},
// defaults for title
title: {
orient: 'top',
anchor: 'middle',
offset: 4,
subtitlePadding: 3
},
// defaults for axes
axis: {
minExtent: 0,
maxExtent: 200,
bandPosition: 0.5,
domain: true,
domainWidth: 1,
domainColor: gray,
grid: false,
gridWidth: 1,
gridColor: lightGray,
labels: true,
labelAngle: 0,
labelLimit: 180,
labelPadding: 2,
ticks: true,
tickColor: gray,
tickOffset: 0,
tickRound: true,
tickSize: 5,
tickWidth: 1,
titlePadding: 4
},
// correction for centering bias
axisBand: {
tickOffset: -0.5
},
// defaults for cartographic projection
projection: {
type: 'mercator'
},
// defaults for legends
legend: {
orient: 'right',
padding: 0,
gridAlign: 'each',
columnPadding: 10,
rowPadding: 2,
symbolDirection: 'vertical',
gradientDirection: 'vertical',
gradientLength: 200,
gradientThickness: 16,
gradientStrokeColor: lightGray,
gradientStrokeWidth: 0,
gradientLabelOffset: 2,
labelAlign: 'left',
labelBaseline: 'middle',
labelLimit: 160,
labelOffset: 4,
labelOverlap: true,
symbolLimit: 30,
symbolType: 'circle',
symbolSize: 100,
symbolOffset: 0,
symbolStrokeWidth: 1.5,
symbolBaseFillColor: 'transparent',
symbolBaseStrokeColor: gray,
titleLimit: 180,
titleOrient: 'top',
titlePadding: 5,
layout: {
offset: 18,
direction: 'horizontal',
left: { direction: 'vertical' },
right: { direction: 'vertical' }
}
},
// defaults for scale ranges
range: {
category: {
scheme: 'tableau10'
},
ordinal: {
scheme: 'blues'
},
heatmap: {
scheme: 'yellowgreenblue'
},
ramp: {
scheme: 'blues'
},
diverging: {
scheme: 'blueorange',
extent: [1, 0]
},
symbol: [
'circle',
'square',
'triangle-up',
'cross',
'diamond',
'triangle-right',
'triangle-down',
'triangle-left'
]
}
};
}
function parse(spec, config) {
if (!vegaUtil.isObject(spec)) {
vegaUtil.error('Input Vega specification must be an object.');
}
config = vegaUtil.mergeConfig(defaults(), config, spec.config);
return parseView(spec, new Scope$1(config)).toRuntime();
}
exports.AxisDomainRole = AxisDomainRole;
exports.AxisGridRole = AxisGridRole;
exports.AxisLabelRole = AxisLabelRole;
exports.AxisRole = AxisRole;
exports.AxisTickRole = AxisTickRole;
exports.AxisTitleRole = AxisTitleRole;
exports.DataScope = DataScope;
exports.FrameRole = FrameRole;
exports.LegendEntryRole = LegendEntryRole;
exports.LegendLabelRole = LegendLabelRole;
exports.LegendRole = LegendRole;
exports.LegendSymbolRole = LegendSymbolRole;
exports.LegendTitleRole = LegendTitleRole;
exports.MarkRole = MarkRole;
exports.Scope = Scope$1;
exports.ScopeRole = ScopeRole;
exports.config = defaults;
exports.parse = parse;
exports.signal = parseSignal;
exports.signalUpdates = parseSignalUpdates;
exports.stream = parseStream;
Object.defineProperty(exports, '__esModule', { value: true });
})));