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