vega-functions 5.18.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,790 +0,0 @@
1
- import { truthy, hasOwnProperty, error, stringValue, isString, isFunction, extend, isArray, isObject, field, ascending, isRegExp, peek, identity, array as array$1, zoomSymlog, zoomPow, zoomLog, zoomLinear, panSymlog, panPow, panLog, panLinear, clampRange, utcquarter, quarter, truncate, inrange, span, pad, lerp, flush, toString, toNumber, toBoolean, isNumber, isDate, isBoolean, extent, toDate } from 'vega-util';
2
- import { Literal, codegenExpression, constants, functions, parseExpression, CallExpression } from 'vega-expression';
3
- import { isRegisteredScale, bandSpace, scale as scale$1, scaleFraction } from 'vega-scale';
4
- import { geoBounds as geoBounds$1, geoCentroid as geoCentroid$1, geoArea as geoArea$1 } from 'd3-geo';
5
- import { rgb, hsl, hcl, lab } from 'd3-color';
6
- import { isTuple } from 'vega-dataflow';
7
- import { Gradient, pathRender, pathParse, Bounds, intersect as intersect$1 } from 'vega-scenegraph';
8
- import { selectionVisitor, selectionTest, selectionIdTest, selectionResolve, selectionTuples } from 'vega-selections';
9
- import { sampleUniform, sampleLogNormal, sampleNormal, quantileUniform, quantileLogNormal, quantileNormal, densityUniform, densityLogNormal, densityNormal, cumulativeUniform, cumulativeLogNormal, cumulativeNormal, random } from 'vega-statistics';
10
- import { utcdayofyear, dayofyear, utcweek, week, timeUnitSpecifier, timeSequence, timeOffset, utcSequence, utcOffset } from 'vega-time';
11
- import { range as range$1 } from 'd3-array';
12
-
13
- function data(name) {
14
- const data = this.context.data[name];
15
- return data ? data.values.value : [];
16
- }
17
- function indata(name, field, value) {
18
- const index = this.context.data[name]['index:' + field],
19
- entry = index ? index.value.get(value) : undefined;
20
- return entry ? entry.count : entry;
21
- }
22
- function setdata(name, tuples) {
23
- const df = this.context.dataflow,
24
- data = this.context.data[name],
25
- input = data.input;
26
- df.pulse(input, df.changeset().remove(truthy).insert(tuples));
27
- return 1;
28
- }
29
-
30
- function encode (item, name, retval) {
31
- if (item) {
32
- const df = this.context.dataflow,
33
- target = item.mark.source;
34
- df.pulse(target, df.changeset().encode(item, name));
35
- }
36
- return retval !== undefined ? retval : item;
37
- }
38
-
39
- const wrap = method => function (value, spec) {
40
- const locale = this.context.dataflow.locale();
41
- return value === null ? 'null' : locale[method](spec)(value);
42
- };
43
- const format = wrap('format');
44
- const timeFormat = wrap('timeFormat');
45
- const utcFormat = wrap('utcFormat');
46
- const timeParse = wrap('timeParse');
47
- const utcParse = wrap('utcParse');
48
- const dateObj = new Date(2000, 0, 1);
49
- function time(month, day, specifier) {
50
- if (!Number.isInteger(month) || !Number.isInteger(day)) return '';
51
- dateObj.setYear(2000);
52
- dateObj.setMonth(month);
53
- dateObj.setDate(day);
54
- return timeFormat.call(this, dateObj, specifier);
55
- }
56
- function monthFormat(month) {
57
- return time.call(this, month, 1, '%B');
58
- }
59
- function monthAbbrevFormat(month) {
60
- return time.call(this, month, 1, '%b');
61
- }
62
- function dayFormat(day) {
63
- return time.call(this, 0, 2 + day, '%A');
64
- }
65
- function dayAbbrevFormat(day) {
66
- return time.call(this, 0, 2 + day, '%a');
67
- }
68
-
69
- const DataPrefix = ':';
70
- const IndexPrefix = '@';
71
- const ScalePrefix = '%';
72
- const SignalPrefix = '$';
73
-
74
- function dataVisitor(name, args, scope, params) {
75
- if (args[0].type !== Literal) {
76
- error('First argument to data functions must be a string literal.');
77
- }
78
- const data = args[0].value,
79
- dataName = DataPrefix + data;
80
- if (!hasOwnProperty(dataName, params)) {
81
- try {
82
- params[dataName] = scope.getData(data).tuplesRef();
83
- } catch (err) {
84
- // if data set does not exist, there's nothing to track
85
- }
86
- }
87
- }
88
- function indataVisitor(name, args, scope, params) {
89
- if (args[0].type !== Literal) error('First argument to indata must be a string literal.');
90
- if (args[1].type !== Literal) error('Second argument to indata must be a string literal.');
91
- const data = args[0].value,
92
- field = args[1].value,
93
- indexName = IndexPrefix + field;
94
- if (!hasOwnProperty(indexName, params)) {
95
- params[indexName] = scope.getData(data).indataRef(scope, field);
96
- }
97
- }
98
- function scaleVisitor(name, args, scope, params) {
99
- if (args[0].type === Literal) {
100
- // add scale dependency
101
- addScaleDependency(scope, params, args[0].value);
102
- } else {
103
- // indirect scale lookup; add all scales as parameters
104
- for (name in scope.scales) {
105
- addScaleDependency(scope, params, name);
106
- }
107
- }
108
- }
109
- function addScaleDependency(scope, params, name) {
110
- const scaleName = ScalePrefix + name;
111
- if (!hasOwnProperty(params, scaleName)) {
112
- try {
113
- params[scaleName] = scope.scaleRef(name);
114
- } catch (err) {
115
- // TODO: error handling? warning?
116
- }
117
- }
118
- }
119
-
120
- /**
121
- * nameOrFunction must be a string or function that was registered.
122
- * Return undefined if scale is not recognized.
123
- */
124
- function getScale(nameOrFunction, ctx) {
125
- if (isString(nameOrFunction)) {
126
- const maybeScale = ctx.scales[nameOrFunction];
127
- return maybeScale && isRegisteredScale(maybeScale.value) ? maybeScale.value : undefined;
128
- } else if (isFunction(nameOrFunction)) {
129
- return isRegisteredScale(nameOrFunction) ? nameOrFunction : undefined;
130
- }
131
- return undefined;
132
- }
133
- function internalScaleFunctions(codegen, fnctx, visitors) {
134
- // add helper method to the 'this' expression function context
135
- fnctx.__bandwidth = s => s && s.bandwidth ? s.bandwidth() : 0;
136
-
137
- // register AST visitors for internal scale functions
138
- visitors._bandwidth = scaleVisitor;
139
- visitors._range = scaleVisitor;
140
- visitors._scale = scaleVisitor;
141
-
142
- // resolve scale reference directly to the signal hash argument
143
- const ref = arg => '_[' + (arg.type === Literal ? stringValue(ScalePrefix + arg.value) : stringValue(ScalePrefix) + '+' + codegen(arg)) + ']';
144
-
145
- // define and return internal scale function code generators
146
- // these internal functions are called by mark encoders
147
- return {
148
- _bandwidth: args => `this.__bandwidth(${ref(args[0])})`,
149
- _range: args => `${ref(args[0])}.range()`,
150
- _scale: args => `${ref(args[0])}(${codegen(args[1])})`
151
- };
152
- }
153
-
154
- function geoMethod(methodName, globalMethod) {
155
- return function (projection, geojson, group) {
156
- if (projection) {
157
- // projection defined, use it
158
- const p = getScale(projection, (group || this).context);
159
- return p && p.path[methodName](geojson);
160
- } else {
161
- // projection undefined, use global method
162
- return globalMethod(geojson);
163
- }
164
- };
165
- }
166
- const geoArea = geoMethod('area', geoArea$1);
167
- const geoBounds = geoMethod('bounds', geoBounds$1);
168
- const geoCentroid = geoMethod('centroid', geoCentroid$1);
169
- function geoScale(projection, group) {
170
- const p = getScale(projection, (group || this).context);
171
- return p && p.scale();
172
- }
173
-
174
- function inScope (item) {
175
- const group = this.context.group;
176
- let value = false;
177
- if (group) while (item) {
178
- if (item === group) {
179
- value = true;
180
- break;
181
- }
182
- item = item.mark.group;
183
- }
184
- return value;
185
- }
186
-
187
- function log(df, method, args) {
188
- try {
189
- df[method].apply(df, ['EXPRESSION'].concat([].slice.call(args)));
190
- } catch (err) {
191
- df.warn(err);
192
- }
193
- return args[args.length - 1];
194
- }
195
- function warn() {
196
- return log(this.context.dataflow, 'warn', arguments);
197
- }
198
- function info() {
199
- return log(this.context.dataflow, 'info', arguments);
200
- }
201
- function debug() {
202
- return log(this.context.dataflow, 'debug', arguments);
203
- }
204
-
205
- // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
206
- function channel_luminance_value(channelValue) {
207
- const val = channelValue / 255;
208
- if (val <= 0.03928) {
209
- return val / 12.92;
210
- }
211
- return Math.pow((val + 0.055) / 1.055, 2.4);
212
- }
213
- function luminance(color) {
214
- const c = rgb(color),
215
- r = channel_luminance_value(c.r),
216
- g = channel_luminance_value(c.g),
217
- b = channel_luminance_value(c.b);
218
- return 0.2126 * r + 0.7152 * g + 0.0722 * b;
219
- }
220
-
221
- // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
222
- function contrast(color1, color2) {
223
- const lum1 = luminance(color1),
224
- lum2 = luminance(color2),
225
- lumL = Math.max(lum1, lum2),
226
- lumD = Math.min(lum1, lum2);
227
- return (lumL + 0.05) / (lumD + 0.05);
228
- }
229
-
230
- function merge () {
231
- const args = [].slice.call(arguments);
232
- args.unshift({});
233
- return extend(...args);
234
- }
235
-
236
- function equal(a, b) {
237
- return a === b || a !== a && b !== b ? true : isArray(a) ? isArray(b) && a.length === b.length ? equalArray(a, b) : false : isObject(a) && isObject(b) ? equalObject(a, b) : false;
238
- }
239
- function equalArray(a, b) {
240
- for (let i = 0, n = a.length; i < n; ++i) {
241
- if (!equal(a[i], b[i])) return false;
242
- }
243
- return true;
244
- }
245
- function equalObject(a, b) {
246
- for (const key in a) {
247
- if (!equal(a[key], b[key])) return false;
248
- }
249
- return true;
250
- }
251
- function removePredicate(props) {
252
- return _ => equalObject(props, _);
253
- }
254
- function modify (name, insert, remove, toggle, modify, values) {
255
- const df = this.context.dataflow,
256
- data = this.context.data[name],
257
- input = data.input,
258
- stamp = df.stamp();
259
- let changes = data.changes,
260
- predicate,
261
- key;
262
- if (df._trigger === false || !(input.value.length || insert || toggle)) {
263
- // nothing to do!
264
- return 0;
265
- }
266
- if (!changes || changes.stamp < stamp) {
267
- data.changes = changes = df.changeset();
268
- changes.stamp = stamp;
269
- df.runAfter(() => {
270
- data.modified = true;
271
- df.pulse(input, changes).run();
272
- }, true, 1);
273
- }
274
- if (remove) {
275
- predicate = remove === true ? truthy : isArray(remove) || isTuple(remove) ? remove : removePredicate(remove);
276
- changes.remove(predicate);
277
- }
278
- if (insert) {
279
- changes.insert(insert);
280
- }
281
- if (toggle) {
282
- predicate = removePredicate(toggle);
283
- if (input.value.some(predicate)) {
284
- changes.remove(predicate);
285
- } else {
286
- changes.insert(toggle);
287
- }
288
- }
289
- if (modify) {
290
- for (key in values) {
291
- changes.modify(modify, key, values[key]);
292
- }
293
- }
294
- return 1;
295
- }
296
-
297
- function pinchDistance(event) {
298
- const t = event.touches,
299
- dx = t[0].clientX - t[1].clientX,
300
- dy = t[0].clientY - t[1].clientY;
301
- return Math.hypot(dx, dy);
302
- }
303
- function pinchAngle(event) {
304
- const t = event.touches;
305
- return Math.atan2(t[0].clientY - t[1].clientY, t[0].clientX - t[1].clientX);
306
- }
307
-
308
- // memoize accessor functions
309
- const accessors = {};
310
- function pluck (data, name) {
311
- const accessor = accessors[name] || (accessors[name] = field(name));
312
- return isArray(data) ? data.map(accessor) : accessor(data);
313
- }
314
-
315
- function array(seq) {
316
- return isArray(seq) || ArrayBuffer.isView(seq) ? seq : null;
317
- }
318
- function sequence(seq) {
319
- return array(seq) || (isString(seq) ? seq : null);
320
- }
321
- function join(seq) {
322
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
323
- args[_key - 1] = arguments[_key];
324
- }
325
- return array(seq).join(...args);
326
- }
327
- function indexof(seq) {
328
- for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
329
- args[_key2 - 1] = arguments[_key2];
330
- }
331
- return sequence(seq).indexOf(...args);
332
- }
333
- function lastindexof(seq) {
334
- for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
335
- args[_key3 - 1] = arguments[_key3];
336
- }
337
- return sequence(seq).lastIndexOf(...args);
338
- }
339
- function slice(seq) {
340
- for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
341
- args[_key4 - 1] = arguments[_key4];
342
- }
343
- return sequence(seq).slice(...args);
344
- }
345
- function replace(str, pattern, repl) {
346
- if (isFunction(repl)) error('Function argument passed to replace.');
347
- if (!isString(pattern) && !isRegExp(pattern)) error('Please pass a string or RegExp argument to replace.');
348
- return String(str).replace(pattern, repl);
349
- }
350
- function reverse(seq) {
351
- return array(seq).slice().reverse();
352
- }
353
- function sort(seq) {
354
- return array(seq).slice().sort(ascending);
355
- }
356
-
357
- function bandspace(count, paddingInner, paddingOuter) {
358
- return bandSpace(count || 0, paddingInner || 0, paddingOuter || 0);
359
- }
360
- function bandwidth(name, group) {
361
- const s = getScale(name, (group || this).context);
362
- return s && s.bandwidth ? s.bandwidth() : 0;
363
- }
364
- function copy(name, group) {
365
- const s = getScale(name, (group || this).context);
366
- return s ? s.copy() : undefined;
367
- }
368
- function domain(name, group) {
369
- const s = getScale(name, (group || this).context);
370
- return s ? s.domain() : [];
371
- }
372
- function invert(name, range, group) {
373
- const s = getScale(name, (group || this).context);
374
- return !s ? undefined : isArray(range) ? (s.invertRange || s.invert)(range) : (s.invert || s.invertExtent)(range);
375
- }
376
- function range(name, group) {
377
- const s = getScale(name, (group || this).context);
378
- return s && s.range ? s.range() : [];
379
- }
380
- function scale(name, value, group) {
381
- const s = getScale(name, (group || this).context);
382
- return s ? s(value) : undefined;
383
- }
384
-
385
- function scaleGradient (scale, p0, p1, count, group) {
386
- scale = getScale(scale, (group || this).context);
387
- const gradient = Gradient(p0, p1);
388
- let stops = scale.domain(),
389
- min = stops[0],
390
- max = peek(stops),
391
- fraction = identity;
392
- if (!(max - min)) {
393
- // expand scale if domain has zero span, fix #1479
394
- scale = (scale.interpolator ? scale$1('sequential')().interpolator(scale.interpolator()) : scale$1('linear')().interpolate(scale.interpolate()).range(scale.range())).domain([min = 0, max = 1]);
395
- } else {
396
- fraction = scaleFraction(scale, min, max);
397
- }
398
- if (scale.ticks) {
399
- stops = scale.ticks(+count || 15);
400
- if (min !== stops[0]) stops.unshift(min);
401
- if (max !== peek(stops)) stops.push(max);
402
- }
403
- stops.forEach(_ => gradient.stop(fraction(_), scale(_)));
404
- return gradient;
405
- }
406
-
407
- function geoShape(projection, geojson, group) {
408
- const p = getScale(projection, (group || this).context);
409
- return function (context) {
410
- return p ? p.path.context(context)(geojson) : '';
411
- };
412
- }
413
- function pathShape(path) {
414
- let p = null;
415
- return function (context) {
416
- return context ? pathRender(context, p = p || pathParse(path)) : path;
417
- };
418
- }
419
-
420
- const datum = d => d.data;
421
- function treeNodes(name, context) {
422
- const tree = data.call(context, name);
423
- return tree.root && tree.root.lookup || {};
424
- }
425
- function treePath(name, source, target) {
426
- const nodes = treeNodes(name, this),
427
- s = nodes[source],
428
- t = nodes[target];
429
- return s && t ? s.path(t).map(datum) : undefined;
430
- }
431
- function treeAncestors(name, node) {
432
- const n = treeNodes(name, this)[node];
433
- return n ? n.ancestors().map(datum) : undefined;
434
- }
435
-
436
- const _window = () => typeof window !== 'undefined' && window || null;
437
- function screen() {
438
- const w = _window();
439
- return w ? w.screen : {};
440
- }
441
- function windowSize() {
442
- const w = _window();
443
- return w ? [w.innerWidth, w.innerHeight] : [undefined, undefined];
444
- }
445
- function containerSize() {
446
- const view = this.context.dataflow,
447
- el = view.container && view.container();
448
- return el ? [el.clientWidth, el.clientHeight] : [undefined, undefined];
449
- }
450
-
451
- function intersect (b, opt, group) {
452
- if (!b) return [];
453
- const [u, v] = b,
454
- box = new Bounds().set(u[0], u[1], v[0], v[1]),
455
- scene = group || this.context.dataflow.scenegraph().root;
456
- return intersect$1(scene, box, filter(opt));
457
- }
458
- function filter(opt) {
459
- let p = null;
460
- if (opt) {
461
- const types = array$1(opt.marktype),
462
- names = array$1(opt.markname);
463
- p = _ => (!types.length || types.some(t => _.marktype === t)) && (!names.length || names.some(s => _.name === s));
464
- }
465
- return p;
466
- }
467
-
468
- /**
469
- * Appends a new point to the lasso
470
- *
471
- * @param {*} lasso the lasso in pixel space
472
- * @param {*} x the x coordinate in pixel space
473
- * @param {*} y the y coordinate in pixel space
474
- * @param {*} minDist the minimum distance, in pixels, that thenew point needs to be apart from the last point
475
- * @returns a new array containing the lasso with the new point
476
- */
477
- function lassoAppend(lasso, x, y) {
478
- let minDist = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 5;
479
- lasso = array$1(lasso);
480
- const last = lasso[lasso.length - 1];
481
-
482
- // Add point to lasso if its the first point or distance to last point exceed minDist
483
- return last === undefined || Math.hypot(last[0] - x, last[1] - y) > minDist ? [...lasso, [x, y]] : lasso;
484
- }
485
-
486
- /**
487
- * Generates a svg path command which draws a lasso
488
- *
489
- * @param {*} lasso the lasso in pixel space in the form [[x,y], [x,y], ...]
490
- * @returns the svg path command that draws the lasso
491
- */
492
- function lassoPath(lasso) {
493
- return array$1(lasso).reduce((svg, _ref, i) => {
494
- let [x, y] = _ref;
495
- return svg += i == 0 ? `M ${x},${y} ` : i === lasso.length - 1 ? ' Z' : `L ${x},${y} `;
496
- }, '');
497
- }
498
-
499
- /**
500
- * Inverts the lasso from pixel space to an array of vega scenegraph tuples
501
- *
502
- * @param {*} data the dataset
503
- * @param {*} pixelLasso the lasso in pixel space, [[x,y], [x,y], ...]
504
- * @param {*} unit the unit where the lasso is defined
505
- *
506
- * @returns an array of vega scenegraph tuples
507
- */
508
- function intersectLasso(markname, pixelLasso, unit) {
509
- const {
510
- x,
511
- y,
512
- mark
513
- } = unit;
514
- const bb = new Bounds().set(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
515
-
516
- // Get bounding box around lasso
517
- for (const [px, py] of pixelLasso) {
518
- if (px < bb.x1) bb.x1 = px;
519
- if (px > bb.x2) bb.x2 = px;
520
- if (py < bb.y1) bb.y1 = py;
521
- if (py > bb.y2) bb.y2 = py;
522
- }
523
-
524
- // Translate bb against unit coordinates
525
- bb.translate(x, y);
526
- const intersection = intersect([[bb.x1, bb.y1], [bb.x2, bb.y2]], markname, mark);
527
-
528
- // Check every point against the lasso
529
- return intersection.filter(tuple => pointInPolygon(tuple.x, tuple.y, pixelLasso));
530
- }
531
-
532
- /**
533
- * Performs a test if a point is inside a polygon based on the idea from
534
- * https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
535
- *
536
- * This method will not need the same start/end point since it wraps around the edges of the array
537
- *
538
- * @param {*} test a point to test against
539
- * @param {*} polygon a polygon in the form [[x,y], [x,y], ...]
540
- * @returns true if the point lies inside the polygon, false otherwise
541
- */
542
- function pointInPolygon(testx, testy, polygon) {
543
- let intersections = 0;
544
- for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
545
- const [prevX, prevY] = polygon[j];
546
- const [x, y] = polygon[i];
547
-
548
- // count intersections
549
- if (y > testy != prevY > testy && testx < (prevX - x) * (testy - y) / (prevY - y) + x) {
550
- intersections++;
551
- }
552
- }
553
-
554
- // point is in polygon if intersection count is odd
555
- return intersections & 1;
556
- }
557
-
558
- // Expression function context object
559
- const functionContext = {
560
- random() {
561
- return random();
562
- },
563
- // override default
564
- cumulativeNormal,
565
- cumulativeLogNormal,
566
- cumulativeUniform,
567
- densityNormal,
568
- densityLogNormal,
569
- densityUniform,
570
- quantileNormal,
571
- quantileLogNormal,
572
- quantileUniform,
573
- sampleNormal,
574
- sampleLogNormal,
575
- sampleUniform,
576
- isArray,
577
- isBoolean,
578
- isDate,
579
- isDefined(_) {
580
- return _ !== undefined;
581
- },
582
- isNumber,
583
- isObject,
584
- isRegExp,
585
- isString,
586
- isTuple,
587
- isValid(_) {
588
- return _ != null && _ === _;
589
- },
590
- toBoolean,
591
- toDate(_) {
592
- return toDate(_);
593
- },
594
- // suppress extra arguments
595
- toNumber,
596
- toString,
597
- indexof,
598
- join,
599
- lastindexof,
600
- replace,
601
- reverse,
602
- sort,
603
- slice,
604
- flush,
605
- lerp,
606
- merge,
607
- pad,
608
- peek,
609
- pluck,
610
- span,
611
- inrange,
612
- truncate,
613
- rgb,
614
- lab,
615
- hcl,
616
- hsl,
617
- luminance,
618
- contrast,
619
- sequence: range$1,
620
- format,
621
- utcFormat,
622
- utcParse,
623
- utcOffset,
624
- utcSequence,
625
- timeFormat,
626
- timeParse,
627
- timeOffset,
628
- timeSequence,
629
- timeUnitSpecifier,
630
- monthFormat,
631
- monthAbbrevFormat,
632
- dayFormat,
633
- dayAbbrevFormat,
634
- quarter,
635
- utcquarter,
636
- week,
637
- utcweek,
638
- dayofyear,
639
- utcdayofyear,
640
- warn,
641
- info,
642
- debug,
643
- extent(_) {
644
- return extent(_);
645
- },
646
- // suppress extra arguments
647
- inScope,
648
- intersect,
649
- clampRange,
650
- pinchDistance,
651
- pinchAngle,
652
- screen,
653
- containerSize,
654
- windowSize,
655
- bandspace,
656
- setdata,
657
- pathShape,
658
- panLinear,
659
- panLog,
660
- panPow,
661
- panSymlog,
662
- zoomLinear,
663
- zoomLog,
664
- zoomPow,
665
- zoomSymlog,
666
- encode,
667
- modify,
668
- lassoAppend,
669
- lassoPath,
670
- intersectLasso
671
- };
672
- const eventFunctions = ['view', 'item', 'group', 'xy', 'x', 'y'],
673
- // event functions
674
- eventPrefix = 'event.vega.',
675
- // event function prefix
676
- thisPrefix = 'this.',
677
- // function context prefix
678
- astVisitors = {}; // AST visitors for dependency analysis
679
-
680
- // export code generator parameters
681
- const codegenParams = {
682
- forbidden: ['_'],
683
- allowed: ['datum', 'event', 'item'],
684
- fieldvar: 'datum',
685
- globalvar: id => `_[${stringValue(SignalPrefix + id)}]`,
686
- functions: buildFunctions,
687
- constants: constants,
688
- visitors: astVisitors
689
- };
690
-
691
- // export code generator
692
- const codeGenerator = codegenExpression(codegenParams);
693
-
694
- // Build expression function registry
695
- function buildFunctions(codegen) {
696
- const fn = functions(codegen);
697
- eventFunctions.forEach(name => fn[name] = eventPrefix + name);
698
- for (const name in functionContext) {
699
- fn[name] = thisPrefix + name;
700
- }
701
- extend(fn, internalScaleFunctions(codegen, functionContext, astVisitors));
702
- return fn;
703
- }
704
-
705
- // Register an expression function
706
- function expressionFunction(name, fn, visitor) {
707
- if (arguments.length === 1) {
708
- return functionContext[name];
709
- }
710
-
711
- // register with the functionContext
712
- functionContext[name] = fn;
713
-
714
- // if there is an astVisitor register that, too
715
- if (visitor) astVisitors[name] = visitor;
716
-
717
- // if the code generator has already been initialized,
718
- // we need to also register the function with it
719
- if (codeGenerator) codeGenerator.functions[name] = thisPrefix + name;
720
- return this;
721
- }
722
-
723
- // register expression functions with ast visitors
724
- expressionFunction('bandwidth', bandwidth, scaleVisitor);
725
- expressionFunction('copy', copy, scaleVisitor);
726
- expressionFunction('domain', domain, scaleVisitor);
727
- expressionFunction('range', range, scaleVisitor);
728
- expressionFunction('invert', invert, scaleVisitor);
729
- expressionFunction('scale', scale, scaleVisitor);
730
- expressionFunction('gradient', scaleGradient, scaleVisitor);
731
- expressionFunction('geoArea', geoArea, scaleVisitor);
732
- expressionFunction('geoBounds', geoBounds, scaleVisitor);
733
- expressionFunction('geoCentroid', geoCentroid, scaleVisitor);
734
- expressionFunction('geoShape', geoShape, scaleVisitor);
735
- expressionFunction('geoScale', geoScale, scaleVisitor);
736
- expressionFunction('indata', indata, indataVisitor);
737
- expressionFunction('data', data, dataVisitor);
738
- expressionFunction('treePath', treePath, dataVisitor);
739
- expressionFunction('treeAncestors', treeAncestors, dataVisitor);
740
-
741
- // register Vega-Lite selection functions
742
- expressionFunction('vlSelectionTest', selectionTest, selectionVisitor);
743
- expressionFunction('vlSelectionIdTest', selectionIdTest, selectionVisitor);
744
- expressionFunction('vlSelectionResolve', selectionResolve, selectionVisitor);
745
- expressionFunction('vlSelectionTuples', selectionTuples);
746
-
747
- function parser (expr, scope) {
748
- const params = {};
749
-
750
- // parse the expression to an abstract syntax tree (ast)
751
- let ast;
752
- try {
753
- expr = isString(expr) ? expr : stringValue(expr) + '';
754
- ast = parseExpression(expr);
755
- } catch (err) {
756
- error('Expression parse error: ' + expr);
757
- }
758
-
759
- // analyze ast function calls for dependencies
760
- ast.visit(node => {
761
- if (node.type !== CallExpression) return;
762
- const name = node.callee.name,
763
- visit = codegenParams.visitors[name];
764
- if (visit) visit(name, node.arguments, scope, params);
765
- });
766
-
767
- // perform code generation
768
- const gen = codeGenerator(ast);
769
-
770
- // collect signal dependencies
771
- gen.globals.forEach(name => {
772
- const signalName = SignalPrefix + name;
773
- if (!hasOwnProperty(params, signalName) && scope.getSignal(name)) {
774
- params[signalName] = scope.signalRef(name);
775
- }
776
- });
777
-
778
- // return generated expression code and dependencies
779
- return {
780
- $expr: extend({
781
- code: gen.code
782
- }, scope.options.ast ? {
783
- ast
784
- } : null),
785
- $fields: gen.fields,
786
- $params: params
787
- };
788
- }
789
-
790
- export { DataPrefix, IndexPrefix, ScalePrefix, SignalPrefix, bandspace, bandwidth, codeGenerator, codegenParams, containerSize, contrast, copy, data, dataVisitor, dayAbbrevFormat, dayFormat, debug, domain, encode, expressionFunction, format, functionContext, geoArea, geoBounds, geoCentroid, geoScale, geoShape, inScope, indata, indataVisitor, indexof, info, invert, join, lastindexof, luminance, merge, modify, monthAbbrevFormat, monthFormat, parser as parseExpression, pathShape, pinchAngle, pinchDistance, pluck, range, replace, reverse, scale, scaleGradient, scaleVisitor, screen, setdata, slice, sort, timeFormat, timeParse, treeAncestors, treePath, utcFormat, utcParse, warn, windowSize };