(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 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 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 }); })));