vega-parser
Version:
Parse Vega specifications to runtime dataflows.
83 lines (68 loc) • 2.28 kB
JavaScript
import parseStream from './stream.js';
import {Scope, View} from '../util.js';
import {parseSelector} from 'vega-event-selector';
import {parseExpression} from 'vega-functions';
import {array, error, extend, isString, stringValue} from 'vega-util';
// bypass expression parser for internal operator references
const OP_VALUE_EXPR = {
code: '_.$value',
ast: {type: 'Identifier', value: 'value'}
};
export default function(spec, scope, target) {
const encode = spec.encode,
entry = {target: target};
let events = spec.events,
update = spec.update,
sources = [];
if (!events) {
error('Signal update missing events specification.');
}
// interpret as an event selector string
if (isString(events)) {
events = parseSelector(events, scope.isSubscope() ? Scope : View);
}
// separate event streams from signal updates
events = 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) error('Signal encode and update are mutually exclusive.');
update = 'encode(item(),' + stringValue(encode) + ')';
}
// resolve update value
entry.update = isString(update) ? parseExpression(update, scope)
: update.expr != null ? parseExpression(update.expr, scope)
: update.value != null ? update.value
: update.signal != null ? {
$expr: OP_VALUE_EXPR,
$params: {$value: scope.signalRef(update.signal)}
}
: error('Invalid signal update specification.');
if (spec.force) {
entry.options = {force: true};
}
sources.forEach(source =>
scope.addUpdate(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)
+ ']'
};
}