diff --git a/build/embed.d.ts b/build/embed.d.ts
new file mode 100644
index 00000000..4a87888c
--- /dev/null
+++ b/build/embed.d.ts
@@ -0,0 +1,45 @@
+import * as vegaImport from 'vega-lib';
+import * as VegaLite from 'vega-lite';
+import { Config as VgConfig, Loader, Spec as VgSpec, View } from 'vega-lib';
+import { Config as VlConfig } from 'vega-lite/build/src/config';
+import { TopLevelExtendedSpec as VlSpec } from 'vega-lite/build/src/spec';
+export declare const vega: typeof vegaImport;
+export declare const vl: typeof VegaLite;
+export declare type Mode = 'vega' | 'vega-lite';
+export interface EmbedOptions {
+ actions?: boolean | {
+ export?: boolean;
+ source?: boolean;
+ editor?: boolean;
+ };
+ mode?: Mode;
+ logLevel?: number;
+ loader?: Loader;
+ renderer?: 'canvas' | 'svg';
+ onBeforeParse?: (spec: VisualizationSpec) => VisualizationSpec;
+ width?: number;
+ height?: number;
+ padding?: number | {
+ left?: number;
+ right?: number;
+ top?: number;
+ bottom?: number;
+ };
+ config?: string | VlConfig | VgConfig;
+ sourceHeader?: string;
+ sourceFooter?: string;
+ editorUrl?: string;
+}
+export declare type VisualizationSpec = VlSpec | VgSpec;
+/**
+ * Embed a Vega visualization component in a web page. This function returns a promise.
+ *
+ * @param el DOM element in which to place component (DOM node or CSS selector).
+ * @param spec String : A URL string from which to load the Vega specification.
+ * Object : The Vega/Vega-Lite specification as a parsed JSON object.
+ * @param opt A JavaScript object containing options for embedding.
+ */
+export default function embed(el: HTMLBaseElement | string, spec: string | VisualizationSpec, opt: EmbedOptions): Promise<{} | {
+ view: View;
+ spec: VisualizationSpec;
+}>;
diff --git a/build/embed.js b/build/embed.js
new file mode 100644
index 00000000..8bff52e7
--- /dev/null
+++ b/build/embed.js
@@ -0,0 +1,161 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var versionCompare = require("compare-versions");
+var d3 = require("d3-selection");
+var vegaImport = require("vega-lib");
+var VegaLite = require("vega-lite");
+var vega_schema_url_parser_1 = require("vega-schema-url-parser");
+var post_1 = require("./post");
+exports.vega = vegaImport;
+exports.vl = VegaLite;
+var NAMES = {
+ 'vega': 'Vega',
+ 'vega-lite': 'Vega-Lite',
+};
+var VERSION = {
+ 'vega': exports.vega.version,
+ 'vega-lite': exports.vl ? exports.vl.version : 'not available',
+};
+var PREPROCESSOR = {
+ 'vega': function (vgjson, _) { return vgjson; },
+ 'vega-lite': function (vljson, config) { return exports.vl.compile(vljson, config).spec; },
+};
+/**
+ * Embed a Vega visualization component in a web page. This function returns a promise.
+ *
+ * @param el DOM element in which to place component (DOM node or CSS selector).
+ * @param spec String : A URL string from which to load the Vega specification.
+ * Object : The Vega/Vega-Lite specification as a parsed JSON object.
+ * @param opt A JavaScript object containing options for embedding.
+ */
+function embed(el, spec, opt) {
+ try {
+ opt = opt || {};
+ var actions = opt.actions !== undefined ? opt.actions : true;
+ var loader = opt.loader || exports.vega.loader();
+ var renderer = opt.renderer || 'canvas';
+ var logLevel = opt.logLevel || exports.vega.Warn;
+ // Load the visualization specification.
+ if (exports.vega.isString(spec)) {
+ return loader.load(spec).then(function (data) { return embed(el, JSON.parse(data), opt); }).catch(Promise.reject);
+ }
+ // Load Vega theme/configuration.
+ var config = opt.config;
+ if (exports.vega.isString(config)) {
+ return loader.load(config).then(function (data) {
+ opt.config = JSON.parse(data);
+ return embed(el, spec, opt);
+ }).catch(Promise.reject);
+ }
+ // Decide mode
+ var parsed = void 0;
+ var mode_1;
+ if (spec.$schema) {
+ parsed = vega_schema_url_parser_1.default(spec.$schema);
+ if (opt.mode && opt.mode !== parsed.library) {
+ console.warn("The given visualization spec is written in " + NAMES[parsed.library] + ", but mode argument sets " + NAMES[opt.mode] + ".");
+ }
+ mode_1 = parsed.library;
+ if (versionCompare(parsed.version, VERSION[mode_1]) > 0) {
+ console.warn("The input spec uses " + mode_1 + " " + parsed.version + ", but the current version of " + NAMES[mode_1] + " is " + VERSION[mode_1] + ".");
+ }
+ }
+ else {
+ mode_1 = opt.mode || 'vega';
+ }
+ var vgSpec = PREPROCESSOR[mode_1](spec, config);
+ if (mode_1 === 'vega-lite') {
+ if (vgSpec.$schema) {
+ parsed = vega_schema_url_parser_1.default(vgSpec.$schema);
+ if (versionCompare(parsed.version, VERSION.vega) > 0) {
+ console.warn("The compiled spec uses Vega " + parsed.version + ", but current version is " + VERSION.vega + ".");
+ }
+ }
+ }
+ // ensure container div has class 'vega-embed'
+ var div = d3.select(el) // d3.select supports elements and strings
+ .classed('vega-embed', true)
+ .html(''); // clear container
+ if (opt.onBeforeParse) {
+ // Allow Vega spec to be modified before being used
+ vgSpec = opt.onBeforeParse(vgSpec);
+ }
+ var runtime = exports.vega.parse(vgSpec, opt.config); // may throw an Error if parsing fails
+ var view_1 = new exports.vega.View(runtime, { loader: loader, logLevel: logLevel, renderer: renderer })
+ .initialize(el);
+ // Vega-Lite does not need hover so we can improve perf by not activating it
+ if (mode_1 !== 'vega-lite') {
+ view_1.hover();
+ }
+ if (opt) {
+ if (opt.width) {
+ view_1.width(opt.width);
+ }
+ if (opt.height) {
+ view_1.height(opt.height);
+ }
+ if (opt.padding) {
+ view_1.padding(opt.padding);
+ }
+ }
+ view_1.run();
+ if (actions !== false) {
+ // add child div to house action links
+ var ctrl = div.append('div')
+ .attr('class', 'vega-actions');
+ // add 'Export' action
+ if (actions === true || actions.export !== false) {
+ var ext_1 = renderer === 'canvas' ? 'png' : 'svg';
+ ctrl.append('a')
+ .text("Export as " + ext_1.toUpperCase())
+ .attr('href', '#')
+ .attr('target', '_blank')
+ .attr('download', "visualization." + ext_1)
+ .on('mousedown', function () {
+ var _this = this;
+ view_1.toImageURL(ext_1).then(function (url) {
+ _this.href = url;
+ }).catch(function (error) { throw error; });
+ d3.event.preventDefault();
+ });
+ }
+ // add 'View Source' action
+ if (actions === true || actions.source !== false) {
+ ctrl.append('a')
+ .text('View Source')
+ .attr('href', '#')
+ .on('click', function () {
+ viewSource(JSON.stringify(spec, null, 2), opt.sourceHeader || '', opt.sourceFooter || '');
+ d3.event.preventDefault();
+ });
+ }
+ // add 'Open in Vega Editor' action
+ if (actions === true || actions.editor !== false) {
+ var editorUrl_1 = opt.editorUrl || 'https://vega.github.io/editor/';
+ ctrl.append('a')
+ .text('Open in Vega Editor')
+ .attr('href', '#')
+ .on('click', function () {
+ post_1.post(window, editorUrl_1, {
+ mode: mode_1,
+ spec: JSON.stringify(spec, null, 2),
+ });
+ d3.event.preventDefault();
+ });
+ }
+ }
+ return Promise.resolve({ view: view_1, spec: spec });
+ }
+ catch (err) {
+ return Promise.reject(err);
+ }
+}
+exports.default = embed;
+function viewSource(source, sourceHeader, sourceFooter) {
+ var header = "
" + sourceHeader + "";
+ var footer = "
" + sourceFooter + "";
+ var win = window.open('');
+ win.document.write(header + source + footer);
+ win.document.title = 'Vega JSON Source';
+}
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW1iZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZW1iZWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxpREFBbUQ7QUFDbkQsaUNBQW1DO0FBQ25DLHFDQUF1QztBQUN2QyxvQ0FBc0M7QUFDdEMsaUVBQWtEO0FBS2xELCtCQUE4QjtBQUVqQixRQUFBLElBQUksR0FBRyxVQUFVLENBQUM7QUFDbEIsUUFBQSxFQUFFLEdBQUcsUUFBUSxDQUFDO0FBb0IzQixJQUFNLEtBQUssR0FBRztJQUNaLE1BQU0sRUFBTyxNQUFNO0lBQ25CLFdBQVcsRUFBRSxXQUFXO0NBQ3pCLENBQUM7QUFFRixJQUFNLE9BQU8sR0FBRztJQUNkLE1BQU0sRUFBTyxZQUFJLENBQUMsT0FBTztJQUN6QixXQUFXLEVBQUUsVUFBRSxDQUFDLENBQUMsQ0FBQyxVQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxlQUFlO0NBQy9DLENBQUM7QUFFRixJQUFNLFlBQVksR0FBRztJQUNuQixNQUFNLEVBQU8sVUFBQyxNQUFNLEVBQUUsQ0FBQyxJQUFLLE9BQUEsTUFBTSxFQUFOLENBQU07SUFDbEMsV0FBVyxFQUFFLFVBQUMsTUFBTSxFQUFFLE1BQU0sSUFBSyxPQUFBLFVBQUUsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFBL0IsQ0FBK0I7Q0FDakUsQ0FBQztBQUlGOzs7Ozs7O0dBT0c7QUFDSCxlQUE4QixFQUE0QixFQUFFLElBQWdDLEVBQUUsR0FBaUI7SUFDN0csSUFBSSxDQUFDO1FBQ0gsR0FBRyxHQUFHLEdBQUcsSUFBSSxFQUFFLENBQUM7UUFDaEIsSUFBTSxPQUFPLEdBQUksR0FBRyxDQUFDLE9BQU8sS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztRQUVoRSxJQUFNLE1BQU0sR0FBVyxHQUFHLENBQUMsTUFBTSxJQUFJLFlBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNuRCxJQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQztRQUMxQyxJQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsUUFBUSxJQUFJLFlBQUksQ0FBQyxJQUFJLENBQUM7UUFFM0Msd0NBQXdDO1FBQ3hDLEVBQUUsQ0FBQyxDQUFDLFlBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3hCLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FDM0IsVUFBQSxJQUFJLElBQUksT0FBQSxLQUFLLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsR0FBRyxDQUFDLEVBQWhDLENBQWdDLENBQ3pDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxQixDQUFDO1FBRUQsaUNBQWlDO1FBQ2pDLElBQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUM7UUFDMUIsRUFBRSxDQUFDLENBQUMsWUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDMUIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQUEsSUFBSTtnQkFDbEMsR0FBRyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUM5QixNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDOUIsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzQixDQUFDO1FBRUQsY0FBYztRQUNkLElBQUksTUFBTSxTQUFvQyxDQUFDO1FBQy9DLElBQUksTUFBVSxDQUFDO1FBRWYsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDakIsTUFBTSxHQUFHLGdDQUFZLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ3BDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztnQkFDNUMsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBOEMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsaUNBQTRCLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLE1BQUcsQ0FBQyxDQUFDO1lBQ2xJLENBQUM7WUFFRCxNQUFJLEdBQUcsTUFBTSxDQUFDLE9BQWUsQ0FBQztZQUU5QixFQUFFLENBQUMsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsTUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN0RCxPQUFPLENBQUMsSUFBSSxDQUFDLHlCQUF1QixNQUFJLFNBQUksTUFBTSxDQUFDLE9BQU8scUNBQWdDLEtBQUssQ0FBQyxNQUFJLENBQUMsWUFBTyxPQUFPLENBQUMsTUFBSSxDQUFDLE1BQUcsQ0FBQyxDQUFDO1lBQ2hJLENBQUM7UUFDSCxDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7WUFDTixNQUFJLEdBQUcsR0FBRyxDQUFDLElBQUksSUFBSSxNQUFNLENBQUM7UUFDNUIsQ0FBQztRQUVELElBQUksTUFBTSxHQUFXLFlBQVksQ0FBQyxNQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFFdEQsRUFBRSxDQUFDLENBQUMsTUFBSSxLQUFLLFdBQVcsQ0FBQyxDQUFDLENBQUM7WUFDekIsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7Z0JBQ25CLE1BQU0sR0FBRyxnQ0FBWSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztnQkFFdEMsRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQ3JELE9BQU8sQ0FBQyxJQUFJLENBQUMsaUNBQStCLE1BQU0sQ0FBQyxPQUFPLGlDQUE0QixPQUFPLENBQUMsSUFBSSxNQUFHLENBQUMsQ0FBQztnQkFDekcsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsOENBQThDO1FBQzlDLElBQU0sR0FBRyxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBUyxDQUFDLENBQUUsMENBQTBDO2FBQ3pFLE9BQU8sQ0FBQyxZQUFZLEVBQUUsSUFBSSxDQUFDO2FBQzNCLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLGtCQUFrQjtRQUUvQixFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztZQUN0QixtREFBbUQ7WUFDbkQsTUFBTSxHQUFHLEdBQUcsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDckMsQ0FBQztRQUVELElBQU0sT0FBTyxHQUFHLFlBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFFLHNDQUFzQztRQUV2RixJQUFNLE1BQUksR0FBRyxJQUFJLFlBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUMsTUFBTSxRQUFBLEVBQUUsUUFBUSxVQUFBLEVBQUUsUUFBUSxVQUFBLEVBQUMsQ0FBQzthQUM5RCxVQUFVLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFbEIsNEVBQTRFO1FBQzVFLEVBQUUsQ0FBQyxDQUFDLE1BQUksS0FBSyxXQUFXLENBQUMsQ0FBQyxDQUFDO1lBQ3pCLE1BQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNmLENBQUM7UUFFRCxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ1IsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBQ2QsTUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDeEIsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUNmLE1BQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzFCLENBQUM7WUFDRCxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztnQkFDaEIsTUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDNUIsQ0FBQztRQUNILENBQUM7UUFFRCxNQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFFWCxFQUFFLENBQUMsQ0FBQyxPQUFPLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQztZQUN0QixzQ0FBc0M7WUFDdEMsSUFBTSxJQUFJLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7aUJBQzNCLElBQUksQ0FBQyxPQUFPLEVBQUUsY0FBYyxDQUFDLENBQUM7WUFFakMsc0JBQXNCO1lBQ3RCLEVBQUUsQ0FBQyxDQUFDLE9BQU8sS0FBSyxJQUFJLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUNqRCxJQUFNLEtBQUcsR0FBRyxRQUFRLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztnQkFDbEQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7cUJBQ2IsSUFBSSxDQUFDLGVBQWEsS0FBRyxDQUFDLFdBQVcsRUFBSSxDQUFDO3FCQUN0QyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQztxQkFDakIsSUFBSSxDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUM7cUJBQ3hCLElBQUksQ0FBQyxVQUFVLEVBQUUsbUJBQWlCLEtBQUssQ0FBQztxQkFDeEMsRUFBRSxDQUFDLFdBQVcsRUFBRTtvQkFBQSxpQkFLaEI7b0JBSkMsTUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBQSxHQUFHO3dCQUMzQixLQUFJLENBQUMsSUFBSSxHQUFJLEdBQUcsQ0FBQztvQkFDbkIsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLFVBQUEsS0FBSyxJQUFNLE1BQU0sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQ3BDLEVBQUUsQ0FBQyxLQUFLLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBQzVCLENBQUMsQ0FBQyxDQUFDO1lBQ1AsQ0FBQztZQUVELDJCQUEyQjtZQUMzQixFQUFFLENBQUMsQ0FBQyxPQUFPLEtBQUssSUFBSSxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFDakQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7cUJBQ2IsSUFBSSxDQUFDLGFBQWEsQ0FBQztxQkFDbkIsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUM7cUJBQ2pCLEVBQUUsQ0FBQyxPQUFPLEVBQUU7b0JBQ1gsVUFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsWUFBWSxJQUFJLEVBQUUsRUFBRSxHQUFHLENBQUMsWUFBWSxJQUFJLEVBQUUsQ0FBQyxDQUFDO29CQUMxRixFQUFFLENBQUMsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUM1QixDQUFDLENBQUMsQ0FBQztZQUNQLENBQUM7WUFFRCxtQ0FBbUM7WUFDbkMsRUFBRSxDQUFDLENBQUMsT0FBTyxLQUFLLElBQUksSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBQ2pELElBQU0sV0FBUyxHQUFHLEdBQUcsQ0FBQyxTQUFTLElBQUksZ0NBQWdDLENBQUM7Z0JBQ3BFLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDO3FCQUNiLElBQUksQ0FBQyxxQkFBcUIsQ0FBQztxQkFDM0IsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUM7cUJBQ2pCLEVBQUUsQ0FBQyxPQUFPLEVBQUU7b0JBQ1gsV0FBSSxDQUFDLE1BQU0sRUFBRSxXQUFTLEVBQUU7d0JBQ3RCLElBQUksUUFBQTt3QkFDSixJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDcEMsQ0FBQyxDQUFDO29CQUNILEVBQUUsQ0FBQyxLQUFLLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBQzVCLENBQUMsQ0FBQyxDQUFDO1lBQ1AsQ0FBQztRQUNILENBQUM7UUFFRCxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFDLElBQUksUUFBQSxFQUFFLElBQUksTUFBQSxFQUFDLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNiLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzdCLENBQUM7QUFDSCxDQUFDO0FBOUlELHdCQThJQztBQUVELG9CQUFvQixNQUFjLEVBQUUsWUFBb0IsRUFBRSxZQUFvQjtJQUM1RSxJQUFNLE1BQU0sR0FBRyxpQkFBZSxZQUFZLDRDQUF1QyxDQUFDO0lBQ2xGLElBQU0sTUFBTSxHQUFHLGtCQUFnQixZQUFZLG1CQUFnQixDQUFDO0lBQzVELElBQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDNUIsR0FBRyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLE1BQU0sR0FBRyxNQUFNLENBQUMsQ0FBQztJQUM3QyxHQUFHLENBQUMsUUFBUSxDQUFDLEtBQUssR0FBRyxrQkFBa0IsQ0FBQztBQUMxQyxDQUFDIn0=
\ No newline at end of file
diff --git a/build/index.d.ts b/build/index.d.ts
new file mode 100644
index 00000000..03286194
--- /dev/null
+++ b/build/index.d.ts
@@ -0,0 +1,7 @@
+import embed from './embed';
+declare const embedModule: typeof embed & {
+ default?: typeof embed;
+ vega?;
+ vl?;
+};
+export = embedModule;
diff --git a/build/index.js b/build/index.js
new file mode 100644
index 00000000..6783682e
--- /dev/null
+++ b/build/index.js
@@ -0,0 +1,11 @@
+"use strict";
+var vega = require("vega-lib");
+var vl = require("vega-lite");
+var embed_1 = require("./embed");
+var embedModule = embed_1.default;
+embedModule.default = embed_1.default;
+// expose Vega and Vega-Lite libs
+embedModule.vega = vega;
+embedModule.vl = vl;
+module.exports = embedModule;
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLCtCQUFpQztBQUNqQyw4QkFBZ0M7QUFFaEMsaUNBQTRCO0FBRTVCLElBQU0sV0FBVyxHQUF3RCxlQUFLLENBQUM7QUFFL0UsV0FBVyxDQUFDLE9BQU8sR0FBRyxlQUFLLENBQUM7QUFFNUIsaUNBQWlDO0FBQ2pDLFdBQVcsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBQ3hCLFdBQVcsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDO0FBRXBCLGlCQUFTLFdBQVcsQ0FBQyJ9
\ No newline at end of file
diff --git a/build/post.d.ts b/build/post.d.ts
new file mode 100644
index 00000000..7431e9a1
--- /dev/null
+++ b/build/post.d.ts
@@ -0,0 +1,4 @@
+/**
+ * Open editor url in a new window, and pass a message.
+ */
+export declare function post(window: Window, url: string, data: any): void;
diff --git a/build/post.js b/build/post.js
new file mode 100644
index 00000000..f867d955
--- /dev/null
+++ b/build/post.js
@@ -0,0 +1,31 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+/**
+ * Open editor url in a new window, and pass a message.
+ */
+function post(window, url, data) {
+ var editor = window.open(url);
+ var wait = 10000;
+ var step = 250;
+ var count = ~~(wait / step);
+ function listen(evt) {
+ if (evt.source === editor) {
+ count = 0;
+ window.removeEventListener('message', listen, false);
+ }
+ }
+ window.addEventListener('message', listen, false);
+ // send message
+ // periodically resend until ack received or timeout
+ function send() {
+ if (count <= 0) {
+ return;
+ }
+ editor.postMessage(data, '*');
+ setTimeout(send, step);
+ count -= 1;
+ }
+ setTimeout(send, step);
+}
+exports.post = post;
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9zdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9wb3N0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUE7O0dBRUc7QUFDSCxjQUFxQixNQUFjLEVBQUUsR0FBVyxFQUFFLElBQVM7SUFDekQsSUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNoQyxJQUFNLElBQUksR0FBRyxLQUFLLENBQUM7SUFDbkIsSUFBTSxJQUFJLEdBQUcsR0FBRyxDQUFDO0lBQ2pCLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQztJQUU1QixnQkFBZ0IsR0FBRztRQUNqQixFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsTUFBTSxLQUFLLE1BQU0sQ0FBQyxDQUFDLENBQUM7WUFDMUIsS0FBSyxHQUFHLENBQUMsQ0FBQztZQUNWLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ3ZELENBQUM7SUFDSCxDQUFDO0lBQ0QsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFFbEQsZUFBZTtJQUNmLG9EQUFvRDtJQUNwRDtRQUNFLEVBQUUsQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2YsTUFBTSxDQUFDO1FBQ1QsQ0FBQztRQUNELE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzlCLFVBQVUsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDdkIsS0FBSyxJQUFJLENBQUMsQ0FBQztJQUNiLENBQUM7SUFDRCxVQUFVLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ3pCLENBQUM7QUF6QkQsb0JBeUJDIn0=
\ No newline at end of file
diff --git a/build/vega-embed.js b/build/vega-embed.js
new file mode 100644
index 00000000..02d705d7
--- /dev/null
+++ b/build/vega-embed.js
@@ -0,0 +1,37884 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.vegaEmbed = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o n2) return 1;
+ if (n2 > n1) return -1;
+ }
+
+ if ([s1[2], s2[2]].every(patch.test.bind(patch))) {
+ var p1 = patch.exec(s1[2])[1].split('.').map(tryParse);
+ var p2 = patch.exec(s2[2])[1].split('.').map(tryParse);
+
+ for (i = 0; i < Math.max(p1.length, p2.length); i++) {
+ if (p1[i] === undefined || typeof p2[i] === 'string' && typeof p1[i] === 'number') return -1;
+ if (p2[i] === undefined || typeof p1[i] === 'string' && typeof p2[i] === 'number') return 1;
+
+ if (p1[i] > p2[i]) return 1;
+ if (p2[i] > p1[i]) return -1;
+ }
+ } else if ([s1[2], s2[2]].some(patch.test.bind(patch))) {
+ return patch.test(s1[2]) ? -1 : 1;
+ }
+
+ return 0;
+ };
+
+}));
+
+},{}],3:[function(require,module,exports){
+// https://d3js.org/d3-selection/ Version 1.3.0. Copyright 2018 Mike Bostock.
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (factory((global.d3 = global.d3 || {})));
+}(this, (function (exports) { 'use strict';
+
+var xhtml = "http://www.w3.org/1999/xhtml";
+
+var namespaces = {
+ svg: "http://www.w3.org/2000/svg",
+ xhtml: xhtml,
+ xlink: "http://www.w3.org/1999/xlink",
+ xml: "http://www.w3.org/XML/1998/namespace",
+ xmlns: "http://www.w3.org/2000/xmlns/"
+};
+
+function namespace(name) {
+ var prefix = name += "", i = prefix.indexOf(":");
+ if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
+ return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;
+}
+
+function creatorInherit(name) {
+ return function() {
+ var document = this.ownerDocument,
+ uri = this.namespaceURI;
+ return uri === xhtml && document.documentElement.namespaceURI === xhtml
+ ? document.createElement(name)
+ : document.createElementNS(uri, name);
+ };
+}
+
+function creatorFixed(fullname) {
+ return function() {
+ return this.ownerDocument.createElementNS(fullname.space, fullname.local);
+ };
+}
+
+function creator(name) {
+ var fullname = namespace(name);
+ return (fullname.local
+ ? creatorFixed
+ : creatorInherit)(fullname);
+}
+
+function none() {}
+
+function selector(selector) {
+ return selector == null ? none : function() {
+ return this.querySelector(selector);
+ };
+}
+
+function selection_select(select) {
+ if (typeof select !== "function") select = selector(select);
+
+ for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
+ if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
+ if ("__data__" in node) subnode.__data__ = node.__data__;
+ subgroup[i] = subnode;
+ }
+ }
+ }
+
+ return new Selection(subgroups, this._parents);
+}
+
+function empty() {
+ return [];
+}
+
+function selectorAll(selector) {
+ return selector == null ? empty : function() {
+ return this.querySelectorAll(selector);
+ };
+}
+
+function selection_selectAll(select) {
+ if (typeof select !== "function") select = selectorAll(select);
+
+ for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
+ if (node = group[i]) {
+ subgroups.push(select.call(node, node.__data__, i, group));
+ parents.push(node);
+ }
+ }
+ }
+
+ return new Selection(subgroups, parents);
+}
+
+var matcher = function(selector) {
+ return function() {
+ return this.matches(selector);
+ };
+};
+
+if (typeof document !== "undefined") {
+ var element = document.documentElement;
+ if (!element.matches) {
+ var vendorMatches = element.webkitMatchesSelector
+ || element.msMatchesSelector
+ || element.mozMatchesSelector
+ || element.oMatchesSelector;
+ matcher = function(selector) {
+ return function() {
+ return vendorMatches.call(this, selector);
+ };
+ };
+ }
+}
+
+var matcher$1 = matcher;
+
+function selection_filter(match) {
+ if (typeof match !== "function") match = matcher$1(match);
+
+ for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
+ if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
+ subgroup.push(node);
+ }
+ }
+ }
+
+ return new Selection(subgroups, this._parents);
+}
+
+function sparse(update) {
+ return new Array(update.length);
+}
+
+function selection_enter() {
+ return new Selection(this._enter || this._groups.map(sparse), this._parents);
+}
+
+function EnterNode(parent, datum) {
+ this.ownerDocument = parent.ownerDocument;
+ this.namespaceURI = parent.namespaceURI;
+ this._next = null;
+ this._parent = parent;
+ this.__data__ = datum;
+}
+
+EnterNode.prototype = {
+ constructor: EnterNode,
+ appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
+ insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
+ querySelector: function(selector) { return this._parent.querySelector(selector); },
+ querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
+};
+
+function constant(x) {
+ return function() {
+ return x;
+ };
+}
+
+var keyPrefix = "$"; // Protect against keys like “__proto__”.
+
+function bindIndex(parent, group, enter, update, exit, data) {
+ var i = 0,
+ node,
+ groupLength = group.length,
+ dataLength = data.length;
+
+ // Put any non-null nodes that fit into update.
+ // Put any null nodes into enter.
+ // Put any remaining data into enter.
+ for (; i < dataLength; ++i) {
+ if (node = group[i]) {
+ node.__data__ = data[i];
+ update[i] = node;
+ } else {
+ enter[i] = new EnterNode(parent, data[i]);
+ }
+ }
+
+ // Put any non-null nodes that don’t fit into exit.
+ for (; i < groupLength; ++i) {
+ if (node = group[i]) {
+ exit[i] = node;
+ }
+ }
+}
+
+function bindKey(parent, group, enter, update, exit, data, key) {
+ var i,
+ node,
+ nodeByKeyValue = {},
+ groupLength = group.length,
+ dataLength = data.length,
+ keyValues = new Array(groupLength),
+ keyValue;
+
+ // Compute the key for each node.
+ // If multiple nodes have the same key, the duplicates are added to exit.
+ for (i = 0; i < groupLength; ++i) {
+ if (node = group[i]) {
+ keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
+ if (keyValue in nodeByKeyValue) {
+ exit[i] = node;
+ } else {
+ nodeByKeyValue[keyValue] = node;
+ }
+ }
+ }
+
+ // Compute the key for each datum.
+ // If there a node associated with this key, join and add it to update.
+ // If there is not (or the key is a duplicate), add it to enter.
+ for (i = 0; i < dataLength; ++i) {
+ keyValue = keyPrefix + key.call(parent, data[i], i, data);
+ if (node = nodeByKeyValue[keyValue]) {
+ update[i] = node;
+ node.__data__ = data[i];
+ nodeByKeyValue[keyValue] = null;
+ } else {
+ enter[i] = new EnterNode(parent, data[i]);
+ }
+ }
+
+ // Add any remaining nodes that were not bound to data to exit.
+ for (i = 0; i < groupLength; ++i) {
+ if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
+ exit[i] = node;
+ }
+ }
+}
+
+function selection_data(value, key) {
+ if (!value) {
+ data = new Array(this.size()), j = -1;
+ this.each(function(d) { data[++j] = d; });
+ return data;
+ }
+
+ var bind = key ? bindKey : bindIndex,
+ parents = this._parents,
+ groups = this._groups;
+
+ if (typeof value !== "function") value = constant(value);
+
+ for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
+ var parent = parents[j],
+ group = groups[j],
+ groupLength = group.length,
+ data = value.call(parent, parent && parent.__data__, j, parents),
+ dataLength = data.length,
+ enterGroup = enter[j] = new Array(dataLength),
+ updateGroup = update[j] = new Array(dataLength),
+ exitGroup = exit[j] = new Array(groupLength);
+
+ bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
+
+ // Now connect the enter nodes to their following update node, such that
+ // appendChild can insert the materialized enter node before this node,
+ // rather than at the end of the parent node.
+ for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
+ if (previous = enterGroup[i0]) {
+ if (i0 >= i1) i1 = i0 + 1;
+ while (!(next = updateGroup[i1]) && ++i1 < dataLength);
+ previous._next = next || null;
+ }
+ }
+ }
+
+ update = new Selection(update, parents);
+ update._enter = enter;
+ update._exit = exit;
+ return update;
+}
+
+function selection_exit() {
+ return new Selection(this._exit || this._groups.map(sparse), this._parents);
+}
+
+function selection_merge(selection$$1) {
+
+ for (var groups0 = this._groups, groups1 = selection$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
+ for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
+ if (node = group0[i] || group1[i]) {
+ merge[i] = node;
+ }
+ }
+ }
+
+ for (; j < m0; ++j) {
+ merges[j] = groups0[j];
+ }
+
+ return new Selection(merges, this._parents);
+}
+
+function selection_order() {
+
+ for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
+ for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
+ if (node = group[i]) {
+ if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
+ next = node;
+ }
+ }
+ }
+
+ return this;
+}
+
+function selection_sort(compare) {
+ if (!compare) compare = ascending;
+
+ function compareNode(a, b) {
+ return a && b ? compare(a.__data__, b.__data__) : !a - !b;
+ }
+
+ for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
+ if (node = group[i]) {
+ sortgroup[i] = node;
+ }
+ }
+ sortgroup.sort(compareNode);
+ }
+
+ return new Selection(sortgroups, this._parents).order();
+}
+
+function ascending(a, b) {
+ return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
+}
+
+function selection_call() {
+ var callback = arguments[0];
+ arguments[0] = this;
+ callback.apply(null, arguments);
+ return this;
+}
+
+function selection_nodes() {
+ var nodes = new Array(this.size()), i = -1;
+ this.each(function() { nodes[++i] = this; });
+ return nodes;
+}
+
+function selection_node() {
+
+ for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
+ for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
+ var node = group[i];
+ if (node) return node;
+ }
+ }
+
+ return null;
+}
+
+function selection_size() {
+ var size = 0;
+ this.each(function() { ++size; });
+ return size;
+}
+
+function selection_empty() {
+ return !this.node();
+}
+
+function selection_each(callback) {
+
+ for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
+ for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
+ if (node = group[i]) callback.call(node, node.__data__, i, group);
+ }
+ }
+
+ return this;
+}
+
+function attrRemove(name) {
+ return function() {
+ this.removeAttribute(name);
+ };
+}
+
+function attrRemoveNS(fullname) {
+ return function() {
+ this.removeAttributeNS(fullname.space, fullname.local);
+ };
+}
+
+function attrConstant(name, value) {
+ return function() {
+ this.setAttribute(name, value);
+ };
+}
+
+function attrConstantNS(fullname, value) {
+ return function() {
+ this.setAttributeNS(fullname.space, fullname.local, value);
+ };
+}
+
+function attrFunction(name, value) {
+ return function() {
+ var v = value.apply(this, arguments);
+ if (v == null) this.removeAttribute(name);
+ else this.setAttribute(name, v);
+ };
+}
+
+function attrFunctionNS(fullname, value) {
+ return function() {
+ var v = value.apply(this, arguments);
+ if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
+ else this.setAttributeNS(fullname.space, fullname.local, v);
+ };
+}
+
+function selection_attr(name, value) {
+ var fullname = namespace(name);
+
+ if (arguments.length < 2) {
+ var node = this.node();
+ return fullname.local
+ ? node.getAttributeNS(fullname.space, fullname.local)
+ : node.getAttribute(fullname);
+ }
+
+ return this.each((value == null
+ ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
+ ? (fullname.local ? attrFunctionNS : attrFunction)
+ : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
+}
+
+function defaultView(node) {
+ return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
+ || (node.document && node) // node is a Window
+ || node.defaultView; // node is a Document
+}
+
+function styleRemove(name) {
+ return function() {
+ this.style.removeProperty(name);
+ };
+}
+
+function styleConstant(name, value, priority) {
+ return function() {
+ this.style.setProperty(name, value, priority);
+ };
+}
+
+function styleFunction(name, value, priority) {
+ return function() {
+ var v = value.apply(this, arguments);
+ if (v == null) this.style.removeProperty(name);
+ else this.style.setProperty(name, v, priority);
+ };
+}
+
+function selection_style(name, value, priority) {
+ return arguments.length > 1
+ ? this.each((value == null
+ ? styleRemove : typeof value === "function"
+ ? styleFunction
+ : styleConstant)(name, value, priority == null ? "" : priority))
+ : styleValue(this.node(), name);
+}
+
+function styleValue(node, name) {
+ return node.style.getPropertyValue(name)
+ || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
+}
+
+function propertyRemove(name) {
+ return function() {
+ delete this[name];
+ };
+}
+
+function propertyConstant(name, value) {
+ return function() {
+ this[name] = value;
+ };
+}
+
+function propertyFunction(name, value) {
+ return function() {
+ var v = value.apply(this, arguments);
+ if (v == null) delete this[name];
+ else this[name] = v;
+ };
+}
+
+function selection_property(name, value) {
+ return arguments.length > 1
+ ? this.each((value == null
+ ? propertyRemove : typeof value === "function"
+ ? propertyFunction
+ : propertyConstant)(name, value))
+ : this.node()[name];
+}
+
+function classArray(string) {
+ return string.trim().split(/^|\s+/);
+}
+
+function classList(node) {
+ return node.classList || new ClassList(node);
+}
+
+function ClassList(node) {
+ this._node = node;
+ this._names = classArray(node.getAttribute("class") || "");
+}
+
+ClassList.prototype = {
+ add: function(name) {
+ var i = this._names.indexOf(name);
+ if (i < 0) {
+ this._names.push(name);
+ this._node.setAttribute("class", this._names.join(" "));
+ }
+ },
+ remove: function(name) {
+ var i = this._names.indexOf(name);
+ if (i >= 0) {
+ this._names.splice(i, 1);
+ this._node.setAttribute("class", this._names.join(" "));
+ }
+ },
+ contains: function(name) {
+ return this._names.indexOf(name) >= 0;
+ }
+};
+
+function classedAdd(node, names) {
+ var list = classList(node), i = -1, n = names.length;
+ while (++i < n) list.add(names[i]);
+}
+
+function classedRemove(node, names) {
+ var list = classList(node), i = -1, n = names.length;
+ while (++i < n) list.remove(names[i]);
+}
+
+function classedTrue(names) {
+ return function() {
+ classedAdd(this, names);
+ };
+}
+
+function classedFalse(names) {
+ return function() {
+ classedRemove(this, names);
+ };
+}
+
+function classedFunction(names, value) {
+ return function() {
+ (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
+ };
+}
+
+function selection_classed(name, value) {
+ var names = classArray(name + "");
+
+ if (arguments.length < 2) {
+ var list = classList(this.node()), i = -1, n = names.length;
+ while (++i < n) if (!list.contains(names[i])) return false;
+ return true;
+ }
+
+ return this.each((typeof value === "function"
+ ? classedFunction : value
+ ? classedTrue
+ : classedFalse)(names, value));
+}
+
+function textRemove() {
+ this.textContent = "";
+}
+
+function textConstant(value) {
+ return function() {
+ this.textContent = value;
+ };
+}
+
+function textFunction(value) {
+ return function() {
+ var v = value.apply(this, arguments);
+ this.textContent = v == null ? "" : v;
+ };
+}
+
+function selection_text(value) {
+ return arguments.length
+ ? this.each(value == null
+ ? textRemove : (typeof value === "function"
+ ? textFunction
+ : textConstant)(value))
+ : this.node().textContent;
+}
+
+function htmlRemove() {
+ this.innerHTML = "";
+}
+
+function htmlConstant(value) {
+ return function() {
+ this.innerHTML = value;
+ };
+}
+
+function htmlFunction(value) {
+ return function() {
+ var v = value.apply(this, arguments);
+ this.innerHTML = v == null ? "" : v;
+ };
+}
+
+function selection_html(value) {
+ return arguments.length
+ ? this.each(value == null
+ ? htmlRemove : (typeof value === "function"
+ ? htmlFunction
+ : htmlConstant)(value))
+ : this.node().innerHTML;
+}
+
+function raise() {
+ if (this.nextSibling) this.parentNode.appendChild(this);
+}
+
+function selection_raise() {
+ return this.each(raise);
+}
+
+function lower() {
+ if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
+}
+
+function selection_lower() {
+ return this.each(lower);
+}
+
+function selection_append(name) {
+ var create = typeof name === "function" ? name : creator(name);
+ return this.select(function() {
+ return this.appendChild(create.apply(this, arguments));
+ });
+}
+
+function constantNull() {
+ return null;
+}
+
+function selection_insert(name, before) {
+ var create = typeof name === "function" ? name : creator(name),
+ select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
+ return this.select(function() {
+ return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
+ });
+}
+
+function remove() {
+ var parent = this.parentNode;
+ if (parent) parent.removeChild(this);
+}
+
+function selection_remove() {
+ return this.each(remove);
+}
+
+function selection_cloneShallow() {
+ return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);
+}
+
+function selection_cloneDeep() {
+ return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);
+}
+
+function selection_clone(deep) {
+ return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
+}
+
+function selection_datum(value) {
+ return arguments.length
+ ? this.property("__data__", value)
+ : this.node().__data__;
+}
+
+var filterEvents = {};
+
+exports.event = null;
+
+if (typeof document !== "undefined") {
+ var element$1 = document.documentElement;
+ if (!("onmouseenter" in element$1)) {
+ filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"};
+ }
+}
+
+function filterContextListener(listener, index, group) {
+ listener = contextListener(listener, index, group);
+ return function(event) {
+ var related = event.relatedTarget;
+ if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
+ listener.call(this, event);
+ }
+ };
+}
+
+function contextListener(listener, index, group) {
+ return function(event1) {
+ var event0 = exports.event; // Events can be reentrant (e.g., focus).
+ exports.event = event1;
+ try {
+ listener.call(this, this.__data__, index, group);
+ } finally {
+ exports.event = event0;
+ }
+ };
+}
+
+function parseTypenames(typenames) {
+ return typenames.trim().split(/^|\s+/).map(function(t) {
+ var name = "", i = t.indexOf(".");
+ if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
+ return {type: t, name: name};
+ });
+}
+
+function onRemove(typename) {
+ return function() {
+ var on = this.__on;
+ if (!on) return;
+ for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
+ if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
+ this.removeEventListener(o.type, o.listener, o.capture);
+ } else {
+ on[++i] = o;
+ }
+ }
+ if (++i) on.length = i;
+ else delete this.__on;
+ };
+}
+
+function onAdd(typename, value, capture) {
+ var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
+ return function(d, i, group) {
+ var on = this.__on, o, listener = wrap(value, i, group);
+ if (on) for (var j = 0, m = on.length; j < m; ++j) {
+ if ((o = on[j]).type === typename.type && o.name === typename.name) {
+ this.removeEventListener(o.type, o.listener, o.capture);
+ this.addEventListener(o.type, o.listener = listener, o.capture = capture);
+ o.value = value;
+ return;
+ }
+ }
+ this.addEventListener(typename.type, listener, capture);
+ o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
+ if (!on) this.__on = [o];
+ else on.push(o);
+ };
+}
+
+function selection_on(typename, value, capture) {
+ var typenames = parseTypenames(typename + ""), i, n = typenames.length, t;
+
+ if (arguments.length < 2) {
+ var on = this.node().__on;
+ if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
+ for (i = 0, o = on[j]; i < n; ++i) {
+ if ((t = typenames[i]).type === o.type && t.name === o.name) {
+ return o.value;
+ }
+ }
+ }
+ return;
+ }
+
+ on = value ? onAdd : onRemove;
+ if (capture == null) capture = false;
+ for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
+ return this;
+}
+
+function customEvent(event1, listener, that, args) {
+ var event0 = exports.event;
+ event1.sourceEvent = exports.event;
+ exports.event = event1;
+ try {
+ return listener.apply(that, args);
+ } finally {
+ exports.event = event0;
+ }
+}
+
+function dispatchEvent(node, type, params) {
+ var window = defaultView(node),
+ event = window.CustomEvent;
+
+ if (typeof event === "function") {
+ event = new event(type, params);
+ } else {
+ event = window.document.createEvent("Event");
+ if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
+ else event.initEvent(type, false, false);
+ }
+
+ node.dispatchEvent(event);
+}
+
+function dispatchConstant(type, params) {
+ return function() {
+ return dispatchEvent(this, type, params);
+ };
+}
+
+function dispatchFunction(type, params) {
+ return function() {
+ return dispatchEvent(this, type, params.apply(this, arguments));
+ };
+}
+
+function selection_dispatch(type, params) {
+ return this.each((typeof params === "function"
+ ? dispatchFunction
+ : dispatchConstant)(type, params));
+}
+
+var root = [null];
+
+function Selection(groups, parents) {
+ this._groups = groups;
+ this._parents = parents;
+}
+
+function selection() {
+ return new Selection([[document.documentElement]], root);
+}
+
+Selection.prototype = selection.prototype = {
+ constructor: Selection,
+ select: selection_select,
+ selectAll: selection_selectAll,
+ filter: selection_filter,
+ data: selection_data,
+ enter: selection_enter,
+ exit: selection_exit,
+ merge: selection_merge,
+ order: selection_order,
+ sort: selection_sort,
+ call: selection_call,
+ nodes: selection_nodes,
+ node: selection_node,
+ size: selection_size,
+ empty: selection_empty,
+ each: selection_each,
+ attr: selection_attr,
+ style: selection_style,
+ property: selection_property,
+ classed: selection_classed,
+ text: selection_text,
+ html: selection_html,
+ raise: selection_raise,
+ lower: selection_lower,
+ append: selection_append,
+ insert: selection_insert,
+ remove: selection_remove,
+ clone: selection_clone,
+ datum: selection_datum,
+ on: selection_on,
+ dispatch: selection_dispatch
+};
+
+function select(selector) {
+ return typeof selector === "string"
+ ? new Selection([[document.querySelector(selector)]], [document.documentElement])
+ : new Selection([[selector]], root);
+}
+
+function create(name) {
+ return select(creator(name).call(document.documentElement));
+}
+
+var nextId = 0;
+
+function local() {
+ return new Local;
+}
+
+function Local() {
+ this._ = "@" + (++nextId).toString(36);
+}
+
+Local.prototype = local.prototype = {
+ constructor: Local,
+ get: function(node) {
+ var id = this._;
+ while (!(id in node)) if (!(node = node.parentNode)) return;
+ return node[id];
+ },
+ set: function(node, value) {
+ return node[this._] = value;
+ },
+ remove: function(node) {
+ return this._ in node && delete node[this._];
+ },
+ toString: function() {
+ return this._;
+ }
+};
+
+function sourceEvent() {
+ var current = exports.event, source;
+ while (source = current.sourceEvent) current = source;
+ return current;
+}
+
+function point(node, event) {
+ var svg = node.ownerSVGElement || node;
+
+ if (svg.createSVGPoint) {
+ var point = svg.createSVGPoint();
+ point.x = event.clientX, point.y = event.clientY;
+ point = point.matrixTransform(node.getScreenCTM().inverse());
+ return [point.x, point.y];
+ }
+
+ var rect = node.getBoundingClientRect();
+ return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
+}
+
+function mouse(node) {
+ var event = sourceEvent();
+ if (event.changedTouches) event = event.changedTouches[0];
+ return point(node, event);
+}
+
+function selectAll(selector) {
+ return typeof selector === "string"
+ ? new Selection([document.querySelectorAll(selector)], [document.documentElement])
+ : new Selection([selector == null ? [] : selector], root);
+}
+
+function touch(node, touches, identifier) {
+ if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches;
+
+ for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {
+ if ((touch = touches[i]).identifier === identifier) {
+ return point(node, touch);
+ }
+ }
+
+ return null;
+}
+
+function touches(node, touches) {
+ if (touches == null) touches = sourceEvent().touches;
+
+ for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {
+ points[i] = point(node, touches[i]);
+ }
+
+ return points;
+}
+
+exports.create = create;
+exports.creator = creator;
+exports.local = local;
+exports.matcher = matcher$1;
+exports.mouse = mouse;
+exports.namespace = namespace;
+exports.namespaces = namespaces;
+exports.clientPoint = point;
+exports.select = select;
+exports.selectAll = selectAll;
+exports.selection = selection;
+exports.selector = selector;
+exports.selectorAll = selectorAll;
+exports.style = styleValue;
+exports.touch = touch;
+exports.touches = touches;
+exports.window = defaultView;
+exports.customEvent = customEvent;
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
+
+},{}],4:[function(require,module,exports){
+(function (Buffer){
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (factory((global.vega = global.vega || {})));
+}(this, (function (exports) { 'use strict';
+
+var accessor = function(fn, fields, name) {
+ fn.fields = fields || [];
+ fn.fname = name;
+ return fn;
+};
+
+function accessorName(fn) {
+ return fn == null ? null : fn.fname;
+}
+
+function accessorFields(fn) {
+ return fn == null ? null : fn.fields;
+}
+
+var error$1 = function(message) {
+ throw Error(message);
+};
+
+var splitAccessPath = function(p) {
+ var path = [],
+ q = null,
+ b = 0,
+ n = p.length,
+ s = '',
+ i, j, c;
+
+ p = p + '';
+
+ function push() {
+ path.push(s + p.substring(i, j));
+ s = '';
+ i = j + 1;
+ }
+
+ for (i=j=0; j i) {
+ push();
+ } else {
+ i = j + 1;
+ }
+ } else if (c === '[') {
+ if (j > i) push();
+ b = i = j + 1;
+ } else if (c === ']') {
+ if (!b) error$1('Access path missing open bracket: ' + p);
+ if (b > 0) push();
+ b = 0;
+ i = j + 1;
+ }
+ }
+
+ if (b) error$1('Access path missing closing bracket: ' + p);
+ if (q) error$1('Access path missing closing quote: ' + p);
+
+ if (j > i) {
+ j++;
+ push();
+ }
+
+ return path;
+};
+
+var isArray = Array.isArray;
+
+var isObject = function(_) {
+ return _ === Object(_);
+};
+
+var isString = function(_) {
+ return typeof _ === 'string';
+};
+
+function $(x) {
+ return isArray(x) ? '[' + x.map($) + ']'
+ : isObject(x) || isString(x) ?
+ // Output valid JSON and JS source strings.
+ // See http://timelessrepo.com/json-isnt-a-javascript-subset
+ JSON.stringify(x).replace('\u2028','\\u2028').replace('\u2029', '\\u2029')
+ : x;
+}
+
+var field = function(field, name) {
+ var path = splitAccessPath(field),
+ code = 'return _[' + path.map($).join('][') + '];';
+
+ return accessor(
+ Function('_', code),
+ [(field = path.length===1 ? path[0] : field)],
+ name || field
+ );
+};
+
+var empty = [];
+
+var id = field('id');
+
+var identity = accessor(function(_) { return _; }, empty, 'identity');
+
+var zero = accessor(function() { return 0; }, empty, 'zero');
+
+var one = accessor(function() { return 1; }, empty, 'one');
+
+var truthy = accessor(function() { return true; }, empty, 'true');
+
+var falsy = accessor(function() { return false; }, empty, 'false');
+
+function log(method, level, input) {
+ var args = [level].concat([].slice.call(input));
+ console[method].apply(console, args); // eslint-disable-line no-console
+}
+
+var None = 0;
+var Error$1 = 1;
+var Warn = 2;
+var Info = 3;
+var Debug = 4;
+
+var logger = function(_) {
+ var level = _ || None;
+ return {
+ level: function(_) {
+ if (arguments.length) {
+ level = +_;
+ return this;
+ } else {
+ return level;
+ }
+ },
+ error: function() {
+ if (level >= Error$1) log('error', 'ERROR', arguments);
+ return this;
+ },
+ warn: function() {
+ if (level >= Warn) log('warn', 'WARN', arguments);
+ return this;
+ },
+ info: function() {
+ if (level >= Info) log('log', 'INFO', arguments);
+ return this;
+ },
+ debug: function() {
+ if (level >= Debug) log('log', 'DEBUG', arguments);
+ return this;
+ }
+ }
+};
+
+var peek = function(array) {
+ return array[array.length - 1];
+};
+
+var toNumber = function(_) {
+ return _ == null || _ === '' ? null : +_;
+};
+
+function exp(sign) {
+ return function(x) { return sign * Math.exp(x); };
+}
+
+function log$1(sign) {
+ return function(x) { return Math.log(sign * x); };
+}
+
+function pow(exponent) {
+ return function(x) {
+ return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);
+ };
+}
+
+function pan(domain, delta, lift, ground) {
+ var d0 = lift(domain[0]),
+ d1 = lift(peek(domain)),
+ dd = (d1 - d0) * delta;
+
+ return [
+ ground(d0 - dd),
+ ground(d1 - dd)
+ ];
+}
+
+function panLinear(domain, delta) {
+ return pan(domain, delta, toNumber, identity);
+}
+
+function panLog(domain, delta) {
+ var sign = Math.sign(domain[0]);
+ return pan(domain, delta, log$1(sign), exp(sign));
+}
+
+function panPow(domain, delta, exponent) {
+ return pan(domain, delta, pow(exponent), pow(1/exponent));
+}
+
+function zoom(domain, anchor, scale, lift, ground) {
+ var d0 = lift(domain[0]),
+ d1 = lift(peek(domain)),
+ da = anchor != null ? lift(anchor) : (d0 + d1) / 2;
+
+ return [
+ ground(da + (d0 - da) * scale),
+ ground(da + (d1 - da) * scale)
+ ];
+}
+
+function zoomLinear(domain, anchor, scale) {
+ return zoom(domain, anchor, scale, toNumber, identity);
+}
+
+function zoomLog(domain, anchor, scale) {
+ var sign = Math.sign(domain[0]);
+ return zoom(domain, anchor, scale, log$1(sign), exp(sign));
+}
+
+function zoomPow(domain, anchor, scale, exponent) {
+ return zoom(domain, anchor, scale, pow(exponent), pow(1/exponent));
+}
+
+var array = function(_) {
+ return _ != null ? (isArray(_) ? _ : [_]) : [];
+};
+
+var isFunction = function(_) {
+ return typeof _ === 'function';
+};
+
+var compare = function(fields, orders) {
+ var idx = [],
+ cmp = (fields = array(fields)).map(function(f, i) {
+ if (f == null) {
+ return null;
+ } else {
+ idx.push(i);
+ return isFunction(f) ? f
+ : splitAccessPath(f).map($).join('][');
+ }
+ }),
+ n = idx.length - 1,
+ ord = array(orders),
+ code = 'var u,v;return ',
+ i, j, f, u, v, d, t, lt, gt;
+
+ if (n < 0) return null;
+
+ for (j=0; j<=n; ++j) {
+ i = idx[j];
+ f = cmp[i];
+
+ if (isFunction(f)) {
+ d = 'f' + i;
+ u = '(u=this.' + d + '(a))';
+ v = '(v=this.' + d + '(b))';
+ (t = t || {})[d] = f;
+ } else {
+ u = '(u=a['+f+'])';
+ v = '(v=b['+f+'])';
+ }
+
+ d = '((v=v instanceof Date?+v:v),(u=u instanceof Date?+u:u))';
+
+ if (ord[i] !== 'descending') {
+ gt = 1;
+ lt = -1;
+ } else {
+ gt = -1;
+ lt = 1;
+ }
+
+ code += '(' + u+'<'+v+'||u==null)&&v!=null?' + lt
+ + ':(u>v||v==null)&&u!=null?' + gt
+ + ':'+d+'!==u&&v===v?' + lt
+ + ':v!==v&&u===u?' + gt
+ + (i < n ? ':' : ':0');
+ }
+
+ f = Function('a', 'b', code + ';');
+ if (t) f = f.bind(t);
+
+ fields = fields.reduce(function(map, field) {
+ if (isFunction(field)) {
+ (accessorFields(field) || []).forEach(function(_) { map[_] = 1; });
+ } else if (field != null) {
+ map[field + ''] = 1;
+ }
+ return map;
+ }, {});
+
+ return accessor(f, Object.keys(fields));
+};
+
+var constant = function(_) {
+ return isFunction(_) ? _ : function() { return _; };
+};
+
+var debounce = function(delay, handler) {
+ var tid, evt;
+
+ function callback() {
+ handler(evt);
+ tid = evt = null;
+ }
+
+ return function(e) {
+ evt = e;
+ if (tid) clearTimeout(tid);
+ tid = setTimeout(callback, delay);
+ };
+};
+
+var extend = function(_) {
+ for (var x, k, i=1, len=arguments.length; i= b) {
+ a = c = b;
+ break;
+ }
+ }
+ u = v = i;
+ while (++i < n) {
+ b = array[i];
+ if (b != null) {
+ if (a > b) {
+ a = b;
+ u = i;
+ }
+ if (c < b) {
+ c = b;
+ v = i;
+ }
+ }
+ }
+ } else {
+ while (++i < n) {
+ b = f(array[i], i, array);
+ if (b != null && b >= b) {
+ a = c = b;
+ break;
+ }
+ }
+ u = v = i;
+ while (++i < n) {
+ b = f(array[i], i, array);
+ if (b != null) {
+ if (a > b) {
+ a = b;
+ u = i;
+ }
+ if (c < b) {
+ c = b;
+ v = i;
+ }
+ }
+ }
+ }
+
+ return [u, v];
+};
+
+var NULL = {};
+
+var fastmap = function(input) {
+ var obj = {},
+ map,
+ test;
+
+ function has(key) {
+ return obj.hasOwnProperty(key) && obj[key] !== NULL;
+ }
+
+ map = {
+ size: 0,
+ empty: 0,
+ object: obj,
+ has: has,
+ get: function(key) {
+ return has(key) ? obj[key] : undefined;
+ },
+ set: function(key, value) {
+ if (!has(key)) {
+ ++map.size;
+ if (obj[key] === NULL) --map.empty;
+ }
+ obj[key] = value;
+ return this;
+ },
+ delete: function(key) {
+ if (has(key)) {
+ --map.size;
+ ++map.empty;
+ obj[key] = NULL;
+ }
+ return this;
+ },
+ clear: function() {
+ map.size = map.empty = 0;
+ map.object = obj = {};
+ },
+ test: function(_) {
+ if (arguments.length) {
+ test = _;
+ return map;
+ } else {
+ return test;
+ }
+ },
+ clean: function() {
+ var next = {},
+ size = 0,
+ key, value;
+ for (key in obj) {
+ value = obj[key];
+ if (value !== NULL && (!test || !test(value))) {
+ next[key] = value;
+ ++size;
+ }
+ }
+ map.size = size;
+ map.empty = 0;
+ map.object = (obj = next);
+ }
+ };
+
+ if (input) Object.keys(input).forEach(function(key) {
+ map.set(key, input[key]);
+ });
+
+ return map;
+};
+
+var inherits = function(child, parent) {
+ var proto = (child.prototype = Object.create(parent.prototype));
+ proto.constructor = child;
+ return proto;
+};
+
+var isBoolean = function(_) {
+ return typeof _ === 'boolean';
+};
+
+var isDate = function(_) {
+ return Object.prototype.toString.call(_) === '[object Date]';
+};
+
+var isNumber = function(_) {
+ return typeof _ === 'number';
+};
+
+var isRegExp = function(_) {
+ return Object.prototype.toString.call(_) === '[object RegExp]';
+};
+
+var key = function(fields, flat) {
+ if (fields) {
+ fields = flat
+ ? array(fields).map(function(f) { return f.replace(/\\(.)/g, '$1'); })
+ : array(fields);
+ }
+
+ var fn = !(fields && fields.length)
+ ? function() { return ''; }
+ : Function('_', 'return \'\'+' +
+ fields.map(function(f) {
+ return '_[' + (flat
+ ? $(f)
+ : splitAccessPath(f).map($).join('][')
+ ) + ']';
+ }).join('+\'|\'+') + ';');
+
+ return accessor(fn, fields, 'key');
+};
+
+var merge = function(compare, array0, array1, output) {
+ var n0 = array0.length,
+ n1 = array1.length;
+
+ if (!n1) return array0;
+ if (!n0) return array1;
+
+ var merged = output || new array0.constructor(n0 + n1),
+ i0 = 0, i1 = 0, i = 0;
+
+ for (; i0 0
+ ? array1[i1++]
+ : array0[i0++];
+ }
+
+ for (; i0= 0) s += str;
+ return s;
+};
+
+var pad = function(str, length, padchar, align) {
+ var c = padchar || ' ',
+ s = str + '',
+ n = length - s.length;
+
+ return n <= 0 ? s
+ : align === 'left' ? repeat(c, n) + s
+ : align === 'center' ? repeat(c, ~~(n/2)) + s + repeat(c, Math.ceil(n/2))
+ : s + repeat(c, n);
+};
+
+var toBoolean = function(_) {
+ return _ == null || _ === '' ? null : !_ || _ === 'false' || _ === '0' ? false : !!_;
+};
+
+function defaultParser(_) {
+ return isNumber(_) ? _ : isDate(_) ? _ : Date.parse(_);
+}
+
+var toDate = function(_, parser) {
+ parser = parser || defaultParser;
+ return _ == null || _ === '' ? null : parser(_);
+};
+
+var toString = function(_) {
+ return _ == null || _ === '' ? null : _ + '';
+};
+
+var toSet = function(_) {
+ for (var s={}, i=0, n=_.length; i= 0) {
+ list.splice(idx, 1);
+ }
+ }
+ return list;
+ };
+
+ return list;
+}
+
+var TUPLE_ID_KEY = Symbol('vega_id');
+var TUPLE_ID = 1;
+
+/**
+ * Resets the internal tuple id counter to one.
+ */
+
+
+/**
+ * Checks if an input value is a registered tuple.
+ * @param {*} t - The value to check.
+ * @return {boolean} True if the input is a tuple, false otherwise.
+ */
+function isTuple(t) {
+ return !!(t && tupleid(t));
+}
+
+/**
+ * Returns the id of a tuple.
+ * @param {object} t - The input tuple.
+ * @return {*} the tuple id.
+ */
+function tupleid(t) {
+ return t[TUPLE_ID_KEY];
+}
+
+/**
+ * Sets the id of a tuple.
+ * @param {object} t - The input tuple.
+ * @param {*} id - The id value to set.
+ * @return {object} the input tuple.
+ */
+function setid(t, id) {
+ t[TUPLE_ID_KEY] = id;
+ return t;
+}
+
+/**
+ * Ingest an object or value as a data tuple.
+ * If the input value is an object, an id field will be added to it. For
+ * efficiency, the input object is modified directly. A copy is not made.
+ * If the input value is a literal, it will be wrapped in a new object
+ * instance, with the value accessible as the 'data' property.
+ * @param datum - The value to ingest.
+ * @return {object} The ingested data tuple.
+ */
+function ingest(datum) {
+ var t = (datum === Object(datum)) ? datum : {data: datum};
+ return tupleid(t) ? t : setid(t, TUPLE_ID++);
+}
+
+/**
+ * Given a source tuple, return a derived copy.
+ * @param {object} t - The source tuple.
+ * @return {object} The derived tuple.
+ */
+function derive(t) {
+ return rederive(t, ingest({}));
+}
+
+/**
+ * Rederive a derived tuple by copying values from the source tuple.
+ * @param {object} t - The source tuple.
+ * @param {object} d - The derived tuple.
+ * @return {object} The derived tuple.
+ */
+function rederive(t, d) {
+ for (var k in t) d[k] = t[k];
+ return d;
+}
+
+/**
+ * Replace an existing tuple with a new tuple.
+ * @param {object} t - The existing data tuple.
+ * @param {object} d - The new tuple that replaces the old.
+ * @return {object} The new tuple.
+ */
+function replace(t, d) {
+ return setid(d, tupleid(t));
+}
+
+function isChangeSet(v) {
+ return v && v.constructor === changeset;
+}
+
+function changeset() {
+ var add = [], // insert tuples
+ rem = [], // remove tuples
+ mod = [], // modify tuples
+ remp = [], // remove by predicate
+ modp = [], // modify by predicate
+ reflow = false;
+
+ return {
+ constructor: changeset,
+ insert: function(t) {
+ var d = array(t), i = 0, n = d.length;
+ for (; i= 0) {
+ if (v[index] !== value || force) {
+ v[index] = value;
+ mod[index + ':' + name] = -1;
+ mod[name] = -1;
+ }
+ } else if (v !== value || force) {
+ o[name] = value;
+ mod[name] = isArray(value) ? 1 + value.length : -1;
+ }
+
+ return o;
+};
+
+/**
+ * Tests if one or more parameters has been modified. If invoked with no
+ * arguments, returns true if any parameter value has changed. If the first
+ * argument is array, returns trues if any parameter name in the array has
+ * changed. Otherwise, tests if the given name and optional array index has
+ * changed.
+ * @param {string} name - The parameter name to test.
+ * @param {number} [index=undefined] - The parameter array index to test.
+ * @return {boolean} - Returns true if a queried parameter was modified.
+ */
+prototype$2.modified = function(name, index) {
+ var mod = this[CACHE], k;
+ if (!arguments.length) {
+ for (k in mod) { if (mod[k]) return true; }
+ return false;
+ } else if (isArray(name)) {
+ for (k=0; k= 0)
+ ? (index + 1 < mod[name] || !!mod[index + ':' + name])
+ : !!mod[name];
+};
+
+/**
+ * Clears the modification records. After calling this method,
+ * all parameters are considered unmodified.
+ */
+prototype$2.clear = function() {
+ this[CACHE] = {};
+ return this;
+};
+
+var OP_ID = 0;
+var PULSE = 'pulse';
+var NO_PARAMS = new Parameters();
+
+// Boolean Flags
+var SKIP = 1;
+var MODIFIED = 2;
+
+/**
+ * An Operator is a processing node in a dataflow graph.
+ * Each operator stores a value and an optional value update function.
+ * Operators can accept a hash of named parameters. Parameter values can
+ * either be direct (JavaScript literals, arrays, objects) or indirect
+ * (other operators whose values will be pulled dynamically). Operators
+ * included as parameters will have this operator added as a dependency.
+ * @constructor
+ * @param {*} [init] - The initial value for this operator.
+ * @param {function(object, Pulse)} [update] - An update function. Upon
+ * evaluation of this operator, the update function will be invoked and the
+ * return value will be used as the new value of this operator.
+ * @param {object} [params] - The parameters for this operator.
+ * @param {boolean} [react=true] - Flag indicating if this operator should
+ * listen for changes to upstream operators included as parameters.
+ * @see parameters
+ */
+function Operator(init, update, params, react) {
+ this.id = ++OP_ID;
+ this.value = init;
+ this.stamp = -1;
+ this.rank = -1;
+ this.qrank = -1;
+ this.flags = 0;
+
+ if (update) {
+ this._update = update;
+ }
+ if (params) this.parameters(params, react);
+}
+
+var prototype$1 = Operator.prototype;
+
+/**
+ * Returns a list of target operators dependent on this operator.
+ * If this list does not exist, it is created and then returned.
+ * @return {UniqueList}
+ */
+prototype$1.targets = function() {
+ return this._targets || (this._targets = UniqueList(id));
+};
+
+/**
+ * Sets the value of this operator.
+ * @param {*} value - the value to set.
+ * @return {Number} Returns 1 if the operator value has changed
+ * according to strict equality, returns 0 otherwise.
+ */
+prototype$1.set = function(value) {
+ if (this.value !== value) {
+ this.value = value;
+ return 1;
+ } else {
+ return 0;
+ }
+};
+
+function flag(bit) {
+ return function(state) {
+ var f = this.flags;
+ if (arguments.length === 0) return !!(f & bit);
+ this.flags = state ? (f | bit) : (f & ~bit);
+ return this;
+ };
+}
+
+/**
+ * Indicates that operator evaluation should be skipped on the next pulse.
+ * This operator will still propagate incoming pulses, but its update function
+ * will not be invoked. The skip flag is reset after every pulse, so calling
+ * this method will affect processing of the next pulse only.
+ */
+prototype$1.skip = flag(SKIP);
+
+/**
+ * Indicates that this operator's value has been modified on its most recent
+ * pulse. Normally modification is checked via strict equality; however, in
+ * some cases it is more efficient to update the internal state of an object.
+ * In those cases, the modified flag can be used to trigger propagation. Once
+ * set, the modification flag persists across pulses until unset. The flag can
+ * be used with the last timestamp to test if a modification is recent.
+ */
+prototype$1.modified = flag(MODIFIED);
+
+/**
+ * Sets the parameters for this operator. The parameter values are analyzed for
+ * operator instances. If found, this operator will be added as a dependency
+ * of the parameterizing operator. Operator values are dynamically marshalled
+ * from each operator parameter prior to evaluation. If a parameter value is
+ * an array, the array will also be searched for Operator instances. However,
+ * the search does not recurse into sub-arrays or object properties.
+ * @param {object} params - A hash of operator parameters.
+ * @param {boolean} [react=true] - A flag indicating if this operator should
+ * automatically update (react) when parameter values change. In other words,
+ * this flag determines if the operator registers itself as a listener on
+ * any upstream operators included in the parameters.
+ * @return {Operator[]} - An array of upstream dependencies.
+ */
+prototype$1.parameters = function(params, react) {
+ react = react !== false;
+ var self = this,
+ argval = (self._argval = self._argval || new Parameters()),
+ argops = (self._argops = self._argops || []),
+ deps = [],
+ name, value, n, i;
+
+ function add(name, index, value) {
+ if (value instanceof Operator) {
+ if (value !== self) {
+ if (react) value.targets().add(self);
+ deps.push(value);
+ }
+ argops.push({op:value, name:name, index:index});
+ } else {
+ argval.set(name, index, value);
+ }
+ }
+
+ for (name in params) {
+ value = params[name];
+
+ if (name === PULSE) {
+ array(value).forEach(function(op) {
+ if (!(op instanceof Operator)) {
+ error$1('Pulse parameters must be operator instances.');
+ } else if (op !== self) {
+ op.targets().add(self);
+ deps.push(op);
+ }
+ });
+ self.source = value;
+ } else if (isArray(value)) {
+ argval.set(name, -1, Array(n = value.length));
+ for (i=0; i} - The source operators that should propagate
+ * to the target operator.
+ */
+var connect = function(target, sources) {
+ var targetRank = target.rank, i, n;
+
+ for (i=0, n=sources.length; i pause) {
+ t = now;
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+};
+
+prototype$3.debounce = function(delay) {
+ var s = stream();
+
+ this.targets().add(stream(null, null,
+ debounce(delay, function(e) {
+ var df = e.dataflow;
+ s.receive(e);
+ if (df && df.run) df.run();
+ })
+ ));
+
+ return s;
+};
+
+prototype$3.between = function(a, b) {
+ var active = false;
+ a.targets().add(stream(null, null, function() { active = true; }));
+ b.targets().add(stream(null, null, function() { active = false; }));
+ return this.filter(function() { return active; });
+};
+
+/**
+ * Create a new event stream from an event source.
+ * @param {object} source - The event source to monitor. The input must
+ * support the addEventListener method.
+ * @param {string} type - The event type.
+ * @param {function(object): boolean} [filter] - Event filter function.
+ * @param {function(object): *} [apply] - Event application function.
+ * If provided, this function will be invoked and the result will be
+ * used as the downstream event value.
+ * @return {EventStream}
+ */
+var events = function(source, type, filter, apply) {
+ var df = this,
+ s = stream(filter, apply),
+ send = function(e) {
+ e.dataflow = df;
+ try {
+ s.receive(e);
+ } catch (error) {
+ df.error(error);
+ } finally {
+ df.run();
+ }
+ },
+ sources;
+
+ if (typeof source === 'string' && typeof document !== 'undefined') {
+ sources = document.querySelectorAll(source);
+ } else {
+ sources = array(source);
+ }
+
+ for (var i=0, n=sources.length; i= keys.length) {
+ if (sortValues != null) array.sort(sortValues);
+ return rollup != null ? rollup(array) : array;
+ }
+
+ var i = -1,
+ n = array.length,
+ key = keys[depth++],
+ keyValue,
+ value,
+ valuesByKey = map(),
+ values,
+ result = createResult();
+
+ while (++i < n) {
+ if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) {
+ values.push(value);
+ } else {
+ valuesByKey.set(keyValue, [value]);
+ }
+ }
+
+ valuesByKey.each(function(values, key) {
+ setResult(result, key, apply(values, depth, createResult, setResult));
+ });
+
+ return result;
+ }
+
+ function entries(map$$1, depth) {
+ if (++depth > keys.length) return map$$1;
+ var array, sortKey = sortKeys[depth - 1];
+ if (rollup != null && depth >= keys.length) array = map$$1.entries();
+ else array = [], map$$1.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });
+ return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;
+ }
+
+ return nest = {
+ object: function(array) { return apply(array, 0, createObject, setObject); },
+ map: function(array) { return apply(array, 0, createMap, setMap); },
+ entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },
+ key: function(d) { keys.push(d); return nest; },
+ sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },
+ sortValues: function(order) { sortValues = order; return nest; },
+ rollup: function(f) { rollup = f; return nest; }
+ };
+};
+
+function createObject() {
+ return {};
+}
+
+function setObject(object, key, value) {
+ object[key] = value;
+}
+
+function createMap() {
+ return map();
+}
+
+function setMap(map$$1, key, value) {
+ map$$1.set(key, value);
+}
+
+function Set() {}
+
+var proto = map.prototype;
+
+Set.prototype = set.prototype = {
+ constructor: Set,
+ has: proto.has,
+ add: function(value) {
+ value += "";
+ this[prefix + value] = value;
+ return this;
+ },
+ remove: proto.remove,
+ clear: proto.clear,
+ values: proto.keys,
+ size: proto.size,
+ empty: proto.empty,
+ each: proto.each
+};
+
+function set(object, f) {
+ var set = new Set;
+
+ // Copy constructor.
+ if (object instanceof Set) object.each(function(value) { set.add(value); });
+
+ // Otherwise, assume it’s an array.
+ else if (object) {
+ var i = -1, n = object.length;
+ if (f == null) while (++i < n) set.add(object[i]);
+ else while (++i < n) set.add(f(object[i], i, object));
+ }
+
+ return set;
+}
+
+var noop = {value: function() {}};
+
+function dispatch() {
+ for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
+ if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t);
+ _[t] = [];
+ }
+ return new Dispatch(_);
+}
+
+function Dispatch(_) {
+ this._ = _;
+}
+
+function parseTypenames(typenames, types) {
+ return typenames.trim().split(/^|\s+/).map(function(t) {
+ var name = "", i = t.indexOf(".");
+ if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
+ if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
+ return {type: t, name: name};
+ });
+}
+
+Dispatch.prototype = dispatch.prototype = {
+ constructor: Dispatch,
+ on: function(typename, callback) {
+ var _ = this._,
+ T = parseTypenames(typename + "", _),
+ t,
+ i = -1,
+ n = T.length;
+
+ // If no callback was specified, return the callback of the given type and name.
+ if (arguments.length < 2) {
+ while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
+ return;
+ }
+
+ // If a type was specified, set the callback for the given type and name.
+ // Otherwise, if a null callback was specified, remove callbacks of the given name.
+ if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
+ while (++i < n) {
+ if (t = (typename = T[i]).type) _[t] = set$2(_[t], typename.name, callback);
+ else if (callback == null) for (t in _) _[t] = set$2(_[t], typename.name, null);
+ }
+
+ return this;
+ },
+ copy: function() {
+ var copy = {}, _ = this._;
+ for (var t in _) copy[t] = _[t].slice();
+ return new Dispatch(copy);
+ },
+ call: function(type, that) {
+ if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
+ if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
+ for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
+ },
+ apply: function(type, that, args) {
+ if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
+ for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
+ }
+};
+
+function get(type, name) {
+ for (var i = 0, n = type.length, c; i < n; ++i) {
+ if ((c = type[i]).name === name) {
+ return c.value;
+ }
+ }
+}
+
+function set$2(type, name, callback) {
+ for (var i = 0, n = type.length; i < n; ++i) {
+ if (type[i].name === name) {
+ type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
+ break;
+ }
+ }
+ if (callback != null) type.push({name: name, value: callback});
+ return type;
+}
+
+var request$1 = function(url, callback) {
+ var request,
+ event = dispatch("beforesend", "progress", "load", "error"),
+ mimeType,
+ headers = map(),
+ xhr = new XMLHttpRequest,
+ user = null,
+ password = null,
+ response,
+ responseType,
+ timeout = 0;
+
+ // If IE does not support CORS, use XDomainRequest.
+ if (typeof XDomainRequest !== "undefined"
+ && !("withCredentials" in xhr)
+ && /^(http(s)?:)?\/\//.test(url)) xhr = new XDomainRequest;
+
+ "onload" in xhr
+ ? xhr.onload = xhr.onerror = xhr.ontimeout = respond
+ : xhr.onreadystatechange = function(o) { xhr.readyState > 3 && respond(o); };
+
+ function respond(o) {
+ var status = xhr.status, result;
+ if (!status && hasResponse(xhr)
+ || status >= 200 && status < 300
+ || status === 304) {
+ if (response) {
+ try {
+ result = response.call(request, xhr);
+ } catch (e) {
+ event.call("error", request, e);
+ return;
+ }
+ } else {
+ result = xhr;
+ }
+ event.call("load", request, result);
+ } else {
+ event.call("error", request, o);
+ }
+ }
+
+ xhr.onprogress = function(e) {
+ event.call("progress", request, e);
+ };
+
+ request = {
+ header: function(name, value) {
+ name = (name + "").toLowerCase();
+ if (arguments.length < 2) return headers.get(name);
+ if (value == null) headers.remove(name);
+ else headers.set(name, value + "");
+ return request;
+ },
+
+ // If mimeType is non-null and no Accept header is set, a default is used.
+ mimeType: function(value) {
+ if (!arguments.length) return mimeType;
+ mimeType = value == null ? null : value + "";
+ return request;
+ },
+
+ // Specifies what type the response value should take;
+ // for instance, arraybuffer, blob, document, or text.
+ responseType: function(value) {
+ if (!arguments.length) return responseType;
+ responseType = value;
+ return request;
+ },
+
+ timeout: function(value) {
+ if (!arguments.length) return timeout;
+ timeout = +value;
+ return request;
+ },
+
+ user: function(value) {
+ return arguments.length < 1 ? user : (user = value == null ? null : value + "", request);
+ },
+
+ password: function(value) {
+ return arguments.length < 1 ? password : (password = value == null ? null : value + "", request);
+ },
+
+ // Specify how to convert the response content to a specific type;
+ // changes the callback value on "load" events.
+ response: function(value) {
+ response = value;
+ return request;
+ },
+
+ // Alias for send("GET", …).
+ get: function(data, callback) {
+ return request.send("GET", data, callback);
+ },
+
+ // Alias for send("POST", …).
+ post: function(data, callback) {
+ return request.send("POST", data, callback);
+ },
+
+ // If callback is non-null, it will be used for error and load events.
+ send: function(method, data, callback) {
+ xhr.open(method, url, true, user, password);
+ if (mimeType != null && !headers.has("accept")) headers.set("accept", mimeType + ",*/*");
+ if (xhr.setRequestHeader) headers.each(function(value, name) { xhr.setRequestHeader(name, value); });
+ if (mimeType != null && xhr.overrideMimeType) xhr.overrideMimeType(mimeType);
+ if (responseType != null) xhr.responseType = responseType;
+ if (timeout > 0) xhr.timeout = timeout;
+ if (callback == null && typeof data === "function") callback = data, data = null;
+ if (callback != null && callback.length === 1) callback = fixCallback(callback);
+ if (callback != null) request.on("error", callback).on("load", function(xhr) { callback(null, xhr); });
+ event.call("beforesend", request, xhr);
+ xhr.send(data == null ? null : data);
+ return request;
+ },
+
+ abort: function() {
+ xhr.abort();
+ return request;
+ },
+
+ on: function() {
+ var value = event.on.apply(event, arguments);
+ return value === event ? request : value;
+ }
+ };
+
+ if (callback != null) {
+ if (typeof callback !== "function") throw new Error("invalid callback: " + callback);
+ return request.get(callback);
+ }
+
+ return request;
+};
+
+function fixCallback(callback) {
+ return function(error, xhr) {
+ callback(error == null ? xhr : null);
+ };
+}
+
+function hasResponse(xhr) {
+ var type = xhr.responseType;
+ return type && type !== "text"
+ ? xhr.response // null on error
+ : xhr.responseText; // "" on error
+}
+
+var EOL = {};
+var EOF = {};
+var QUOTE = 34;
+var NEWLINE = 10;
+var RETURN = 13;
+
+function objectConverter(columns) {
+ return new Function("d", "return {" + columns.map(function(name, i) {
+ return JSON.stringify(name) + ": d[" + i + "]";
+ }).join(",") + "}");
+}
+
+function customConverter(columns, f) {
+ var object = objectConverter(columns);
+ return function(row, i) {
+ return f(object(row), i, columns);
+ };
+}
+
+// Compute unique columns in order of discovery.
+function inferColumns(rows) {
+ var columnSet = Object.create(null),
+ columns = [];
+
+ rows.forEach(function(row) {
+ for (var column in row) {
+ if (!(column in columnSet)) {
+ columns.push(columnSet[column] = column);
+ }
+ }
+ });
+
+ return columns;
+}
+
+var dsvFormat = function(delimiter) {
+ var reFormat = new RegExp("[\"" + delimiter + "\n\r]"),
+ DELIMITER = delimiter.charCodeAt(0);
+
+ function parse(text, f) {
+ var convert, columns, rows = parseRows(text, function(row, i) {
+ if (convert) return convert(row, i - 1);
+ columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
+ });
+ rows.columns = columns || [];
+ return rows;
+ }
+
+ function parseRows(text, f) {
+ var rows = [], // output rows
+ N = text.length,
+ I = 0, // current character index
+ n = 0, // current line number
+ t, // current token
+ eof = N <= 0, // current token followed by EOF?
+ eol = false; // current token followed by EOL?
+
+ // Strip the trailing newline.
+ if (text.charCodeAt(N - 1) === NEWLINE) --N;
+ if (text.charCodeAt(N - 1) === RETURN) --N;
+
+ function token() {
+ if (eof) return EOF;
+ if (eol) return eol = false, EOL;
+
+ // Unescape quotes.
+ var i, j = I, c;
+ if (text.charCodeAt(j) === QUOTE) {
+ while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE);
+ if ((i = I) >= N) eof = true;
+ else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;
+ else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
+ return text.slice(j + 1, i - 1).replace(/""/g, "\"");
+ }
+
+ // Find next delimiter or newline.
+ while (I < N) {
+ if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;
+ else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
+ else if (c !== DELIMITER) continue;
+ return text.slice(j, i);
+ }
+
+ // Return last token before EOF.
+ return eof = true, text.slice(j, N);
+ }
+
+ while ((t = token()) !== EOF) {
+ var row = [];
+ while (t !== EOL && t !== EOF) row.push(t), t = token();
+ if (f && (row = f(row, n++)) == null) continue;
+ rows.push(row);
+ }
+
+ return rows;
+ }
+
+ function format(rows, columns) {
+ if (columns == null) columns = inferColumns(rows);
+ return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) {
+ return columns.map(function(column) {
+ return formatValue(row[column]);
+ }).join(delimiter);
+ })).join("\n");
+ }
+
+ function formatRows(rows) {
+ return rows.map(formatRow).join("\n");
+ }
+
+ function formatRow(row) {
+ return row.map(formatValue).join(delimiter);
+ }
+
+ function formatValue(text) {
+ return text == null ? ""
+ : reFormat.test(text += "") ? "\"" + text.replace(/"/g, "\"\"") + "\""
+ : text;
+ }
+
+ return {
+ parse: parse,
+ parseRows: parseRows,
+ format: format,
+ formatRows: formatRows
+ };
+};
+
+var csv$1 = dsvFormat(",");
+
+var tsv = dsvFormat("\t");
+
+// Matches absolute URLs with optional protocol
+// https://... file://... //...
+var protocol_re = /^([A-Za-z]+:)?\/\//;
+
+// Special treatment in node.js for the file: protocol
+var fileProtocol = 'file://';
+
+// Request options to check for d3-request
+var requestOptions = [
+ 'mimeType',
+ 'responseType',
+ 'user',
+ 'password'
+];
+
+/**
+ * Creates a new loader instance that provides methods for requesting files
+ * from either the network or disk, and for sanitizing request URIs.
+ * @param {object} [options] - Optional default loading options to use.
+ * @return {object} - A new loader instance.
+ */
+var loader = function(options) {
+ return {
+ options: options || {},
+ sanitize: sanitize,
+ load: load,
+ file: file,
+ http: http
+ };
+};
+
+function marshall(loader, options) {
+ return extend({}, loader.options, options);
+}
+
+/**
+ * Load an external resource, typically either from the web or from the local
+ * filesystem. This function uses {@link sanitize} to first sanitize the uri,
+ * then calls either {@link http} (for web requests) or {@link file} (for
+ * filesystem loading).
+ * @param {string} uri - The resource indicator (e.g., URL or filename).
+ * @param {object} [options] - Optional loading options. These options will
+ * override any existing default options.
+ * @return {Promise} - A promise that resolves to the loaded content.
+ */
+function load(uri, options) {
+ var loader = this;
+ return loader.sanitize(uri, options)
+ .then(function(opt) {
+ var url = opt.href;
+ return opt.localFile
+ ? loader.file(url)
+ : loader.http(url, options);
+ });
+}
+
+/**
+ * URI sanitizer function.
+ * @param {string} uri - The uri (url or filename) to sanity check.
+ * @param {object} options - An options hash.
+ * @return {Promise} - A promise that resolves to an object containing
+ * sanitized uri data, or rejects it the input uri is deemed invalid.
+ * The properties of the resolved object are assumed to be
+ * valid attributes for an HTML 'a' tag. The sanitized uri *must* be
+ * provided by the 'href' property of the returned object.
+ */
+function sanitize(uri, options) {
+ options = marshall(this, options);
+ return new Promise(function(accept, reject) {
+ var result = {href: null},
+ isFile, hasProtocol, loadFile, base;
+
+ if (uri == null || typeof uri !== 'string') {
+ reject('Sanitize failure, invalid URI: ' + $(uri));
+ return;
+ }
+
+ hasProtocol = protocol_re.test(uri);
+
+ // if relative url (no protocol/host), prepend baseURL
+ if ((base = options.baseURL) && !hasProtocol) {
+ // Ensure that there is a slash between the baseURL (e.g. hostname) and url
+ if (!startsWith(uri, '/') && base[base.length-1] !== '/') {
+ uri = '/' + uri;
+ }
+ uri = base + uri;
+ }
+
+ // should we load from file system?
+ loadFile = (isFile = startsWith(uri, fileProtocol))
+ || options.mode === 'file'
+ || options.mode !== 'http' && !hasProtocol && fs();
+
+ if (isFile) {
+ // strip file protocol
+ uri = uri.slice(fileProtocol.length);
+ } else if (startsWith(uri, '//')) {
+ if (options.defaultProtocol === 'file') {
+ // if is file, strip protocol and set loadFile flag
+ uri = uri.slice(2);
+ loadFile = true;
+ } else {
+ // if relative protocol (starts with '//'), prepend default protocol
+ uri = (options.defaultProtocol || 'http') + ':' + uri;
+ }
+ }
+
+ // set non-enumerable mode flag to indicate local file load
+ Object.defineProperty(result, 'localFile', {value: !!loadFile});
+
+ // set uri
+ result.href = uri;
+
+ // set default result target, if specified
+ if (options.target) {
+ result.target = options.target + '';
+ }
+
+ // return
+ accept(result);
+ });
+}
+
+/**
+ * HTTP request loader.
+ * @param {string} url - The url to request.
+ * @param {object} options - An options hash.
+ * @return {Promise} - A promise that resolves to the file contents.
+ */
+function http(url, options) {
+ options = marshall(this, options);
+ return new Promise(function(accept, reject) {
+ var req = request$1(url),
+ name;
+
+ for (name in options.headers) {
+ req.header(name, options.headers[name]);
+ }
+
+ requestOptions.forEach(function(name) {
+ if (options[name]) req[name](options[name]);
+ });
+
+ req.on('error', function(error) {
+ reject(error || 'Error loading URL: ' + url);
+ })
+ .on('load', function(result) {
+ var text$$1 = result && result.responseText;
+ (!result || result.status === 0)
+ ? reject(text$$1 || 'Error')
+ : accept(text$$1);
+ })
+ .get();
+ });
+}
+
+/**
+ * File system loader.
+ * @param {string} filename - The file system path to load.
+ * @return {Promise} - A promise that resolves to the file contents.
+ */
+function file(filename) {
+ return new Promise(function(accept, reject) {
+ var f = fs();
+ f ? f.readFile(filename, function(error, data) {
+ if (error) reject(error);
+ else accept(data);
+ })
+ : reject('No file system access for ' + filename);
+ });
+}
+
+function fs() {
+ var fs = typeof require === 'function' && require('fs');
+ return fs && isFunction(fs.readFile) ? fs : null;
+}
+
+function startsWith(string, query) {
+ return string == null ? false : string.lastIndexOf(query, 0) === 0;
+}
+
+var typeParsers = {
+ boolean: toBoolean,
+ integer: toNumber,
+ number: toNumber,
+ date: toDate,
+ string: toString,
+ unknown: identity
+};
+
+var typeTests = [
+ isBoolean$1,
+ isInteger,
+ isNumber$1,
+ isDate$1
+];
+
+var typeList = [
+ 'boolean',
+ 'integer',
+ 'number',
+ 'date'
+];
+
+function inferType(values, field$$1) {
+ if (!values || !values.length) return 'unknown';
+
+ var tests = typeTests.slice(),
+ value, i, n, j;
+
+ for (i=0, n=values.length; i 1) arcs = extractArcs(topology, object$$1, filter);
+ else for (i = 0, arcs = new Array(n = topology.arcs.length); i < n; ++i) arcs[i] = i;
+ return {type: "MultiLineString", arcs: stitch(topology, arcs)};
+}
+
+function extractArcs(topology, object$$1, filter) {
+ var arcs = [],
+ geomsByArc = [],
+ geom;
+
+ function extract0(i) {
+ var j = i < 0 ? ~i : i;
+ (geomsByArc[j] || (geomsByArc[j] = [])).push({i: i, g: geom});
+ }
+
+ function extract1(arcs) {
+ arcs.forEach(extract0);
+ }
+
+ function extract2(arcs) {
+ arcs.forEach(extract1);
+ }
+
+ function extract3(arcs) {
+ arcs.forEach(extract2);
+ }
+
+ function geometry(o) {
+ switch (geom = o, o.type) {
+ case "GeometryCollection": o.geometries.forEach(geometry); break;
+ case "LineString": extract1(o.arcs); break;
+ case "MultiLineString": case "Polygon": extract2(o.arcs); break;
+ case "MultiPolygon": extract3(o.arcs); break;
+ }
+ }
+
+ geometry(object$$1);
+
+ geomsByArc.forEach(filter == null
+ ? function(geoms) { arcs.push(geoms[0].i); }
+ : function(geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); });
+
+ return arcs;
+}
+
+var bisect = function(a, x) {
+ var lo = 0, hi = a.length;
+ while (lo < hi) {
+ var mid = lo + hi >>> 1;
+ if (a[mid] < x) lo = mid + 1;
+ else hi = mid;
+ }
+ return lo;
+};
+
+var topojson = function(data, format) {
+ var method, object, property;
+ data = json$1(data, format);
+
+ method = (format && (property = format.feature)) ? feature
+ : (format && (property = format.mesh)) ? mesh
+ : error$1('Missing TopoJSON feature or mesh parameter.');
+
+ object = (object = data.objects[property])
+ ? method(data, object)
+ : error$1('Invalid TopoJSON object: ' + property);
+
+ return object && object.features || [object];
+};
+
+var formats = {
+ dsv: dsv$1,
+ csv: delimitedFormat(','),
+ tsv: delimitedFormat('\t'),
+ json: json$1,
+ topojson: topojson
+};
+
+var formats$1 = function(name, format) {
+ if (arguments.length > 1) {
+ formats[name] = format;
+ return this;
+ } else {
+ return formats.hasOwnProperty(name) ? formats[name] : null;
+ }
+};
+
+var t0 = new Date;
+var t1 = new Date;
+
+function newInterval(floori, offseti, count, field) {
+
+ function interval(date) {
+ return floori(date = new Date(+date)), date;
+ }
+
+ interval.floor = interval;
+
+ interval.ceil = function(date) {
+ return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;
+ };
+
+ interval.round = function(date) {
+ var d0 = interval(date),
+ d1 = interval.ceil(date);
+ return date - d0 < d1 - date ? d0 : d1;
+ };
+
+ interval.offset = function(date, step) {
+ return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;
+ };
+
+ interval.range = function(start, stop, step) {
+ var range = [], previous;
+ start = interval.ceil(start);
+ step = step == null ? 1 : Math.floor(step);
+ if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date
+ do range.push(previous = new Date(+start)), offseti(start, step), floori(start);
+ while (previous < start && start < stop);
+ return range;
+ };
+
+ interval.filter = function(test) {
+ return newInterval(function(date) {
+ if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);
+ }, function(date, step) {
+ if (date >= date) {
+ if (step < 0) while (++step <= 0) {
+ while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty
+ } else while (--step >= 0) {
+ while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty
+ }
+ }
+ });
+ };
+
+ if (count) {
+ interval.count = function(start, end) {
+ t0.setTime(+start), t1.setTime(+end);
+ floori(t0), floori(t1);
+ return Math.floor(count(t0, t1));
+ };
+
+ interval.every = function(step) {
+ step = Math.floor(step);
+ return !isFinite(step) || !(step > 0) ? null
+ : !(step > 1) ? interval
+ : interval.filter(field
+ ? function(d) { return field(d) % step === 0; }
+ : function(d) { return interval.count(0, d) % step === 0; });
+ };
+ }
+
+ return interval;
+}
+
+var millisecond = newInterval(function() {
+ // noop
+}, function(date, step) {
+ date.setTime(+date + step);
+}, function(start, end) {
+ return end - start;
+});
+
+// An optimized implementation for this simple case.
+millisecond.every = function(k) {
+ k = Math.floor(k);
+ if (!isFinite(k) || !(k > 0)) return null;
+ if (!(k > 1)) return millisecond;
+ return newInterval(function(date) {
+ date.setTime(Math.floor(date / k) * k);
+ }, function(date, step) {
+ date.setTime(+date + step * k);
+ }, function(start, end) {
+ return (end - start) / k;
+ });
+};
+
+var durationSecond = 1e3;
+var durationMinute = 6e4;
+var durationHour = 36e5;
+var durationDay = 864e5;
+var durationWeek = 6048e5;
+
+var second = newInterval(function(date) {
+ date.setTime(Math.floor(date / durationSecond) * durationSecond);
+}, function(date, step) {
+ date.setTime(+date + step * durationSecond);
+}, function(start, end) {
+ return (end - start) / durationSecond;
+}, function(date) {
+ return date.getUTCSeconds();
+});
+
+var minute = newInterval(function(date) {
+ date.setTime(Math.floor(date / durationMinute) * durationMinute);
+}, function(date, step) {
+ date.setTime(+date + step * durationMinute);
+}, function(start, end) {
+ return (end - start) / durationMinute;
+}, function(date) {
+ return date.getMinutes();
+});
+
+var hour = newInterval(function(date) {
+ var offset = date.getTimezoneOffset() * durationMinute % durationHour;
+ if (offset < 0) offset += durationHour;
+ date.setTime(Math.floor((+date - offset) / durationHour) * durationHour + offset);
+}, function(date, step) {
+ date.setTime(+date + step * durationHour);
+}, function(start, end) {
+ return (end - start) / durationHour;
+}, function(date) {
+ return date.getHours();
+});
+
+var day = newInterval(function(date) {
+ date.setHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setDate(date.getDate() + step);
+}, function(start, end) {
+ return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay;
+}, function(date) {
+ return date.getDate() - 1;
+});
+
+function weekday(i) {
+ return newInterval(function(date) {
+ date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);
+ date.setHours(0, 0, 0, 0);
+ }, function(date, step) {
+ date.setDate(date.getDate() + step * 7);
+ }, function(start, end) {
+ return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek;
+ });
+}
+
+var sunday = weekday(0);
+var monday = weekday(1);
+var tuesday = weekday(2);
+var wednesday = weekday(3);
+var thursday = weekday(4);
+var friday = weekday(5);
+var saturday = weekday(6);
+
+var month = newInterval(function(date) {
+ date.setDate(1);
+ date.setHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setMonth(date.getMonth() + step);
+}, function(start, end) {
+ return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;
+}, function(date) {
+ return date.getMonth();
+});
+
+var year = newInterval(function(date) {
+ date.setMonth(0, 1);
+ date.setHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setFullYear(date.getFullYear() + step);
+}, function(start, end) {
+ return end.getFullYear() - start.getFullYear();
+}, function(date) {
+ return date.getFullYear();
+});
+
+// An optimized implementation for this simple case.
+year.every = function(k) {
+ return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
+ date.setFullYear(Math.floor(date.getFullYear() / k) * k);
+ date.setMonth(0, 1);
+ date.setHours(0, 0, 0, 0);
+ }, function(date, step) {
+ date.setFullYear(date.getFullYear() + step * k);
+ });
+};
+
+var utcMinute = newInterval(function(date) {
+ date.setUTCSeconds(0, 0);
+}, function(date, step) {
+ date.setTime(+date + step * durationMinute);
+}, function(start, end) {
+ return (end - start) / durationMinute;
+}, function(date) {
+ return date.getUTCMinutes();
+});
+
+var utcHour = newInterval(function(date) {
+ date.setUTCMinutes(0, 0, 0);
+}, function(date, step) {
+ date.setTime(+date + step * durationHour);
+}, function(start, end) {
+ return (end - start) / durationHour;
+}, function(date) {
+ return date.getUTCHours();
+});
+
+var utcDay = newInterval(function(date) {
+ date.setUTCHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setUTCDate(date.getUTCDate() + step);
+}, function(start, end) {
+ return (end - start) / durationDay;
+}, function(date) {
+ return date.getUTCDate() - 1;
+});
+
+function utcWeekday(i) {
+ return newInterval(function(date) {
+ date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
+ date.setUTCHours(0, 0, 0, 0);
+ }, function(date, step) {
+ date.setUTCDate(date.getUTCDate() + step * 7);
+ }, function(start, end) {
+ return (end - start) / durationWeek;
+ });
+}
+
+var utcSunday = utcWeekday(0);
+var utcMonday = utcWeekday(1);
+var utcTuesday = utcWeekday(2);
+var utcWednesday = utcWeekday(3);
+var utcThursday = utcWeekday(4);
+var utcFriday = utcWeekday(5);
+var utcSaturday = utcWeekday(6);
+
+var utcMonth = newInterval(function(date) {
+ date.setUTCDate(1);
+ date.setUTCHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setUTCMonth(date.getUTCMonth() + step);
+}, function(start, end) {
+ return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
+}, function(date) {
+ return date.getUTCMonth();
+});
+
+var utcYear = newInterval(function(date) {
+ date.setUTCMonth(0, 1);
+ date.setUTCHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setUTCFullYear(date.getUTCFullYear() + step);
+}, function(start, end) {
+ return end.getUTCFullYear() - start.getUTCFullYear();
+}, function(date) {
+ return date.getUTCFullYear();
+});
+
+// An optimized implementation for this simple case.
+utcYear.every = function(k) {
+ return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
+ date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);
+ date.setUTCMonth(0, 1);
+ date.setUTCHours(0, 0, 0, 0);
+ }, function(date, step) {
+ date.setUTCFullYear(date.getUTCFullYear() + step * k);
+ });
+};
+
+function localDate(d) {
+ if (0 <= d.y && d.y < 100) {
+ var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);
+ date.setFullYear(d.y);
+ return date;
+ }
+ return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);
+}
+
+function utcDate(d) {
+ if (0 <= d.y && d.y < 100) {
+ var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));
+ date.setUTCFullYear(d.y);
+ return date;
+ }
+ return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));
+}
+
+function newYear(y) {
+ return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0};
+}
+
+function formatLocale(locale) {
+ var locale_dateTime = locale.dateTime,
+ locale_date = locale.date,
+ locale_time = locale.time,
+ locale_periods = locale.periods,
+ locale_weekdays = locale.days,
+ locale_shortWeekdays = locale.shortDays,
+ locale_months = locale.months,
+ locale_shortMonths = locale.shortMonths;
+
+ var periodRe = formatRe(locale_periods),
+ periodLookup = formatLookup(locale_periods),
+ weekdayRe = formatRe(locale_weekdays),
+ weekdayLookup = formatLookup(locale_weekdays),
+ shortWeekdayRe = formatRe(locale_shortWeekdays),
+ shortWeekdayLookup = formatLookup(locale_shortWeekdays),
+ monthRe = formatRe(locale_months),
+ monthLookup = formatLookup(locale_months),
+ shortMonthRe = formatRe(locale_shortMonths),
+ shortMonthLookup = formatLookup(locale_shortMonths);
+
+ var formats = {
+ "a": formatShortWeekday,
+ "A": formatWeekday,
+ "b": formatShortMonth,
+ "B": formatMonth,
+ "c": null,
+ "d": formatDayOfMonth,
+ "e": formatDayOfMonth,
+ "f": formatMicroseconds,
+ "H": formatHour24,
+ "I": formatHour12,
+ "j": formatDayOfYear,
+ "L": formatMilliseconds,
+ "m": formatMonthNumber,
+ "M": formatMinutes,
+ "p": formatPeriod,
+ "Q": formatUnixTimestamp,
+ "s": formatUnixTimestampSeconds,
+ "S": formatSeconds,
+ "u": formatWeekdayNumberMonday,
+ "U": formatWeekNumberSunday,
+ "V": formatWeekNumberISO,
+ "w": formatWeekdayNumberSunday,
+ "W": formatWeekNumberMonday,
+ "x": null,
+ "X": null,
+ "y": formatYear,
+ "Y": formatFullYear,
+ "Z": formatZone,
+ "%": formatLiteralPercent
+ };
+
+ var utcFormats = {
+ "a": formatUTCShortWeekday,
+ "A": formatUTCWeekday,
+ "b": formatUTCShortMonth,
+ "B": formatUTCMonth,
+ "c": null,
+ "d": formatUTCDayOfMonth,
+ "e": formatUTCDayOfMonth,
+ "f": formatUTCMicroseconds,
+ "H": formatUTCHour24,
+ "I": formatUTCHour12,
+ "j": formatUTCDayOfYear,
+ "L": formatUTCMilliseconds,
+ "m": formatUTCMonthNumber,
+ "M": formatUTCMinutes,
+ "p": formatUTCPeriod,
+ "Q": formatUnixTimestamp,
+ "s": formatUnixTimestampSeconds,
+ "S": formatUTCSeconds,
+ "u": formatUTCWeekdayNumberMonday,
+ "U": formatUTCWeekNumberSunday,
+ "V": formatUTCWeekNumberISO,
+ "w": formatUTCWeekdayNumberSunday,
+ "W": formatUTCWeekNumberMonday,
+ "x": null,
+ "X": null,
+ "y": formatUTCYear,
+ "Y": formatUTCFullYear,
+ "Z": formatUTCZone,
+ "%": formatLiteralPercent
+ };
+
+ var parses = {
+ "a": parseShortWeekday,
+ "A": parseWeekday,
+ "b": parseShortMonth,
+ "B": parseMonth,
+ "c": parseLocaleDateTime,
+ "d": parseDayOfMonth,
+ "e": parseDayOfMonth,
+ "f": parseMicroseconds,
+ "H": parseHour24,
+ "I": parseHour24,
+ "j": parseDayOfYear,
+ "L": parseMilliseconds,
+ "m": parseMonthNumber,
+ "M": parseMinutes,
+ "p": parsePeriod,
+ "Q": parseUnixTimestamp,
+ "s": parseUnixTimestampSeconds,
+ "S": parseSeconds,
+ "u": parseWeekdayNumberMonday,
+ "U": parseWeekNumberSunday,
+ "V": parseWeekNumberISO,
+ "w": parseWeekdayNumberSunday,
+ "W": parseWeekNumberMonday,
+ "x": parseLocaleDate,
+ "X": parseLocaleTime,
+ "y": parseYear,
+ "Y": parseFullYear,
+ "Z": parseZone,
+ "%": parseLiteralPercent
+ };
+
+ // These recursive directive definitions must be deferred.
+ formats.x = newFormat(locale_date, formats);
+ formats.X = newFormat(locale_time, formats);
+ formats.c = newFormat(locale_dateTime, formats);
+ utcFormats.x = newFormat(locale_date, utcFormats);
+ utcFormats.X = newFormat(locale_time, utcFormats);
+ utcFormats.c = newFormat(locale_dateTime, utcFormats);
+
+ function newFormat(specifier, formats) {
+ return function(date) {
+ var string = [],
+ i = -1,
+ j = 0,
+ n = specifier.length,
+ c,
+ pad,
+ format;
+
+ if (!(date instanceof Date)) date = new Date(+date);
+
+ while (++i < n) {
+ if (specifier.charCodeAt(i) === 37) {
+ string.push(specifier.slice(j, i));
+ if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);
+ else pad = c === "e" ? " " : "0";
+ if (format = formats[c]) c = format(date, pad);
+ string.push(c);
+ j = i + 1;
+ }
+ }
+
+ string.push(specifier.slice(j, i));
+ return string.join("");
+ };
+ }
+
+ function newParse(specifier, newDate) {
+ return function(string) {
+ var d = newYear(1900),
+ i = parseSpecifier(d, specifier, string += "", 0),
+ week, day$$1;
+ if (i != string.length) return null;
+
+ // If a UNIX timestamp is specified, return it.
+ if ("Q" in d) return new Date(d.Q);
+
+ // The am-pm flag is 0 for AM, and 1 for PM.
+ if ("p" in d) d.H = d.H % 12 + d.p * 12;
+
+ // Convert day-of-week and week-of-year to day-of-year.
+ if ("V" in d) {
+ if (d.V < 1 || d.V > 53) return null;
+ if (!("w" in d)) d.w = 1;
+ if ("Z" in d) {
+ week = utcDate(newYear(d.y)), day$$1 = week.getUTCDay();
+ week = day$$1 > 4 || day$$1 === 0 ? utcMonday.ceil(week) : utcMonday(week);
+ week = utcDay.offset(week, (d.V - 1) * 7);
+ d.y = week.getUTCFullYear();
+ d.m = week.getUTCMonth();
+ d.d = week.getUTCDate() + (d.w + 6) % 7;
+ } else {
+ week = newDate(newYear(d.y)), day$$1 = week.getDay();
+ week = day$$1 > 4 || day$$1 === 0 ? monday.ceil(week) : monday(week);
+ week = day.offset(week, (d.V - 1) * 7);
+ d.y = week.getFullYear();
+ d.m = week.getMonth();
+ d.d = week.getDate() + (d.w + 6) % 7;
+ }
+ } else if ("W" in d || "U" in d) {
+ if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0;
+ day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay();
+ d.m = 0;
+ d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$$1 + 5) % 7 : d.w + d.U * 7 - (day$$1 + 6) % 7;
+ }
+
+ // If a time zone is specified, all fields are interpreted as UTC and then
+ // offset according to the specified time zone.
+ if ("Z" in d) {
+ d.H += d.Z / 100 | 0;
+ d.M += d.Z % 100;
+ return utcDate(d);
+ }
+
+ // Otherwise, all fields are in local time.
+ return newDate(d);
+ };
+ }
+
+ function parseSpecifier(d, specifier, string, j) {
+ var i = 0,
+ n = specifier.length,
+ m = string.length,
+ c,
+ parse;
+
+ while (i < n) {
+ if (j >= m) return -1;
+ c = specifier.charCodeAt(i++);
+ if (c === 37) {
+ c = specifier.charAt(i++);
+ parse = parses[c in pads ? specifier.charAt(i++) : c];
+ if (!parse || ((j = parse(d, string, j)) < 0)) return -1;
+ } else if (c != string.charCodeAt(j++)) {
+ return -1;
+ }
+ }
+
+ return j;
+ }
+
+ function parsePeriod(d, string, i) {
+ var n = periodRe.exec(string.slice(i));
+ return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseShortWeekday(d, string, i) {
+ var n = shortWeekdayRe.exec(string.slice(i));
+ return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseWeekday(d, string, i) {
+ var n = weekdayRe.exec(string.slice(i));
+ return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseShortMonth(d, string, i) {
+ var n = shortMonthRe.exec(string.slice(i));
+ return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseMonth(d, string, i) {
+ var n = monthRe.exec(string.slice(i));
+ return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseLocaleDateTime(d, string, i) {
+ return parseSpecifier(d, locale_dateTime, string, i);
+ }
+
+ function parseLocaleDate(d, string, i) {
+ return parseSpecifier(d, locale_date, string, i);
+ }
+
+ function parseLocaleTime(d, string, i) {
+ return parseSpecifier(d, locale_time, string, i);
+ }
+
+ function formatShortWeekday(d) {
+ return locale_shortWeekdays[d.getDay()];
+ }
+
+ function formatWeekday(d) {
+ return locale_weekdays[d.getDay()];
+ }
+
+ function formatShortMonth(d) {
+ return locale_shortMonths[d.getMonth()];
+ }
+
+ function formatMonth(d) {
+ return locale_months[d.getMonth()];
+ }
+
+ function formatPeriod(d) {
+ return locale_periods[+(d.getHours() >= 12)];
+ }
+
+ function formatUTCShortWeekday(d) {
+ return locale_shortWeekdays[d.getUTCDay()];
+ }
+
+ function formatUTCWeekday(d) {
+ return locale_weekdays[d.getUTCDay()];
+ }
+
+ function formatUTCShortMonth(d) {
+ return locale_shortMonths[d.getUTCMonth()];
+ }
+
+ function formatUTCMonth(d) {
+ return locale_months[d.getUTCMonth()];
+ }
+
+ function formatUTCPeriod(d) {
+ return locale_periods[+(d.getUTCHours() >= 12)];
+ }
+
+ return {
+ format: function(specifier) {
+ var f = newFormat(specifier += "", formats);
+ f.toString = function() { return specifier; };
+ return f;
+ },
+ parse: function(specifier) {
+ var p = newParse(specifier += "", localDate);
+ p.toString = function() { return specifier; };
+ return p;
+ },
+ utcFormat: function(specifier) {
+ var f = newFormat(specifier += "", utcFormats);
+ f.toString = function() { return specifier; };
+ return f;
+ },
+ utcParse: function(specifier) {
+ var p = newParse(specifier, utcDate);
+ p.toString = function() { return specifier; };
+ return p;
+ }
+ };
+}
+
+var pads = {"-": "", "_": " ", "0": "0"};
+var numberRe = /^\s*\d+/;
+var percentRe = /^%/;
+var requoteRe = /[\\^$*+?|[\]().{}]/g;
+
+function pad$1(value, fill, width) {
+ var sign = value < 0 ? "-" : "",
+ string = (sign ? -value : value) + "",
+ length = string.length;
+ return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
+}
+
+function requote(s) {
+ return s.replace(requoteRe, "\\$&");
+}
+
+function formatRe(names) {
+ return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i");
+}
+
+function formatLookup(names) {
+ var map = {}, i = -1, n = names.length;
+ while (++i < n) map[names[i].toLowerCase()] = i;
+ return map;
+}
+
+function parseWeekdayNumberSunday(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 1));
+ return n ? (d.w = +n[0], i + n[0].length) : -1;
+}
+
+function parseWeekdayNumberMonday(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 1));
+ return n ? (d.u = +n[0], i + n[0].length) : -1;
+}
+
+function parseWeekNumberSunday(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.U = +n[0], i + n[0].length) : -1;
+}
+
+function parseWeekNumberISO(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.V = +n[0], i + n[0].length) : -1;
+}
+
+function parseWeekNumberMonday(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.W = +n[0], i + n[0].length) : -1;
+}
+
+function parseFullYear(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 4));
+ return n ? (d.y = +n[0], i + n[0].length) : -1;
+}
+
+function parseYear(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;
+}
+
+function parseZone(d, string, i) {
+ var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6));
+ return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1;
+}
+
+function parseMonthNumber(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.m = n[0] - 1, i + n[0].length) : -1;
+}
+
+function parseDayOfMonth(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.d = +n[0], i + n[0].length) : -1;
+}
+
+function parseDayOfYear(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 3));
+ return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;
+}
+
+function parseHour24(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.H = +n[0], i + n[0].length) : -1;
+}
+
+function parseMinutes(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.M = +n[0], i + n[0].length) : -1;
+}
+
+function parseSeconds(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.S = +n[0], i + n[0].length) : -1;
+}
+
+function parseMilliseconds(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 3));
+ return n ? (d.L = +n[0], i + n[0].length) : -1;
+}
+
+function parseMicroseconds(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 6));
+ return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;
+}
+
+function parseLiteralPercent(d, string, i) {
+ var n = percentRe.exec(string.slice(i, i + 1));
+ return n ? i + n[0].length : -1;
+}
+
+function parseUnixTimestamp(d, string, i) {
+ var n = numberRe.exec(string.slice(i));
+ return n ? (d.Q = +n[0], i + n[0].length) : -1;
+}
+
+function parseUnixTimestampSeconds(d, string, i) {
+ var n = numberRe.exec(string.slice(i));
+ return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1;
+}
+
+function formatDayOfMonth(d, p) {
+ return pad$1(d.getDate(), p, 2);
+}
+
+function formatHour24(d, p) {
+ return pad$1(d.getHours(), p, 2);
+}
+
+function formatHour12(d, p) {
+ return pad$1(d.getHours() % 12 || 12, p, 2);
+}
+
+function formatDayOfYear(d, p) {
+ return pad$1(1 + day.count(year(d), d), p, 3);
+}
+
+function formatMilliseconds(d, p) {
+ return pad$1(d.getMilliseconds(), p, 3);
+}
+
+function formatMicroseconds(d, p) {
+ return formatMilliseconds(d, p) + "000";
+}
+
+function formatMonthNumber(d, p) {
+ return pad$1(d.getMonth() + 1, p, 2);
+}
+
+function formatMinutes(d, p) {
+ return pad$1(d.getMinutes(), p, 2);
+}
+
+function formatSeconds(d, p) {
+ return pad$1(d.getSeconds(), p, 2);
+}
+
+function formatWeekdayNumberMonday(d) {
+ var day$$1 = d.getDay();
+ return day$$1 === 0 ? 7 : day$$1;
+}
+
+function formatWeekNumberSunday(d, p) {
+ return pad$1(sunday.count(year(d), d), p, 2);
+}
+
+function formatWeekNumberISO(d, p) {
+ var day$$1 = d.getDay();
+ d = (day$$1 >= 4 || day$$1 === 0) ? thursday(d) : thursday.ceil(d);
+ return pad$1(thursday.count(year(d), d) + (year(d).getDay() === 4), p, 2);
+}
+
+function formatWeekdayNumberSunday(d) {
+ return d.getDay();
+}
+
+function formatWeekNumberMonday(d, p) {
+ return pad$1(monday.count(year(d), d), p, 2);
+}
+
+function formatYear(d, p) {
+ return pad$1(d.getFullYear() % 100, p, 2);
+}
+
+function formatFullYear(d, p) {
+ return pad$1(d.getFullYear() % 10000, p, 4);
+}
+
+function formatZone(d) {
+ var z = d.getTimezoneOffset();
+ return (z > 0 ? "-" : (z *= -1, "+"))
+ + pad$1(z / 60 | 0, "0", 2)
+ + pad$1(z % 60, "0", 2);
+}
+
+function formatUTCDayOfMonth(d, p) {
+ return pad$1(d.getUTCDate(), p, 2);
+}
+
+function formatUTCHour24(d, p) {
+ return pad$1(d.getUTCHours(), p, 2);
+}
+
+function formatUTCHour12(d, p) {
+ return pad$1(d.getUTCHours() % 12 || 12, p, 2);
+}
+
+function formatUTCDayOfYear(d, p) {
+ return pad$1(1 + utcDay.count(utcYear(d), d), p, 3);
+}
+
+function formatUTCMilliseconds(d, p) {
+ return pad$1(d.getUTCMilliseconds(), p, 3);
+}
+
+function formatUTCMicroseconds(d, p) {
+ return formatUTCMilliseconds(d, p) + "000";
+}
+
+function formatUTCMonthNumber(d, p) {
+ return pad$1(d.getUTCMonth() + 1, p, 2);
+}
+
+function formatUTCMinutes(d, p) {
+ return pad$1(d.getUTCMinutes(), p, 2);
+}
+
+function formatUTCSeconds(d, p) {
+ return pad$1(d.getUTCSeconds(), p, 2);
+}
+
+function formatUTCWeekdayNumberMonday(d) {
+ var dow = d.getUTCDay();
+ return dow === 0 ? 7 : dow;
+}
+
+function formatUTCWeekNumberSunday(d, p) {
+ return pad$1(utcSunday.count(utcYear(d), d), p, 2);
+}
+
+function formatUTCWeekNumberISO(d, p) {
+ var day$$1 = d.getUTCDay();
+ d = (day$$1 >= 4 || day$$1 === 0) ? utcThursday(d) : utcThursday.ceil(d);
+ return pad$1(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2);
+}
+
+function formatUTCWeekdayNumberSunday(d) {
+ return d.getUTCDay();
+}
+
+function formatUTCWeekNumberMonday(d, p) {
+ return pad$1(utcMonday.count(utcYear(d), d), p, 2);
+}
+
+function formatUTCYear(d, p) {
+ return pad$1(d.getUTCFullYear() % 100, p, 2);
+}
+
+function formatUTCFullYear(d, p) {
+ return pad$1(d.getUTCFullYear() % 10000, p, 4);
+}
+
+function formatUTCZone() {
+ return "+0000";
+}
+
+function formatLiteralPercent() {
+ return "%";
+}
+
+function formatUnixTimestamp(d) {
+ return +d;
+}
+
+function formatUnixTimestampSeconds(d) {
+ return Math.floor(+d / 1000);
+}
+
+var locale$1;
+var timeFormat;
+var timeParse;
+var utcFormat;
+var utcParse;
+
+defaultLocale({
+ dateTime: "%x, %X",
+ date: "%-m/%-d/%Y",
+ time: "%-I:%M:%S %p",
+ periods: ["AM", "PM"],
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+});
+
+function defaultLocale(definition) {
+ locale$1 = formatLocale(definition);
+ timeFormat = locale$1.format;
+ timeParse = locale$1.parse;
+ utcFormat = locale$1.utcFormat;
+ utcParse = locale$1.utcParse;
+ return locale$1;
+}
+
+var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ";
+
+function formatIsoNative(date) {
+ return date.toISOString();
+}
+
+var formatIso = Date.prototype.toISOString
+ ? formatIsoNative
+ : utcFormat(isoSpecifier);
+
+function parseIsoNative(string) {
+ var date = new Date(string);
+ return isNaN(date) ? null : date;
+}
+
+var parseIso = +new Date("2000-01-01T00:00:00.000Z")
+ ? parseIsoNative
+ : utcParse(isoSpecifier);
+
+var read = function(data, schema, dateParse) {
+ schema = schema || {};
+
+ var reader = formats$1(schema.type || 'json');
+ if (!reader) error$1('Unknown data format type: ' + schema.type);
+
+ data = reader(data, schema);
+ if (schema.parse) parse(data, schema.parse, dateParse);
+
+ if (data.hasOwnProperty('columns')) delete data.columns;
+ return data;
+};
+
+function parse(data, types, dateParse) {
+ if (!data.length) return; // early exit for empty data
+
+ dateParse = dateParse || timeParse;
+
+ var fields = data.columns || Object.keys(data[0]),
+ parsers, datum, field$$1, i, j, n, m;
+
+ if (types === 'auto') types = inferTypes(data, fields);
+
+ fields = Object.keys(types);
+ parsers = fields.map(function(field$$1) {
+ var type = types[field$$1],
+ parts, pattern;
+
+ if (type && (type.indexOf('date:') === 0 || type.indexOf('utc:') === 0)) {
+ parts = type.split(/:(.+)?/, 2); // split on first :
+ pattern = parts[1];
+
+ if ((pattern[0] === '\'' && pattern[pattern.length-1] === '\'') ||
+ (pattern[0] === '"' && pattern[pattern.length-1] === '"')) {
+ pattern = pattern.slice(1, -1);
+ }
+
+ return parts[0] === 'utc' ? utcParse(pattern) : dateParse(pattern);
+ }
+
+ if (!typeParsers[type]) {
+ throw Error('Illegal format pattern: ' + field$$1 + ':' + type);
+ }
+
+ return typeParsers[type];
+ });
+
+ for (i=0, n=data.length, m=fields.length; i= 0;) {
+ queue.push(cur = list[i]);
+ if (cur === op) error$1('Cycle detected in dataflow graph.');
+ }
+ }
+ }
+}
+
+/**
+ * Sentinel value indicating pulse propagation should stop.
+ */
+var StopPropagation = {};
+
+// Pulse visit type flags
+var ADD = (1 << 0);
+var REM = (1 << 1);
+var MOD = (1 << 2);
+var ADD_REM = ADD | REM;
+var ADD_MOD = ADD | MOD;
+var ALL = ADD | REM | MOD;
+var REFLOW = (1 << 3);
+var SOURCE = (1 << 4);
+var NO_SOURCE = (1 << 5);
+var NO_FIELDS = (1 << 6);
+
+/**
+ * A Pulse enables inter-operator communication during a run of the
+ * dataflow graph. In addition to the current timestamp, a pulse may also
+ * contain a change-set of added, removed or modified data tuples, as well as
+ * a pointer to a full backing data source. Tuple change sets may not
+ * be fully materialized; for example, to prevent needless array creation
+ * a change set may include larger arrays and corresponding filter functions.
+ * The pulse provides a {@link visit} method to enable proper and efficient
+ * iteration over requested data tuples.
+ *
+ * In addition, each pulse can track modification flags for data tuple fields.
+ * Responsible transform operators should call the {@link modifies} method to
+ * indicate changes to data fields. The {@link modified} method enables
+ * querying of this modification state.
+ *
+ * @constructor
+ * @param {Dataflow} dataflow - The backing dataflow instance.
+ * @param {number} stamp - The current propagation timestamp.
+ * @param {string} [encode] - An optional encoding set name, which is then
+ * accessible as Pulse.encode. Operators can respond to (or ignore) this
+ * setting as appropriate. This parameter can be used in conjunction with
+ * the Encode transform in the vega-encode module.
+ */
+function Pulse(dataflow, stamp, encode) {
+ this.dataflow = dataflow;
+ this.stamp = stamp == null ? -1 : stamp;
+ this.add = [];
+ this.rem = [];
+ this.mod = [];
+ this.fields = null;
+ this.encode = encode || null;
+}
+
+var prototype$4 = Pulse.prototype;
+
+/**
+ * Sentinel value indicating pulse propagation should stop.
+ */
+prototype$4.StopPropagation = StopPropagation;
+
+/**
+ * Boolean flag indicating ADD (added) tuples.
+ */
+prototype$4.ADD = ADD;
+
+/**
+ * Boolean flag indicating REM (removed) tuples.
+ */
+prototype$4.REM = REM;
+
+/**
+ * Boolean flag indicating MOD (modified) tuples.
+ */
+prototype$4.MOD = MOD;
+
+/**
+ * Boolean flag indicating ADD (added) and REM (removed) tuples.
+ */
+prototype$4.ADD_REM = ADD_REM;
+
+/**
+ * Boolean flag indicating ADD (added) and MOD (modified) tuples.
+ */
+prototype$4.ADD_MOD = ADD_MOD;
+
+/**
+ * Boolean flag indicating ADD, REM and MOD tuples.
+ */
+prototype$4.ALL = ALL;
+
+/**
+ * Boolean flag indicating all tuples in a data source
+ * except for the ADD, REM and MOD tuples.
+ */
+prototype$4.REFLOW = REFLOW;
+
+/**
+ * Boolean flag indicating a 'pass-through' to a
+ * backing data source, ignoring ADD, REM and MOD tuples.
+ */
+prototype$4.SOURCE = SOURCE;
+
+/**
+ * Boolean flag indicating that source data should be
+ * suppressed when creating a forked pulse.
+ */
+prototype$4.NO_SOURCE = NO_SOURCE;
+
+/**
+ * Boolean flag indicating that field modifications should be
+ * suppressed when creating a forked pulse.
+ */
+prototype$4.NO_FIELDS = NO_FIELDS;
+
+/**
+ * Creates a new pulse based on the values of this pulse.
+ * The dataflow, time stamp and field modification values are copied over.
+ * By default, new empty ADD, REM and MOD arrays are created.
+ * @param {number} flags - Integer of boolean flags indicating which (if any)
+ * tuple arrays should be copied to the new pulse. The supported flag values
+ * are ADD, REM and MOD. Array references are copied directly: new array
+ * instances are not created.
+ * @return {Pulse} - The forked pulse instance.
+ * @see init
+ */
+prototype$4.fork = function(flags) {
+ return new Pulse(this.dataflow).init(this, flags);
+};
+
+/**
+ * Returns a pulse that adds all tuples from a backing source. This is
+ * useful for cases where operators are added to a dataflow after an
+ * upstream data pipeline has already been processed, ensuring that
+ * new operators can observe all tuples within a stream.
+ * @return {Pulse} - A pulse instance with all source tuples included
+ * in the add array. If the current pulse already has all source
+ * tuples in its add array, it is returned directly. If the current
+ * pulse does not have a backing source, it is returned directly.
+ */
+prototype$4.addAll = function() {
+ var p = this;
+ if (!this.source || this.source.length === this.add.length) {
+ return p;
+ } else {
+ p = new Pulse(this.dataflow).init(this);
+ p.add = p.source;
+ return p;
+ }
+};
+
+/**
+ * Initialize this pulse based on the values of another pulse. This method
+ * is used internally by {@link fork} to initialize a new forked tuple.
+ * The dataflow, time stamp and field modification values are copied over.
+ * By default, new empty ADD, REM and MOD arrays are created.
+ * @param {Pulse} src - The source pulse to copy from.
+ * @param {number} flags - Integer of boolean flags indicating which (if any)
+ * tuple arrays should be copied to the new pulse. The supported flag values
+ * are ADD, REM and MOD. Array references are copied directly: new array
+ * instances are not created. By default, source data arrays are copied
+ * to the new pulse. Use the NO_SOURCE flag to enforce a null source.
+ * @return {Pulse} - Returns this Pulse instance.
+ */
+prototype$4.init = function(src, flags) {
+ var p = this;
+ p.stamp = src.stamp;
+ p.encode = src.encode;
+
+ if (src.fields && !(flags & NO_FIELDS)) {
+ p.fields = src.fields;
+ }
+
+ if (flags & ADD) {
+ p.addF = src.addF;
+ p.add = src.add;
+ } else {
+ p.addF = null;
+ p.add = [];
+ }
+
+ if (flags & REM) {
+ p.remF = src.remF;
+ p.rem = src.rem;
+ } else {
+ p.remF = null;
+ p.rem = [];
+ }
+
+ if (flags & MOD) {
+ p.modF = src.modF;
+ p.mod = src.mod;
+ } else {
+ p.modF = null;
+ p.mod = [];
+ }
+
+ if (flags & NO_SOURCE) {
+ p.srcF = null;
+ p.source = null;
+ } else {
+ p.srcF = src.srcF;
+ p.source = src.source;
+ }
+
+ return p;
+};
+
+/**
+ * Schedules a function to run after pulse propagation completes.
+ * @param {function} func - The function to run.
+ */
+prototype$4.runAfter = function(func) {
+ this.dataflow.runAfter(func);
+};
+
+/**
+ * Indicates if tuples have been added, removed or modified.
+ * @param {number} [flags] - The tuple types (ADD, REM or MOD) to query.
+ * Defaults to ALL, returning true if any tuple type has changed.
+ * @return {boolean} - Returns true if one or more queried tuple types have
+ * changed, false otherwise.
+ */
+prototype$4.changed = function(flags) {
+ var f = flags || ALL;
+ return ((f & ADD) && this.add.length)
+ || ((f & REM) && this.rem.length)
+ || ((f & MOD) && this.mod.length);
+};
+
+/**
+ * Forces a "reflow" of tuple values, such that all tuples in the backing
+ * source are added to the MOD set, unless already present in the ADD set.
+ * @param {boolean} [fork=false] - If true, returns a forked copy of this
+ * pulse, and invokes reflow on that derived pulse.
+ * @return {Pulse} - The reflowed pulse instance.
+ */
+prototype$4.reflow = function(fork) {
+ if (fork) return this.fork(ALL).reflow();
+
+ var len = this.add.length,
+ src = this.source && this.source.length;
+ if (src && src !== len) {
+ this.mod = this.source;
+ if (len) this.filter(MOD, filter(this, ADD));
+ }
+ return this;
+};
+
+/**
+ * Marks one or more data field names as modified to assist dependency
+ * tracking and incremental processing by transform operators.
+ * @param {string|Array} _ - The field(s) to mark as modified.
+ * @return {Pulse} - This pulse instance.
+ */
+prototype$4.modifies = function(_) {
+ var fields = array(_),
+ hash = this.fields || (this.fields = {});
+ fields.forEach(function(f) { hash[f] = true; });
+ return this;
+};
+
+/**
+ * Checks if one or more data fields have been modified during this pulse
+ * propagation timestamp.
+ * @param {string|Array} _ - The field(s) to check for modified.
+ * @return {boolean} - Returns true if any of the provided fields has been
+ * marked as modified, false otherwise.
+ */
+prototype$4.modified = function(_) {
+ var fields = this.fields;
+ return !(this.mod.length && fields) ? false
+ : !arguments.length ? !!fields
+ : isArray(_) ? _.some(function(f) { return fields[f]; })
+ : fields[_];
+};
+
+/**
+ * Adds a filter function to one more tuple sets. Filters are applied to
+ * backing tuple arrays, to determine the actual set of tuples considered
+ * added, removed or modified. They can be used to delay materialization of
+ * a tuple set in order to avoid expensive array copies. In addition, the
+ * filter functions can serve as value transformers: unlike standard predicate
+ * function (which return boolean values), Pulse filters should return the
+ * actual tuple value to process. If a tuple set is already filtered, the
+ * new filter function will be appended into a conjuntive ('and') query.
+ * @param {number} flags - Flags indicating the tuple set(s) to filter.
+ * @param {function(*):object} filter - Filter function that will be applied
+ * to the tuple set array, and should return a data tuple if the value
+ * should be included in the tuple set, and falsy (or null) otherwise.
+ * @return {Pulse} - Returns this pulse instance.
+ */
+prototype$4.filter = function(flags, filter) {
+ var p = this;
+ if (flags & ADD) p.addF = addFilter(p.addF, filter);
+ if (flags & REM) p.remF = addFilter(p.remF, filter);
+ if (flags & MOD) p.modF = addFilter(p.modF, filter);
+ if (flags & SOURCE) p.srcF = addFilter(p.srcF, filter);
+ return p;
+};
+
+function addFilter(a, b) {
+ return a ? function(t,i) { return a(t,i) && b(t,i); } : b;
+}
+
+/**
+ * Materialize one or more tuple sets in this pulse. If the tuple set(s) have
+ * a registered filter function, it will be applied and the tuple set(s) will
+ * be replaced with materialized tuple arrays.
+ * @param {number} flags - Flags indicating the tuple set(s) to materialize.
+ * @return {Pulse} - Returns this pulse instance.
+ */
+prototype$4.materialize = function(flags) {
+ flags = flags || ALL;
+ var p = this;
+ if ((flags & ADD) && p.addF) {
+ p.add = materialize(p.add, p.addF);
+ p.addF = null;
+ }
+ if ((flags & REM) && p.remF) {
+ p.rem = materialize(p.rem, p.remF);
+ p.remF = null;
+ }
+ if ((flags & MOD) && p.modF) {
+ p.mod = materialize(p.mod, p.modF);
+ p.modF = null;
+ }
+ if ((flags & SOURCE) && p.srcF) {
+ p.source = p.source.filter(p.srcF);
+ p.srcF = null;
+ }
+ return p;
+};
+
+function materialize(data, filter) {
+ var out = [];
+ visitArray(data, filter, function(_) { out.push(_); });
+ return out;
+}
+
+function filter(pulse, flags) {
+ var map = {};
+ pulse.visit(flags, function(t) { map[tupleid(t)] = 1; });
+ return function(t) { return map[tupleid(t)] ? null : t; };
+}
+
+/**
+ * Visit one or more tuple sets in this pulse.
+ * @param {number} flags - Flags indicating the tuple set(s) to visit.
+ * Legal values are ADD, REM, MOD and SOURCE (if a backing data source
+ * has been set).
+ * @param {function(object):*} - Visitor function invoked per-tuple.
+ * @return {Pulse} - Returns this pulse instance.
+ */
+prototype$4.visit = function(flags, visitor) {
+ var p = this, v = visitor, src, sum;
+
+ if (flags & SOURCE) {
+ visitArray(p.source, p.srcF, v);
+ return p;
+ }
+
+ if (flags & ADD) visitArray(p.add, p.addF, v);
+ if (flags & REM) visitArray(p.rem, p.remF, v);
+ if (flags & MOD) visitArray(p.mod, p.modF, v);
+
+ if ((flags & REFLOW) && (src = p.source)) {
+ sum = p.add.length + p.mod.length;
+ if (sum === src.length) {
+ // do nothing
+ } else if (sum) {
+ visitArray(src, filter(p, ADD_MOD), v);
+ } else {
+ // if no add/rem/mod tuples, visit source
+ visitArray(src, p.srcF, v);
+ }
+ }
+
+ return p;
+};
+
+/**
+ * Represents a set of multiple pulses. Used as input for operators
+ * that accept multiple pulses at a time. Contained pulses are
+ * accessible via the public "pulses" array property. This pulse doe
+ * not carry added, removed or modified tuples directly. However,
+ * the visit method can be used to traverse all such tuples contained
+ * in sub-pulses with a timestamp matching this parent multi-pulse.
+ * @constructor
+ * @param {Dataflow} dataflow - The backing dataflow instance.
+ * @param {number} stamp - The timestamp.
+ * @param {Array} pulses - The sub-pulses for this multi-pulse.
+ */
+function MultiPulse(dataflow, stamp, pulses, encode) {
+ var p = this,
+ c = 0,
+ pulse, hash, i, n, f;
+
+ this.dataflow = dataflow;
+ this.stamp = stamp;
+ this.fields = null;
+ this.encode = encode || null;
+ this.pulses = pulses;
+
+ for (i=0, n=pulses.length; i= Info) {
+ dt = Date.now();
+ df.debug('-- START PROPAGATION (' + df._clock + ') -----');
+ }
+
+ // initialize queue, reset touched operators
+ df._touched.forEach(function(op) { df._enqueue(op, true); });
+ df._touched = UniqueList(id);
+
+ try {
+ while (df._heap.size() > 0) {
+ op = df._heap.pop();
+
+ // re-queue if rank changes
+ if (op.rank !== op.qrank) { df._enqueue(op, true); continue; }
+
+ // otherwise, evaluate the operator
+ next = op.run(df._getPulse(op, encode));
+
+ if (level >= Debug) {
+ df.debug(op.id, next === StopPropagation ? 'STOP' : next, op);
+ }
+
+ // propagate the pulse
+ if (next !== StopPropagation) {
+ df._pulse = next;
+ if (op._targets) op._targets.forEach(function(op) { df._enqueue(op); });
+ }
+
+ // increment visit counter
+ ++count;
+ }
+ } catch (err) {
+ error = err;
+ }
+
+ // reset pulse map
+ df._pulses = {};
+ df._pulse = null;
+
+ if (level >= Info) {
+ dt = Date.now() - dt;
+ df.info('> Pulse ' + df._clock + ': ' + count + ' operators; ' + dt + 'ms');
+ }
+
+ if (error) {
+ df._postrun = [];
+ df.error(error);
+ }
+
+ if (df._onrun) {
+ try { df._onrun(df, count, error); } catch (err) { df.error(err); }
+ }
+
+ // invoke callbacks queued via runAfter
+ if (df._postrun.length) {
+ var postrun = df._postrun;
+ df._postrun = [];
+ postrun
+ .sort(function(a, b) { return b.priority - a.priority; })
+ .forEach(function(_) { invokeCallback(df, _.callback); });
+ }
+
+ return count;
+}
+
+function invokeCallback(df, callback) {
+ try { callback(df); } catch (err) { df.error(err); }
+}
+
+/**
+ * Runs the dataflow and returns a Promise that resolves when the
+ * propagation cycle completes. The standard run method may exit early
+ * if there are pending data loading operations. In contrast, this
+ * method returns a Promise to allow callers to receive notification
+ * when dataflow evaluation completes.
+ * @return {Promise} - A promise that resolves to this dataflow.
+ */
+function runAsync() {
+ return this._pending || Promise.resolve(this.run());
+}
+
+/**
+ * Schedules a callback function to be invoked after the current pulse
+ * propagation completes. If no propagation is currently occurring,
+ * the function is invoked immediately.
+ * @param {function(Dataflow)} callback - The callback function to run.
+ * The callback will be invoked with this Dataflow instance as its
+ * sole argument.
+ * @param {boolean} enqueue - A boolean flag indicating that the
+ * callback should be queued up to run after the next propagation
+ * cycle, suppressing immediate invocation when propagation is not
+ * currently occurring.
+ */
+function runAfter(callback, enqueue, priority) {
+ if (this._pulse || enqueue) {
+ // pulse propagation is currently running, queue to run after
+ this._postrun.push({
+ priority: priority || 0,
+ callback: callback
+ });
+ } else {
+ // pulse propagation already complete, invoke immediately
+ invokeCallback(this, callback);
+ }
+}
+
+/**
+ * Enqueue an operator into the priority queue for evaluation. The operator
+ * will be enqueued if it has no registered pulse for the current cycle, or if
+ * the force argument is true. Upon enqueue, this method also sets the
+ * operator's qrank to the current rank value.
+ * @param {Operator} op - The operator to enqueue.
+ * @param {boolean} [force] - A flag indicating if the operator should be
+ * forceably added to the queue, even if it has already been previously
+ * enqueued during the current pulse propagation. This is useful when the
+ * dataflow graph is dynamically modified and the operator rank changes.
+ */
+function enqueue(op, force) {
+ var p = !this._pulses[op.id];
+ if (p) this._pulses[op.id] = this._pulse;
+ if (p || force) {
+ op.qrank = op.rank;
+ this._heap.push(op);
+ }
+}
+
+/**
+ * Provide a correct pulse for evaluating an operator. If the operator has an
+ * explicit source operator, we will try to pull the pulse(s) from it.
+ * If there is an array of source operators, we build a multi-pulse.
+ * Otherwise, we return a current pulse with correct source data.
+ * If the pulse is the pulse map has an explicit target set, we use that.
+ * Else if the pulse on the upstream source operator is current, we use that.
+ * Else we use the pulse from the pulse map, but copy the source tuple array.
+ * @param {Operator} op - The operator for which to get an input pulse.
+ * @param {string} [encode] - An (optional) encoding set name with which to
+ * annotate the returned pulse. See {@link run} for more information.
+ */
+function getPulse(op, encode) {
+ var s = op.source,
+ stamp = this._clock,
+ p;
+
+ if (s && isArray(s)) {
+ p = s.map(function(_) { return _.pulse; });
+ return new MultiPulse(this, stamp, p, encode);
+ }
+
+ p = this._pulses[op.id];
+ if (s) {
+ s = s.pulse;
+ if (!s || s === StopPropagation) {
+ p.source = [];
+ } else if (s.stamp === stamp && p.target !== op) {
+ p = s;
+ } else {
+ p.source = s.source;
+ }
+ }
+
+ return p;
+}
+
+var NO_OPT = {skip: false, force: false};
+
+/**
+ * Touches an operator, scheduling it to be evaluated. If invoked outside of
+ * a pulse propagation, the operator will be evaluated the next time this
+ * dataflow is run. If invoked in the midst of pulse propagation, the operator
+ * will be queued for evaluation if and only if the operator has not yet been
+ * evaluated on the current propagation timestamp.
+ * @param {Operator} op - The operator to touch.
+ * @param {object} [options] - Additional options hash.
+ * @param {boolean} [options.skip] - If true, the operator will
+ * be skipped: it will not be evaluated, but its dependents will be.
+ * @return {Dataflow}
+ */
+function touch(op, options) {
+ var opt = options || NO_OPT;
+ if (this._pulse) {
+ // if in midst of propagation, add to priority queue
+ this._enqueue(op);
+ } else {
+ // otherwise, queue for next propagation
+ this._touched.add(op);
+ }
+ if (opt.skip) op.skip(true);
+ return this;
+}
+
+/**
+ * Updates the value of the given operator.
+ * @param {Operator} op - The operator to update.
+ * @param {*} value - The value to set.
+ * @param {object} [options] - Additional options hash.
+ * @param {boolean} [options.force] - If true, the operator will
+ * be re-evaluated even if its value has not changed.
+ * @param {boolean} [options.skip] - If true, the operator will
+ * be skipped: it will not be evaluated, but its dependents will be.
+ * @return {Dataflow}
+ */
+function update(op, value, options) {
+ var opt = options || NO_OPT;
+ if (op.set(value) || opt.force) {
+ this.touch(op, opt);
+ }
+ return this;
+}
+
+/**
+ * Pulses an operator with a changeset of tuples. If invoked outside of
+ * a pulse propagation, the pulse will be applied the next time this
+ * dataflow is run. If invoked in the midst of pulse propagation, the pulse
+ * will be added to the set of active pulses and will be applied if and
+ * only if the target operator has not yet been evaluated on the current
+ * propagation timestamp.
+ * @param {Operator} op - The operator to pulse.
+ * @param {ChangeSet} value - The tuple changeset to apply.
+ * @param {object} [options] - Additional options hash.
+ * @param {boolean} [options.skip] - If true, the operator will
+ * be skipped: it will not be evaluated, but its dependents will be.
+ * @return {Dataflow}
+ */
+function pulse(op, changeset, options) {
+ this.touch(op, options || NO_OPT);
+
+ var p = new Pulse(this, this._clock + (this._pulse ? 0 : 1)),
+ t = op.pulse && op.pulse.source || [];
+ p.target = op;
+ this._pulses[op.id] = changeset.pulse(p, t);
+
+ return this;
+}
+
+function Heap(comparator) {
+ this.cmp = comparator;
+ this.nodes = [];
+}
+
+var prototype$6 = Heap.prototype;
+
+prototype$6.size = function() {
+ return this.nodes.length;
+};
+
+prototype$6.clear = function() {
+ this.nodes = [];
+ return this;
+};
+
+prototype$6.peek = function() {
+ return this.nodes[0];
+};
+
+prototype$6.push = function(x) {
+ var array = this.nodes;
+ array.push(x);
+ return siftdown(array, 0, array.length-1, this.cmp);
+};
+
+prototype$6.pop = function() {
+ var array = this.nodes,
+ last = array.pop(),
+ item;
+
+ if (array.length) {
+ item = array[0];
+ array[0] = last;
+ siftup(array, 0, this.cmp);
+ } else {
+ item = last;
+ }
+ return item;
+};
+
+prototype$6.replace = function(item) {
+ var array = this.nodes,
+ retval = array[0];
+ array[0] = item;
+ siftup(array, 0, this.cmp);
+ return retval;
+};
+
+prototype$6.pushpop = function(item) {
+ var array = this.nodes, ref = array[0];
+ if (array.length && this.cmp(ref, item) < 0) {
+ array[0] = item;
+ item = ref;
+ siftup(array, 0, this.cmp);
+ }
+ return item;
+};
+
+function siftdown(array, start, idx, cmp) {
+ var item, parent, pidx;
+
+ item = array[idx];
+ while (idx > start) {
+ pidx = (idx - 1) >> 1;
+ parent = array[pidx];
+ if (cmp(item, parent) < 0) {
+ array[idx] = parent;
+ idx = pidx;
+ continue;
+ }
+ break;
+ }
+ return (array[idx] = item);
+}
+
+function siftup(array, idx, cmp) {
+ var start = idx,
+ end = array.length,
+ item = array[idx],
+ cidx = 2 * idx + 1, ridx;
+
+ while (cidx < end) {
+ ridx = cidx + 1;
+ if (ridx < end && cmp(array[cidx], array[ridx]) >= 0) {
+ cidx = ridx;
+ }
+ array[idx] = array[cidx];
+ idx = cidx;
+ cidx = 2 * idx + 1;
+ }
+ array[idx] = item;
+ return siftdown(array, start, idx, cmp);
+}
+
+/**
+ * A dataflow graph for reactive processing of data streams.
+ * @constructor
+ */
+function Dataflow() {
+ this._log = logger();
+ this.logLevel(Error$1);
+
+ this._clock = 0;
+ this._rank = 0;
+ try {
+ this._loader = loader();
+ } catch (e) {
+ // do nothing if loader module is unavailable
+ }
+
+ this._touched = UniqueList(id);
+ this._pulses = {};
+ this._pulse = null;
+
+ this._heap = new Heap(function(a, b) { return a.qrank - b.qrank; });
+ this._postrun = [];
+}
+
+var prototype = Dataflow.prototype;
+
+/**
+ * The current timestamp of this dataflow. This value reflects the
+ * timestamp of the previous dataflow run. The dataflow is initialized
+ * with a stamp value of 0. The initial run of the dataflow will have
+ * a timestap of 1, and so on. This value will match the
+ * {@link Pulse.stamp} property.
+ * @return {number} - The current timestamp value.
+ */
+prototype.stamp = function() {
+ return this._clock;
+};
+
+/**
+ * Gets or sets the loader instance to use for data file loading. A
+ * loader object must provide a "load" method for loading files and a
+ * "sanitize" method for checking URL/filename validity. Both methods
+ * should accept a URI and options hash as arguments, and return a Promise
+ * that resolves to the loaded file contents (load) or a hash containing
+ * sanitized URI data with the sanitized url assigned to the "href" property
+ * (sanitize).
+ * @param {object} _ - The loader instance to use.
+ * @return {object|Dataflow} - If no arguments are provided, returns
+ * the current loader instance. Otherwise returns this Dataflow instance.
+ */
+prototype.loader = function(_) {
+ if (arguments.length) {
+ this._loader = _;
+ return this;
+ } else {
+ return this._loader;
+ }
+};
+
+/**
+ * Empty entry threshold for garbage cleaning. Map data structures will
+ * perform cleaning once the number of empty entries exceeds this value.
+ */
+prototype.cleanThreshold = 1e4;
+
+// OPERATOR REGISTRATION
+prototype.add = add;
+prototype.connect = connect;
+prototype.rank = rank;
+prototype.rerank = rerank;
+
+// OPERATOR UPDATES
+prototype.pulse = pulse;
+prototype.touch = touch;
+prototype.update = update;
+prototype.changeset = changeset;
+
+// DATA LOADING
+prototype.ingest = ingest$1;
+prototype.request = request;
+
+// EVENT HANDLING
+prototype.events = events;
+prototype.on = on;
+
+// PULSE PROPAGATION
+prototype.run = run;
+prototype.runAsync = runAsync;
+prototype.runAfter = runAfter;
+prototype._enqueue = enqueue;
+prototype._getPulse = getPulse;
+
+// LOGGING AND ERROR HANDLING
+
+function logMethod(method) {
+ return function() {
+ return this._log[method].apply(this, arguments);
+ };
+}
+
+/**
+ * Logs an error message. By default, logged messages are written to console
+ * output. The message will only be logged if the current log level is high
+ * enough to permit error messages.
+ */
+prototype.error = logMethod('error');
+
+/**
+ * Logs a warning message. By default, logged messages are written to console
+ * output. The message will only be logged if the current log level is high
+ * enough to permit warning messages.
+ */
+prototype.warn = logMethod('warn');
+
+/**
+ * Logs a information message. By default, logged messages are written to
+ * console output. The message will only be logged if the current log level is
+ * high enough to permit information messages.
+ */
+prototype.info = logMethod('info');
+
+/**
+ * Logs a debug message. By default, logged messages are written to console
+ * output. The message will only be logged if the current log level is high
+ * enough to permit debug messages.
+ */
+prototype.debug = logMethod('debug');
+
+/**
+ * Get or set the current log level. If an argument is provided, it
+ * will be used as the new log level.
+ * @param {number} [level] - Should be one of None, Warn, Info
+ * @return {number} - The current log level.
+ */
+prototype.logLevel = logMethod('level');
+
+/**
+ * Abstract class for operators that process data tuples.
+ * Subclasses must provide a {@link transform} method for operator processing.
+ * @constructor
+ * @param {*} [init] - The initial value for this operator.
+ * @param {object} [params] - The parameters for this operator.
+ * @param {Operator} [source] - The operator from which to receive pulses.
+ */
+function Transform(init, params) {
+ Operator.call(this, init, null, params);
+}
+
+var prototype$7 = inherits(Transform, Operator);
+
+/**
+ * Overrides {@link Operator.evaluate} for transform operators.
+ * Internally, this method calls {@link evaluate} to perform processing.
+ * If {@link evaluate} returns a falsy value, the input pulse is returned.
+ * This method should NOT be overridden, instead overrride {@link evaluate}.
+ * @param {Pulse} pulse - the current dataflow pulse.
+ * @return the output pulse for this operator (or StopPropagation)
+ */
+prototype$7.run = function(pulse) {
+ if (pulse.stamp <= this.stamp) return pulse.StopPropagation;
+
+ var rv;
+ if (this.skip()) {
+ this.skip(false);
+ } else {
+ rv = this.evaluate(pulse);
+ }
+ rv = rv || pulse;
+
+ if (rv !== pulse.StopPropagation) this.pulse = rv;
+ this.stamp = pulse.stamp;
+
+ return rv;
+};
+
+/**
+ * Overrides {@link Operator.evaluate} for transform operators.
+ * Marshalls parameter values and then invokes {@link transform}.
+ * @param {Pulse} pulse - the current dataflow pulse.
+ * @return {Pulse} The output pulse (or StopPropagation). A falsy return
+ value (including undefined) will let the input pulse pass through.
+ */
+prototype$7.evaluate = function(pulse) {
+ var params = this.marshall(pulse.stamp),
+ out = this.transform(params, pulse);
+ params.clear();
+ return out;
+};
+
+/**
+ * Process incoming pulses.
+ * Subclasses should override this method to implement transforms.
+ * @param {Parameters} _ - The operator parameter values.
+ * @param {Pulse} pulse - The current dataflow pulse.
+ * @return {Pulse} The output pulse (or StopPropagation). A falsy return
+ * value (including undefined) will let the input pulse pass through.
+ */
+prototype$7.transform = function() {};
+
+var transforms = {};
+
+function definition(type) {
+ var t = transform$1(type);
+ return t && t.Definition || null;
+}
+
+function transform$1(type) {
+ type = type && type.toLowerCase();
+ return transforms.hasOwnProperty(type) ? transforms[type] : null;
+}
+
+// Utilities
+
+function multikey(f) {
+ return function(x) {
+ var n = f.length,
+ i = 1,
+ k = String(f[0](x));
+
+ for (; i 1 ? this.dev / (this.valid-1) : 0',
+ req: ['mean'], idx: 1
+ }),
+ 'variancep': measure({
+ name: 'variancep',
+ set: 'this.valid > 1 ? this.dev / this.valid : 0',
+ req: ['variance'], idx: 2
+ }),
+ 'stdev': measure({
+ name: 'stdev',
+ set: 'this.valid > 1 ? Math.sqrt(this.dev / (this.valid-1)) : 0',
+ req: ['variance'], idx: 2
+ }),
+ 'stdevp': measure({
+ name: 'stdevp',
+ set: 'this.valid > 1 ? Math.sqrt(this.dev / this.valid) : 0',
+ req: ['variance'], idx: 2
+ }),
+ 'stderr': measure({
+ name: 'stderr',
+ set: 'this.valid > 1 ? Math.sqrt(this.dev / (this.valid * (this.valid-1))) : 0',
+ req: ['variance'], idx: 2
+ }),
+ 'distinct': measure({
+ name: 'distinct',
+ set: 'cell.data.distinct(this.get)',
+ req: ['values'], idx: 3
+ }),
+ 'ci0': measure({
+ name: 'ci0',
+ set: 'cell.data.ci0(this.get)',
+ req: ['values'], idx: 3
+ }),
+ 'ci1': measure({
+ name: 'ci1',
+ set: 'cell.data.ci1(this.get)',
+ req: ['values'], idx: 3
+ }),
+ 'median': measure({
+ name: 'median',
+ set: 'cell.data.q2(this.get)',
+ req: ['values'], idx: 3
+ }),
+ 'q1': measure({
+ name: 'q1',
+ set: 'cell.data.q1(this.get)',
+ req: ['values'], idx: 3
+ }),
+ 'q3': measure({
+ name: 'q3',
+ set: 'cell.data.q3(this.get)',
+ req: ['values'], idx: 3
+ }),
+ 'argmin': measure({
+ name: 'argmin',
+ init: 'this.argmin = null;',
+ add: 'if (v < this.min) this.argmin = t;',
+ rem: 'if (v <= this.min) this.argmin = null;',
+ set: 'this.argmin || cell.data.argmin(this.get)',
+ req: ['min'], str: ['values'], idx: 3
+ }),
+ 'argmax': measure({
+ name: 'argmax',
+ init: 'this.argmax = null;',
+ add: 'if (v > this.max) this.argmax = t;',
+ rem: 'if (v >= this.max) this.argmax = null;',
+ set: 'this.argmax || cell.data.argmax(this.get)',
+ req: ['max'], str: ['values'], idx: 3
+ }),
+ 'min': measure({
+ name: 'min',
+ init: 'this.min = null;',
+ add: 'if (v < this.min || this.min === null) this.min = v;',
+ rem: 'if (v <= this.min) this.min = NaN;',
+ set: 'this.min = (isNaN(this.min) ? cell.data.min(this.get) : this.min)',
+ str: ['values'], idx: 4
+ }),
+ 'max': measure({
+ name: 'max',
+ init: 'this.max = null;',
+ add: 'if (v > this.max || this.max === null) this.max = v;',
+ rem: 'if (v >= this.max) this.max = NaN;',
+ set: 'this.max = (isNaN(this.max) ? cell.data.max(this.get) : this.max)',
+ str: ['values'], idx: 4
+ })
+};
+
+var ValidAggregateOps = Object.keys(AggregateOps);
+
+function createMeasure(op, name) {
+ return AggregateOps[op](name);
+}
+
+function measure(base) {
+ return function(out) {
+ var m = extend({init:'', add:'', rem:'', idx:0}, base);
+ m.out = out || base.name;
+ return m;
+ };
+}
+
+function compareIndex(a, b) {
+ return a.idx - b.idx;
+}
+
+function resolve(agg, stream) {
+ function collect(m, a) {
+ function helper(r) { if (!m[r]) collect(m, m[r] = AggregateOps[r]()); }
+ if (a.req) a.req.forEach(helper);
+ if (stream && a.str) a.str.forEach(helper);
+ return m;
+ }
+ var map = agg.reduce(
+ collect,
+ agg.reduce(function(m, a) {
+ m[a.name] = a;
+ return m;
+ }, {})
+ );
+ var values = [], key$$1;
+ for (key$$1 in map) values.push(map[key$$1]);
+ return values.sort(compareIndex);
+}
+
+function compileMeasures(agg, field$$1) {
+ var get = field$$1 || identity,
+ all = resolve(agg, true), // assume streaming removes may occur
+ init = 'var cell = this.cell; this.valid = 0; this.missing = 0;',
+ ctr = 'this.cell = cell; this.init();',
+ add = 'if(v==null){++this.missing; return;} if(v!==v) return; ++this.valid;',
+ rem = 'if(v==null){--this.missing; return;} if(v!==v) return; --this.valid;',
+ set = 'var cell = this.cell;';
+
+ all.forEach(function(a) {
+ init += a.init;
+ add += a.add;
+ rem += a.rem;
+ });
+ agg.slice().sort(compareIndex).forEach(function(a) {
+ set += 't[\'' + a.out + '\']=' + a.set + ';';
+ });
+ set += 'return t;';
+
+ ctr = Function('cell', ctr);
+ ctr.prototype.init = Function(init);
+ ctr.prototype.add = Function('v', 't', add);
+ ctr.prototype.rem = Function('v', 't', rem);
+ ctr.prototype.set = Function('t', set);
+ ctr.prototype.get = get;
+ ctr.fields = agg.map(function(_) { return _.out; });
+ return ctr;
+}
+
+var bin = function(_) {
+ // determine range
+ var maxb = _.maxbins || 20,
+ base = _.base || 10,
+ logb = Math.log(base),
+ div = _.divide || [5, 2],
+ min = _.extent[0],
+ max = _.extent[1],
+ span = max - min,
+ step, level, minstep, precision, v, i, n, eps;
+
+ if (_.step) {
+ // if step size is explicitly given, use that
+ step = _.step;
+ } else if (_.steps) {
+ // if provided, limit choice to acceptable step sizes
+ v = span / maxb;
+ for (i=0, n=_.steps.length; i < n && _.steps[i] < v; ++i);
+ step = _.steps[Math.max(0, i-1)];
+ } else {
+ // else use span to determine step size
+ level = Math.ceil(Math.log(maxb) / logb);
+ minstep = _.minstep || 0;
+ step = Math.max(
+ minstep,
+ Math.pow(base, Math.round(Math.log(span) / logb) - level)
+ );
+
+ // increase step size if too many bins
+ while (Math.ceil(span/step) > maxb) { step *= base; }
+
+ // decrease step size if allowed
+ for (i=0, n=div.length; i= minstep && span / v <= maxb) step = v;
+ }
+ }
+
+ // update precision, min and max
+ v = Math.log(step);
+ precision = v >= 0 ? 0 : ~~(-v / logb) + 1;
+ eps = Math.pow(base, -precision - 1);
+ if (_.nice || _.nice === undefined) {
+ v = Math.floor(min / step + eps) * step;
+ min = min < v ? v - step : v;
+ max = Math.ceil(max / step) * step;
+ }
+
+ return {
+ start: min,
+ stop: max,
+ step: step
+ };
+};
+
+var numbers = function(array, f) {
+ var numbers = [],
+ n = array.length,
+ i = -1, a;
+
+ if (f == null) {
+ while (++i < n) if (!isNaN(a = number(array[i]))) numbers.push(a);
+ } else {
+ while (++i < n) if (!isNaN(a = number(f(array[i], i, array)))) numbers.push(a);
+ }
+ return numbers;
+};
+
+function number(x) {
+ return x === null ? NaN : +x;
+}
+
+exports.random = Math.random;
+
+function setRandom(r) {
+ exports.random = r;
+}
+
+var ascending = function(a, b) {
+ return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
+};
+
+var bisector = function(compare) {
+ if (compare.length === 1) compare = ascendingComparator(compare);
+ return {
+ left: function(a, x, lo, hi) {
+ if (lo == null) lo = 0;
+ if (hi == null) hi = a.length;
+ while (lo < hi) {
+ var mid = lo + hi >>> 1;
+ if (compare(a[mid], x) < 0) lo = mid + 1;
+ else hi = mid;
+ }
+ return lo;
+ },
+ right: function(a, x, lo, hi) {
+ if (lo == null) lo = 0;
+ if (hi == null) hi = a.length;
+ while (lo < hi) {
+ var mid = lo + hi >>> 1;
+ if (compare(a[mid], x) > 0) hi = mid;
+ else lo = mid + 1;
+ }
+ return lo;
+ }
+ };
+};
+
+function ascendingComparator(f) {
+ return function(d, x) {
+ return ascending(f(d), x);
+ };
+}
+
+var ascendingBisect = bisector(ascending);
+var bisectRight = ascendingBisect.right;
+var bisectLeft = ascendingBisect.left;
+
+function pair(a, b) {
+ return [a, b];
+}
+
+var number$1 = function(x) {
+ return x === null ? NaN : +x;
+};
+
+var variance = function(values, valueof) {
+ var n = values.length,
+ m = 0,
+ i = -1,
+ mean = 0,
+ value,
+ delta,
+ sum = 0;
+
+ if (valueof == null) {
+ while (++i < n) {
+ if (!isNaN(value = number$1(values[i]))) {
+ delta = value - mean;
+ mean += delta / ++m;
+ sum += delta * (value - mean);
+ }
+ }
+ }
+
+ else {
+ while (++i < n) {
+ if (!isNaN(value = number$1(valueof(values[i], i, values)))) {
+ delta = value - mean;
+ mean += delta / ++m;
+ sum += delta * (value - mean);
+ }
+ }
+ }
+
+ if (m > 1) return sum / (m - 1);
+};
+
+var extent = function(values, valueof) {
+ var n = values.length,
+ i = -1,
+ value,
+ min,
+ max;
+
+ if (valueof == null) {
+ while (++i < n) { // Find the first comparable value.
+ if ((value = values[i]) != null && value >= value) {
+ min = max = value;
+ while (++i < n) { // Compare the remaining values.
+ if ((value = values[i]) != null) {
+ if (min > value) min = value;
+ if (max < value) max = value;
+ }
+ }
+ }
+ }
+ }
+
+ else {
+ while (++i < n) { // Find the first comparable value.
+ if ((value = valueof(values[i], i, values)) != null && value >= value) {
+ min = max = value;
+ while (++i < n) { // Compare the remaining values.
+ if ((value = valueof(values[i], i, values)) != null) {
+ if (min > value) min = value;
+ if (max < value) max = value;
+ }
+ }
+ }
+ }
+ }
+
+ return [min, max];
+};
+
+var identity$2 = function(x) {
+ return x;
+};
+
+var sequence = function(start, stop, step) {
+ start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
+
+ var i = -1,
+ n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
+ range = new Array(n);
+
+ while (++i < n) {
+ range[i] = start + i * step;
+ }
+
+ return range;
+};
+
+var e10 = Math.sqrt(50);
+var e5 = Math.sqrt(10);
+var e2$1 = Math.sqrt(2);
+
+var ticks = function(start, stop, count) {
+ var reverse,
+ i = -1,
+ n,
+ ticks,
+ step;
+
+ stop = +stop, start = +start, count = +count;
+ if (start === stop && count > 0) return [start];
+ if (reverse = stop < start) n = start, start = stop, stop = n;
+ if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
+
+ if (step > 0) {
+ start = Math.ceil(start / step);
+ stop = Math.floor(stop / step);
+ ticks = new Array(n = Math.ceil(stop - start + 1));
+ while (++i < n) ticks[i] = (start + i) * step;
+ } else {
+ start = Math.floor(start * step);
+ stop = Math.ceil(stop * step);
+ ticks = new Array(n = Math.ceil(start - stop + 1));
+ while (++i < n) ticks[i] = (start - i) / step;
+ }
+
+ if (reverse) ticks.reverse();
+
+ return ticks;
+};
+
+function tickIncrement(start, stop, count) {
+ var step = (stop - start) / Math.max(0, count),
+ power = Math.floor(Math.log(step) / Math.LN10),
+ error = step / Math.pow(10, power);
+ return power >= 0
+ ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2$1 ? 2 : 1) * Math.pow(10, power)
+ : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2$1 ? 2 : 1);
+}
+
+function tickStep(start, stop, count) {
+ var step0 = Math.abs(stop - start) / Math.max(0, count),
+ step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
+ error = step0 / step1;
+ if (error >= e10) step1 *= 10;
+ else if (error >= e5) step1 *= 5;
+ else if (error >= e2$1) step1 *= 2;
+ return stop < start ? -step1 : step1;
+}
+
+var thresholdSturges = function(values) {
+ return Math.ceil(Math.log(values.length) / Math.LN2) + 1;
+};
+
+var threshold = function(values, p, valueof) {
+ if (valueof == null) valueof = number$1;
+ if (!(n = values.length)) return;
+ if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);
+ if (p >= 1) return +valueof(values[n - 1], n - 1, values);
+ var n,
+ i = (n - 1) * p,
+ i0 = Math.floor(i),
+ value0 = +valueof(values[i0], i0, values),
+ value1 = +valueof(values[i0 + 1], i0 + 1, values);
+ return value0 + (value1 - value0) * (i - i0);
+};
+
+var max = function(values, valueof) {
+ var n = values.length,
+ i = -1,
+ value,
+ max;
+
+ if (valueof == null) {
+ while (++i < n) { // Find the first comparable value.
+ if ((value = values[i]) != null && value >= value) {
+ max = value;
+ while (++i < n) { // Compare the remaining values.
+ if ((value = values[i]) != null && value > max) {
+ max = value;
+ }
+ }
+ }
+ }
+ }
+
+ else {
+ while (++i < n) { // Find the first comparable value.
+ if ((value = valueof(values[i], i, values)) != null && value >= value) {
+ max = value;
+ while (++i < n) { // Compare the remaining values.
+ if ((value = valueof(values[i], i, values)) != null && value > max) {
+ max = value;
+ }
+ }
+ }
+ }
+ }
+
+ return max;
+};
+
+var mean = function(values, valueof) {
+ var n = values.length,
+ m = n,
+ i = -1,
+ value,
+ sum = 0;
+
+ if (valueof == null) {
+ while (++i < n) {
+ if (!isNaN(value = number$1(values[i]))) sum += value;
+ else --m;
+ }
+ }
+
+ else {
+ while (++i < n) {
+ if (!isNaN(value = number$1(valueof(values[i], i, values)))) sum += value;
+ else --m;
+ }
+ }
+
+ if (m) return sum / m;
+};
+
+var median = function(values, valueof) {
+ var n = values.length,
+ i = -1,
+ value,
+ numbers = [];
+
+ if (valueof == null) {
+ while (++i < n) {
+ if (!isNaN(value = number$1(values[i]))) {
+ numbers.push(value);
+ }
+ }
+ }
+
+ else {
+ while (++i < n) {
+ if (!isNaN(value = number$1(valueof(values[i], i, values)))) {
+ numbers.push(value);
+ }
+ }
+ }
+
+ return threshold(numbers.sort(ascending), 0.5);
+};
+
+var merge$2 = function(arrays) {
+ var n = arrays.length,
+ m,
+ i = -1,
+ j = 0,
+ merged,
+ array;
+
+ while (++i < n) j += arrays[i].length;
+ merged = new Array(j);
+
+ while (--n >= 0) {
+ array = arrays[n];
+ m = array.length;
+ while (--m >= 0) {
+ merged[--j] = array[m];
+ }
+ }
+
+ return merged;
+};
+
+var min = function(values, valueof) {
+ var n = values.length,
+ i = -1,
+ value,
+ min;
+
+ if (valueof == null) {
+ while (++i < n) { // Find the first comparable value.
+ if ((value = values[i]) != null && value >= value) {
+ min = value;
+ while (++i < n) { // Compare the remaining values.
+ if ((value = values[i]) != null && min > value) {
+ min = value;
+ }
+ }
+ }
+ }
+ }
+
+ else {
+ while (++i < n) { // Find the first comparable value.
+ if ((value = valueof(values[i], i, values)) != null && value >= value) {
+ min = value;
+ while (++i < n) { // Compare the remaining values.
+ if ((value = valueof(values[i], i, values)) != null && min > value) {
+ min = value;
+ }
+ }
+ }
+ }
+ }
+
+ return min;
+};
+
+var permute = function(array, indexes) {
+ var i = indexes.length, permutes = new Array(i);
+ while (i--) permutes[i] = array[indexes[i]];
+ return permutes;
+};
+
+var sum = function(values, valueof) {
+ var n = values.length,
+ i = -1,
+ value,
+ sum = 0;
+
+ if (valueof == null) {
+ while (++i < n) {
+ if (value = +values[i]) sum += value; // Note: zero and null are equivalent.
+ }
+ }
+
+ else {
+ while (++i < n) {
+ if (value = +valueof(values[i], i, values)) sum += value;
+ }
+ }
+
+ return sum;
+};
+
+function length(d) {
+ return d.length;
+}
+
+var bootstrapCI = function(array, samples, alpha, f) {
+ var values = numbers(array, f),
+ n = values.length,
+ m = samples,
+ a, i, j, mu;
+
+ for (j=0, mu=Array(m); j= a && x < b) ? 1 / d : 0;
+ };
+
+ dist.cdf = function(x) {
+ var v = Math.floor(x);
+ return v < a ? 0 : v >= b ? 1 : (v - a + 1) / d;
+ };
+
+ dist.icdf = function(p) {
+ return (p >= 0 && p <= 1) ? a - 1 + Math.floor(p * d) : NaN;
+ };
+
+ return dist.min(min).max(max);
+};
+
+var randomNormal = function(mean, stdev) {
+ var mu,
+ sigma,
+ next = NaN,
+ dist = {};
+
+ dist.mean = function(_) {
+ if (arguments.length) {
+ mu = _ || 0;
+ next = NaN;
+ return dist;
+ } else {
+ return mu;
+ }
+ };
+
+ dist.stdev = function(_) {
+ if (arguments.length) {
+ sigma = _ == null ? 1 : _;
+ next = NaN;
+ return dist;
+ } else {
+ return sigma;
+ }
+ };
+
+ dist.sample = function() {
+ var x = 0, y = 0, rds, c;
+ if (next === next) {
+ x = next;
+ next = NaN;
+ return x;
+ }
+ do {
+ x = exports.random() * 2 - 1;
+ y = exports.random() * 2 - 1;
+ rds = x * x + y * y;
+ } while (rds === 0 || rds > 1);
+ c = Math.sqrt(-2 * Math.log(rds) / rds); // Box-Muller transform
+ next = mu + y * c * sigma;
+ return mu + x * c * sigma;
+ };
+
+ dist.pdf = function(x) {
+ var exp = Math.exp(Math.pow(x-mu, 2) / (-2 * Math.pow(sigma, 2)));
+ return (1 / (sigma * Math.sqrt(2*Math.PI))) * exp;
+ };
+
+ // Approximation from West (2009)
+ // Better Approximations to Cumulative Normal Functions
+ dist.cdf = function(x) {
+ var cd,
+ z = (x - mu) / sigma,
+ Z = Math.abs(z);
+ if (Z > 37) {
+ cd = 0;
+ } else {
+ var sum, exp = Math.exp(-Z*Z/2);
+ if (Z < 7.07106781186547) {
+ sum = 3.52624965998911e-02 * Z + 0.700383064443688;
+ sum = sum * Z + 6.37396220353165;
+ sum = sum * Z + 33.912866078383;
+ sum = sum * Z + 112.079291497871;
+ sum = sum * Z + 221.213596169931;
+ sum = sum * Z + 220.206867912376;
+ cd = exp * sum;
+ sum = 8.83883476483184e-02 * Z + 1.75566716318264;
+ sum = sum * Z + 16.064177579207;
+ sum = sum * Z + 86.7807322029461;
+ sum = sum * Z + 296.564248779674;
+ sum = sum * Z + 637.333633378831;
+ sum = sum * Z + 793.826512519948;
+ sum = sum * Z + 440.413735824752;
+ cd = cd / sum;
+ } else {
+ sum = Z + 0.65;
+ sum = Z + 4 / sum;
+ sum = Z + 3 / sum;
+ sum = Z + 2 / sum;
+ sum = Z + 1 / sum;
+ cd = exp / sum / 2.506628274631;
+ }
+ }
+ return z > 0 ? 1 - cd : cd;
+ };
+
+ // Approximation of Probit function using inverse error function.
+ dist.icdf = function(p) {
+ if (p <= 0 || p >= 1) return NaN;
+ var x = 2*p - 1,
+ v = (8 * (Math.PI - 3)) / (3 * Math.PI * (4-Math.PI)),
+ a = (2 / (Math.PI*v)) + (Math.log(1 - Math.pow(x,2)) / 2),
+ b = Math.log(1 - (x*x)) / v,
+ s = (x > 0 ? 1 : -1) * Math.sqrt(Math.sqrt((a*a) - b) - a);
+ return mu + sigma * Math.SQRT2 * s;
+ };
+
+ return dist.mean(mean).stdev(stdev);
+};
+
+// TODO: support for additional kernels?
+var randomKDE = function(support, bandwidth) {
+ var kernel = randomNormal(),
+ dist = {},
+ n = 0;
+
+ dist.data = function(_) {
+ if (arguments.length) {
+ support = _;
+ n = _ ? _.length : 0;
+ return dist.bandwidth(bandwidth);
+ } else {
+ return support;
+ }
+ };
+
+ dist.bandwidth = function(_) {
+ if (!arguments.length) return bandwidth;
+ bandwidth = _;
+ if (!bandwidth && support) bandwidth = estimateBandwidth(support);
+ return dist;
+ };
+
+ dist.sample = function() {
+ return support[~~(exports.random() * n)] + bandwidth * kernel.sample();
+ };
+
+ dist.pdf = function(x) {
+ for (var y=0, i=0; i= a && x <= b) ? 1 / d : 0;
+ };
+
+ dist.cdf = function(x) {
+ return x < a ? 0 : x > b ? 1 : (x - a) / d;
+ };
+
+ dist.icdf = function(p) {
+ return (p >= 0 && p <= 1) ? a + p * d : NaN;
+ };
+
+ return dist.min(min).max(max);
+};
+
+function TupleStore(key$$1) {
+ this._key = key$$1 ? field(key$$1) : tupleid;
+ this.reset();
+}
+
+var prototype$9 = TupleStore.prototype;
+
+prototype$9.reset = function() {
+ this._add = [];
+ this._rem = [];
+ this._ext = null;
+ this._get = null;
+ this._q = null;
+};
+
+prototype$9.add = function(v) {
+ this._add.push(v);
+};
+
+prototype$9.rem = function(v) {
+ this._rem.push(v);
+};
+
+prototype$9.values = function() {
+ this._get = null;
+ if (this._rem.length === 0) return this._add;
+
+ var a = this._add,
+ r = this._rem,
+ k = this._key,
+ n = a.length,
+ m = r.length,
+ x = Array(n - m),
+ map = {}, i, j, v;
+
+ // use unique key field to clear removed values
+ for (i=0; i= 0) {
+ s = get(v[n]) + '';
+ if (!map.hasOwnProperty(s)) {
+ map[s] = 1;
+ ++count;
+ }
+ }
+
+ return count;
+};
+
+prototype$9.extent = function(get) {
+ if (this._get !== get || !this._ext) {
+ var v = this.values(),
+ i = extentIndex(v, get);
+ this._ext = [v[i[0]], v[i[1]]];
+ this._get = get;
+ }
+ return this._ext;
+};
+
+prototype$9.argmin = function(get) {
+ return this.extent(get)[0] || {};
+};
+
+prototype$9.argmax = function(get) {
+ return this.extent(get)[1] || {};
+};
+
+prototype$9.min = function(get) {
+ var m = this.extent(get)[0];
+ return m != null ? get(m) : +Infinity;
+};
+
+prototype$9.max = function(get) {
+ var m = this.extent(get)[1];
+ return m != null ? get(m) : -Infinity;
+};
+
+prototype$9.quartile = function(get) {
+ if (this._get !== get || !this._q) {
+ this._q = quartiles(this.values(), get);
+ this._get = get;
+ }
+ return this._q;
+};
+
+prototype$9.q1 = function(get) {
+ return this.quartile(get)[0];
+};
+
+prototype$9.q2 = function(get) {
+ return this.quartile(get)[1];
+};
+
+prototype$9.q3 = function(get) {
+ return this.quartile(get)[2];
+};
+
+prototype$9.ci = function(get) {
+ if (this._get !== get || !this._ci) {
+ this._ci = bootstrapCI(this.values(), 1000, 0.05, get);
+ this._get = get;
+ }
+ return this._ci;
+};
+
+prototype$9.ci0 = function(get) {
+ return this.ci(get)[0];
+};
+
+prototype$9.ci1 = function(get) {
+ return this.ci(get)[1];
+};
+
+/**
+ * Group-by aggregation operator.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {Array} [params.groupby] - An array of accessors to groupby.
+ * @param {Array} [params.fields] - An array of accessors to aggregate.
+ * @param {Array} [params.ops] - An array of strings indicating aggregation operations.
+ * @param {Array} [params.as] - An array of output field names for aggregated values.
+ * @param {boolean} [params.cross=false] - A flag indicating that the full
+ * cross-product of groupby values should be generated, including empty cells.
+ * If true, the drop parameter is ignored and empty cells are retained.
+ * @param {boolean} [params.drop=true] - A flag indicating if empty cells should be removed.
+ */
+function Aggregate(params) {
+ Transform.call(this, null, params);
+
+ this._adds = []; // array of added output tuples
+ this._mods = []; // array of modified output tuples
+ this._alen = 0; // number of active added tuples
+ this._mlen = 0; // number of active modified tuples
+ this._drop = true; // should empty aggregation cells be removed
+ this._cross = false; // produce full cross-product of group-by values
+
+ this._dims = []; // group-by dimension accessors
+ this._dnames = []; // group-by dimension names
+
+ this._measures = []; // collection of aggregation monoids
+ this._countOnly = false; // flag indicating only count aggregation
+ this._counts = null; // collection of count fields
+ this._prev = null; // previous aggregation cells
+
+ this._inputs = null; // array of dependent input tuple field names
+ this._outputs = null; // array of output tuple field names
+}
+
+Aggregate.Definition = {
+ "type": "Aggregate",
+ "metadata": {"generates": true, "changes": true},
+ "params": [
+ { "name": "groupby", "type": "field", "array": true },
+ { "name": "ops", "type": "enum", "array": true, "values": ValidAggregateOps },
+ { "name": "fields", "type": "field", "null": true, "array": true },
+ { "name": "as", "type": "string", "null": true, "array": true },
+ { "name": "drop", "type": "boolean", "default": true },
+ { "name": "cross", "type": "boolean", "default": false },
+ { "name": "key", "type": "field" }
+ ]
+};
+
+var prototype$8 = inherits(Aggregate, Transform);
+
+prototype$8.transform = function(_, pulse) {
+ var aggr = this,
+ out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
+ mod;
+
+ this.stamp = out.stamp;
+
+ if (this.value && ((mod = _.modified()) || pulse.modified(this._inputs))) {
+ this._prev = this.value;
+ this.value = mod ? this.init(_) : {};
+ pulse.visit(pulse.SOURCE, function(t) { aggr.add(t); });
+ } else {
+ this.value = this.value || this.init(_);
+ pulse.visit(pulse.REM, function(t) { aggr.rem(t); });
+ pulse.visit(pulse.ADD, function(t) { aggr.add(t); });
+ }
+
+ // Indicate output fields and return aggregate tuples.
+ out.modifies(this._outputs);
+
+ // Should empty cells be dropped?
+ aggr._drop = _.drop !== false;
+
+ // If domain cross-product requested, generate empty cells as needed
+ // and ensure that empty cells are not dropped
+ if (_.cross && aggr._dims.length > 1) {
+ aggr._drop = false;
+ this.cross();
+ }
+
+ return aggr.changes(out);
+};
+
+prototype$8.cross = function() {
+ var aggr = this,
+ curr = aggr.value,
+ dims = aggr._dnames,
+ vals = dims.map(function() { return {}; }),
+ n = dims.length;
+
+ // collect all group-by domain values
+ function collect(cells) {
+ var key$$1, i, t, v;
+ for (key$$1 in cells) {
+ t = cells[key$$1].tuple;
+ for (i=0; i} params.fields - The fields to compare.
+ * @param {Array} [params.orders] - The sort orders.
+ * Each entry should be one of "ascending" (default) or "descending".
+ */
+function Compare(params) {
+ Operator.call(this, null, update$1, params);
+}
+
+inherits(Compare, Operator);
+
+function update$1(_) {
+ return (this.value && !_.modified())
+ ? this.value
+ : compare(_.fields, _.orders);
+}
+
+/**
+ * Count regexp-defined pattern occurrences in a text field.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {function(object): *} params.field - An accessor for the text field.
+ * @param {string} [params.pattern] - RegExp string defining the text pattern.
+ * @param {string} [params.case] - One of 'lower', 'upper' or null (mixed) case.
+ * @param {string} [params.stopwords] - RegExp string of words to ignore.
+ */
+function CountPattern(params) {
+ Transform.call(this, null, params);
+}
+
+CountPattern.Definition = {
+ "type": "CountPattern",
+ "metadata": {"generates": true, "changes": true},
+ "params": [
+ { "name": "field", "type": "field", "required": true },
+ { "name": "case", "type": "enum", "values": ["upper", "lower", "mixed"], "default": "mixed" },
+ { "name": "pattern", "type": "string", "default": "[\\w\"]+" },
+ { "name": "stopwords", "type": "string", "default": "" },
+ { "name": "as", "type": "string", "array": true, "length": 2, "default": ["text", "count"] }
+ ]
+};
+
+function tokenize(text, tcase, match) {
+ switch (tcase) {
+ case 'upper': text = text.toUpperCase(); break;
+ case 'lower': text = text.toLowerCase(); break;
+ }
+ return text.match(match);
+}
+
+var prototype$12 = inherits(CountPattern, Transform);
+
+prototype$12.transform = function(_, pulse) {
+ function process(update) {
+ return function(tuple) {
+ var tokens = tokenize(get(tuple), _.case, match) || [], t;
+ for (var i=0, n=tokens.length; i} [params.as] - The names of the output fields.
+ */
+function Cross(params) {
+ Transform.call(this, null, params);
+}
+
+Cross.Definition = {
+ "type": "Cross",
+ "metadata": {"generates": true},
+ "params": [
+ { "name": "filter", "type": "expr" },
+ { "name": "as", "type": "string", "array": true, "length": 2, "default": ["a", "b"] }
+ ]
+};
+
+var prototype$13 = inherits(Cross, Transform);
+
+prototype$13.transform = function(_, pulse) {
+ var out = pulse.fork(pulse.NO_SOURCE),
+ data = this.value,
+ as = _.as || ['a', 'b'],
+ a = as[0], b = as[1],
+ reset = !data
+ || pulse.changed(pulse.ADD_REM)
+ || _.modified('as')
+ || _.modified('filter');
+
+ if (reset) {
+ if (data) out.rem = data;
+ data = pulse.materialize(pulse.SOURCE).source;
+ out.add = this.value = cross$1(data, a, b, _.filter || truthy);
+ } else {
+ out.mod = data;
+ }
+
+ out.source = this.value;
+ return out.modifies(as);
+};
+
+function cross$1(input, a, b, filter) {
+ var data = [],
+ t = {},
+ n = input.length,
+ i = 0,
+ j, left;
+
+ for (; i} - A method for requesting
+ * source data. Used for distributions (such as KDE) that
+ * require sample data points. This method will only be
+ * invoked if the 'from' parameter for a target data source
+ * is not provided. Typically this method returns backing
+ * source data for a Pulse object.
+ * @return {object} - The output distribution object.
+ */
+function parse$1(def, data) {
+ var func = def[FUNCTION];
+ if (!Distributions.hasOwnProperty(func)) {
+ error$1('Unknown distribution function: ' + func);
+ }
+
+ var d = Distributions[func]();
+
+ for (var name in def) {
+ // if data field, extract values
+ if (name === FIELD) {
+ d.data((def.from || data()).map(def[name]));
+ }
+
+ // if distribution mixture, recurse to parse each definition
+ else if (name === DISTRIBUTIONS) {
+ d[name](def[name].map(function(_) { return parse$1(_, data); }));
+ }
+
+ // otherwise, simply set the parameter
+ else if (typeof d[name] === FUNCTION) {
+ d[name](def[name]);
+ }
+ }
+
+ return d;
+}
+
+/**
+ * Grid sample points for a probability density. Given a distribution and
+ * a sampling extent, will generate points suitable for plotting either
+ * PDF (probability density function) or CDF (cumulative distribution
+ * function) curves.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {object} params.distribution - The probability distribution. This
+ * is an object parameter dependent on the distribution type.
+ * @param {string} [params.method='pdf'] - The distribution method to sample.
+ * One of 'pdf' or 'cdf'.
+ * @param {Array} [params.extent] - The [min, max] extent over which
+ * to sample the distribution. This argument is required in most cases, but
+ * can be omitted if the distribution (e.g., 'kde') supports a 'data' method
+ * that returns numerical sample points from which the extent can be deduced.
+ * @param {number} [params.steps=100] - The number of sampling steps.
+ */
+function Density(params) {
+ Transform.call(this, null, params);
+}
+
+var distributions = [
+ {
+ "key": {"function": "normal"},
+ "params": [
+ { "name": "mean", "type": "number", "default": 0 },
+ { "name": "stdev", "type": "number", "default": 1 }
+ ]
+ },
+ {
+ "key": {"function": "uniform"},
+ "params": [
+ { "name": "min", "type": "number", "default": 0 },
+ { "name": "max", "type": "number", "default": 1 }
+ ]
+ },
+ {
+ "key": {"function": "kde"},
+ "params": [
+ { "name": "field", "type": "field", "required": true },
+ { "name": "from", "type": "data" },
+ { "name": "bandwidth", "type": "number", "default": 0 }
+ ]
+ }
+];
+
+var mixture = {
+ "key": {"function": "mixture"},
+ "params": [
+ { "name": "distributions", "type": "param", "array": true,
+ "params": distributions },
+ { "name": "weights", "type": "number", "array": true }
+ ]
+};
+
+Density.Definition = {
+ "type": "Density",
+ "metadata": {"generates": true},
+ "params": [
+ { "name": "extent", "type": "number", "array": true, "length": 2 },
+ { "name": "steps", "type": "number", "default": 100 },
+ { "name": "method", "type": "string", "default": "pdf",
+ "values": ["pdf", "cdf"] },
+ { "name": "distribution", "type": "param",
+ "params": distributions.concat(mixture) },
+ { "name": "as", "type": "string", "array": true,
+ "default": ["value", "density"] }
+ ]
+};
+
+var prototype$14 = inherits(Density, Transform);
+
+prototype$14.transform = function(_, pulse) {
+ var out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
+
+ if (!this.value || pulse.changed() || _.modified()) {
+ var dist = parse$1(_.distribution, source(pulse)),
+ method = _.method || 'pdf';
+
+ if (method !== 'pdf' && method !== 'cdf') {
+ error$1('Invalid density method: ' + method);
+ }
+ if (!_.extent && !dist.data) {
+ error$1('Missing density extent parameter.');
+ }
+ method = dist[method];
+
+ var as = _.as || ['value', 'density'],
+ domain = _.extent || extent(dist.data()),
+ step = (domain[1] - domain[0]) / (_.steps || 100),
+ values = sequence(domain[0], domain[1] + step/2, step)
+ .map(function(v) {
+ var tuple = {};
+ tuple[as[0]] = v;
+ tuple[as[1]] = method(v);
+ return ingest(tuple);
+ });
+
+ if (this.value) out.rem = this.value;
+ this.value = out.add = out.source = values;
+ }
+
+ return out;
+};
+
+function source(pulse) {
+ return function() { return pulse.materialize(pulse.SOURCE).source; };
+}
+
+/**
+ * Computes extents (min/max) for a data field.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {function(object): *} params.field - The field over which to compute extends.
+ */
+function Extent(params) {
+ Transform.call(this, [+Infinity, -Infinity], params);
+}
+
+Extent.Definition = {
+ "type": "Extent",
+ "metadata": {},
+ "params": [
+ { "name": "field", "type": "field", "required": true }
+ ]
+};
+
+var prototype$15 = inherits(Extent, Transform);
+
+prototype$15.transform = function(_, pulse) {
+ var extent = this.value,
+ field$$1 = _.field,
+ min = extent[0],
+ max = extent[1],
+ flag = pulse.ADD,
+ mod;
+
+ mod = pulse.changed()
+ || pulse.modified(field$$1.fields)
+ || _.modified('field');
+
+ if (mod) {
+ flag = pulse.SOURCE;
+ min = +Infinity;
+ max = -Infinity;
+ }
+
+ pulse.visit(flag, function(t) {
+ var v = field$$1(t);
+ if (v != null) {
+ // coerce to number
+ v = +v;
+ // NaNs will fail all comparisons!
+ if (v < min) min = v;
+ if (v > max) max = v;
+ }
+ });
+
+ this.value = [min, max];
+};
+
+/**
+ * Provides a bridge between a parent transform and a target subflow that
+ * consumes only a subset of the tuples that pass through the parent.
+ * @constructor
+ * @param {Pulse} pulse - A pulse to use as the value of this operator.
+ * @param {Transform} parent - The parent transform (typically a Facet instance).
+ * @param {Transform} target - A transform that receives the subflow of tuples.
+ */
+function Subflow(pulse, parent) {
+ Operator.call(this, pulse);
+ this.parent = parent;
+}
+
+var prototype$17 = inherits(Subflow, Operator);
+
+prototype$17.connect = function(target) {
+ this.targets().add(target);
+ return (target.source = this);
+};
+
+/**
+ * Add an 'add' tuple to the subflow pulse.
+ * @param {Tuple} t - The tuple being added.
+ */
+prototype$17.add = function(t) {
+ this.value.add.push(t);
+};
+
+/**
+ * Add a 'rem' tuple to the subflow pulse.
+ * @param {Tuple} t - The tuple being removed.
+ */
+prototype$17.rem = function(t) {
+ this.value.rem.push(t);
+};
+
+/**
+ * Add a 'mod' tuple to the subflow pulse.
+ * @param {Tuple} t - The tuple being modified.
+ */
+prototype$17.mod = function(t) {
+ this.value.mod.push(t);
+};
+
+/**
+ * Re-initialize this operator's pulse value.
+ * @param {Pulse} pulse - The pulse to copy from.
+ * @see Pulse.init
+ */
+prototype$17.init = function(pulse) {
+ this.value.init(pulse, pulse.NO_SOURCE);
+};
+
+/**
+ * Evaluate this operator. This method overrides the
+ * default behavior to simply return the contained pulse value.
+ * @return {Pulse}
+ */
+prototype$17.evaluate = function() {
+ // assert: this.value.stamp === pulse.stamp
+ return this.value;
+};
+
+/**
+ * Facets a dataflow into a set of subflows based on a key.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {function(Dataflow, string): Operator} params.subflow - A function
+ * that generates a subflow of operators and returns its root operator.
+ * @param {function(object): *} params.key - The key field to facet by.
+ */
+function Facet(params) {
+ Transform.call(this, {}, params);
+ this._keys = fastmap(); // cache previously calculated key values
+
+ // keep track of active subflows, use as targets array for listeners
+ // this allows us to limit propagation to only updated subflows
+ var a = this._targets = [];
+ a.active = 0;
+ a.forEach = function(f) {
+ for (var i=0, n=a.active; i df.cleanThreshold) df.runAfter(cache.clean);
+ return pulse;
+};
+
+/**
+ * Generates one or more field accessor functions.
+ * If the 'name' parameter is an array, an array of field accessors
+ * will be created and the 'as' parameter will be ignored.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {string} params.name - The field name(s) to access.
+ * @param {string} params.as - The accessor function name.
+ */
+function Field(params) {
+ Operator.call(this, null, update$2, params);
+}
+
+inherits(Field, Operator);
+
+function update$2(_) {
+ return (this.value && !_.modified()) ? this.value
+ : isArray(_.name) ? array(_.name).map(function(f) { return field(f); })
+ : field(_.name, _.as);
+}
+
+/**
+ * Filters data tuples according to a predicate function.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {function(object): *} params.expr - The predicate expression function
+ * that determines a tuple's filter status. Truthy values pass the filter.
+ */
+function Filter(params) {
+ Transform.call(this, fastmap(), params);
+}
+
+Filter.Definition = {
+ "type": "Filter",
+ "metadata": {"changes": true},
+ "params": [
+ { "name": "expr", "type": "expr", "required": true }
+ ]
+};
+
+var prototype$18 = inherits(Filter, Transform);
+
+prototype$18.transform = function(_, pulse) {
+ var df = pulse.dataflow,
+ cache = this.value, // cache ids of filtered tuples
+ output = pulse.fork(),
+ add = output.add,
+ rem = output.rem,
+ mod = output.mod,
+ test = _.expr,
+ isMod = true;
+
+ pulse.visit(pulse.REM, function(t) {
+ var id$$1 = tupleid(t);
+ if (!cache.has(id$$1)) rem.push(t);
+ else cache.delete(id$$1);
+ });
+
+ pulse.visit(pulse.ADD, function(t) {
+ if (test(t, _)) add.push(t);
+ else cache.set(tupleid(t), 1);
+ });
+
+ function revisit(t) {
+ var id$$1 = tupleid(t),
+ b = test(t, _),
+ s = cache.get(id$$1);
+ if (b && s) {
+ cache.delete(id$$1);
+ add.push(t);
+ } else if (!b && !s) {
+ cache.set(id$$1, 1);
+ rem.push(t);
+ } else if (isMod && b && !s) {
+ mod.push(t);
+ }
+ }
+
+ pulse.visit(pulse.MOD, revisit);
+
+ if (_.modified()) {
+ isMod = false;
+ pulse.visit(pulse.REFLOW, revisit);
+ }
+
+ if (cache.empty > df.cleanThreshold) df.runAfter(cache.clean);
+ return output;
+};
+
+// use either provided alias or accessor field name
+function fieldNames(fields, as) {
+ if (!fields) return null;
+ return fields.map(function(f, i) {
+ return as[i] || accessorName(f);
+ });
+}
+
+/**
+ * Flattens array-typed field values into new data objects.
+ * If multiple fields are specified, they are treated as parallel arrays,
+ * with output values included for each matching index (or null if missing).
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {Array} params.fields - An array of field
+ * accessors for the tuple fields that should be flattened.
+ * @param {Array} [params.as] - Output field names for flattened
+ * array fields. Any unspecified fields will use the field name provided
+ * by the fields accessors.
+ */
+function Flatten(params) {
+ Transform.call(this, [], params);
+}
+
+Flatten.Definition = {
+ "type": "Flatten",
+ "metadata": {"generates": true, "source": true},
+ "params": [
+ { "name": "fields", "type": "field", "array": true, "required": true },
+ { "name": "as", "type": "string", "array": true }
+ ]
+};
+
+var prototype$19 = inherits(Flatten, Transform);
+
+prototype$19.transform = function(_, pulse) {
+ var out = pulse.fork(pulse.NO_SOURCE),
+ fields = _.fields,
+ as = fieldNames(fields, _.as || []),
+ m = as.length;
+
+ // remove any previous results
+ out.rem = this.value;
+
+ // generate flattened tuples
+ pulse.visit(pulse.SOURCE, function(t) {
+ var arrays = fields.map(function(f) { return f(t); }),
+ maxlen = arrays.reduce(function(l, a) { return Math.max(l, a.length); }, 0),
+ i = 0, j, d, v;
+
+ for (; i} [params.as] - Output field names for folded key
+ * and value fields, defaults to ['key', 'value'].
+ */
+function Fold(params) {
+ Transform.call(this, [], params);
+}
+
+Fold.Definition = {
+ "type": "Fold",
+ "metadata": {"generates": true, "source": true},
+ "params": [
+ { "name": "fields", "type": "field", "array": true, "required": true },
+ { "name": "as", "type": "string", "array": true, "length": 2, "default": ["key", "value"] }
+ ]
+};
+
+var prototype$20 = inherits(Fold, Transform);
+
+prototype$20.transform = function(_, pulse) {
+ var out = pulse.fork(pulse.NO_SOURCE),
+ fields = _.fields,
+ fnames = fields.map(accessorName),
+ as = _.as || ['key', 'value'],
+ k = as[0],
+ v = as[1],
+ n = fields.length;
+
+ out.rem = this.value;
+
+ pulse.visit(pulse.SOURCE, function(t) {
+ for (var i=0, d; i 0) {
+ // need more tuples, generate and add
+ for (add=[]; --num >= 0;) {
+ add.push(t = ingest(gen(_)));
+ data.push(t);
+ }
+ out.add = out.add.length
+ ? out.materialize(out.ADD).add.concat(add)
+ : add;
+ } else {
+ // need fewer tuples, remove
+ rem = data.slice(0, -num);
+ out.rem = out.rem.length
+ ? out.materialize(out.REM).rem.concat(rem)
+ : rem;
+ data = data.slice(-num);
+ }
+
+ out.source = this.value = data;
+ return out;
+};
+
+var Methods = {
+ value: 'value',
+ median: median,
+ mean: mean,
+ min: min,
+ max: max
+};
+
+var Empty = [];
+
+/**
+ * Impute missing values.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {function(object): *} params.field - The value field to impute.
+ * @param {Array} [params.groupby] - An array of
+ * accessors to determine series within which to perform imputation.
+ * @param {function(object): *} params.key - An accessor for a key value.
+ * Each key value should be unique within a group. New tuples will be
+ * imputed for any key values that are not found within a group.
+ * @param {Array<*>} [params.keyvals] - Optional array of required key
+ * values. New tuples will be imputed for any key values that are not
+ * found within a group. In addition, these values will be automatically
+ * augmented with the key values observed in the input data.
+ * @param {string} [method='value'] - The imputation method to use. One of
+ * 'value', 'mean', 'median', 'max', 'min'.
+ * @param {*} [value=0] - The constant value to use for imputation
+ * when using method 'value'.
+ */
+function Impute(params) {
+ Transform.call(this, [], params);
+}
+
+Impute.Definition = {
+ "type": "Impute",
+ "metadata": {"generates": true, "changes": true},
+ "params": [
+ { "name": "field", "type": "field", "required": true },
+ { "name": "key", "type": "field", "required": true },
+ { "name": "keyvals", "array": true },
+ { "name": "groupby", "type": "field", "array": true },
+ { "name": "method", "type": "enum", "default": "value",
+ "values": ["value", "mean", "median", "max", "min"] },
+ { "name": "value", "default": 0 }
+ ]
+};
+
+var prototype$23 = inherits(Impute, Transform);
+
+function getValue(_) {
+ var m = _.method || Methods.value, v;
+
+ if (Methods[m] == null) {
+ error$1('Unrecognized imputation method: ' + m);
+ } else if (m === Methods.value) {
+ v = _.value !== undefined ? _.value : 0;
+ return function() { return v; };
+ } else {
+ return Methods[m];
+ }
+}
+
+function getField(_) {
+ var f = _.field;
+ return function(t) { return t ? f(t) : NaN; };
+}
+
+prototype$23.transform = function(_, pulse) {
+ var out = pulse.fork(pulse.ALL),
+ impute = getValue(_),
+ field$$1 = getField(_),
+ fName = accessorName(_.field),
+ kName = accessorName(_.key),
+ gNames = (_.groupby || []).map(accessorName),
+ groups = partition(pulse.source, _.groupby, _.key, _.keyvals),
+ curr = [],
+ prev = this.value,
+ m = groups.domain.length,
+ group, value, gVals, kVal, g, i, j, l, n, t;
+
+ for (g=0, l=groups.length; g} params.fields - The field name(s) for the key function.
+ * @param {boolean} params.flat - A boolean flag indicating if the field names
+ * should be treated as flat property names, side-stepping nested field
+ * lookups normally indicated by dot or bracket notation.
+ */
+function Key(params) {
+ Operator.call(this, null, update$3, params);
+}
+
+inherits(Key, Operator);
+
+function update$3(_) {
+ return (this.value && !_.modified()) ? this.value : key(_.fields, _.flat);
+}
+
+/**
+ * Extend tuples by joining them with values from a lookup table.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {Map} params.index - The lookup table map.
+ * @param {Array} params.as - Output field names for each lookup value.
+ * @param {*} [params.default] - A default value to use if lookup fails.
+ */
+function Lookup(params) {
+ Transform.call(this, {}, params);
+}
+
+Lookup.Definition = {
+ "type": "Lookup",
+ "metadata": {"modifies": true},
+ "params": [
+ { "name": "index", "type": "index", "params": [
+ {"name": "from", "type": "data", "required": true },
+ {"name": "key", "type": "field", "required": true }
+ ] },
+ { "name": "values", "type": "field", "array": true },
+ { "name": "fields", "type": "field", "array": true, "required": true },
+ { "name": "as", "type": "string", "array": true },
+ { "name": "default", "default": null }
+ ]
+};
+
+var prototype$25 = inherits(Lookup, Transform);
+
+prototype$25.transform = function(_, pulse) {
+ var out = pulse,
+ as = _.as,
+ keys = _.fields,
+ index = _.index,
+ values = _.values,
+ defaultValue = _.default==null ? null : _.default,
+ reset = _.modified(),
+ flag = reset ? pulse.SOURCE : pulse.ADD,
+ n = keys.length,
+ set, m, mods;
+
+ if (values) {
+ m = values.length;
+
+ if (n > 1 && !as) {
+ error$1('Multi-field lookup requires explicit "as" parameter.');
+ }
+ if (as && as.length !== n * m) {
+ error$1('The "as" parameter has too few output field names.');
+ }
+ as = as || values.map(accessorName);
+
+ set = function(t) {
+ for (var i=0, k=0, j, v; i>} params.extents - The input extents.
+ */
+function MultiExtent(params) {
+ Operator.call(this, null, update$4, params);
+}
+
+inherits(MultiExtent, Operator);
+
+function update$4(_) {
+ if (this.value && !_.modified()) {
+ return this.value;
+ }
+
+ var min = +Infinity,
+ max = -Infinity,
+ ext = _.extents,
+ i, n, e;
+
+ for (i=0, n=ext.length; i max) max = e[1];
+ }
+ return [min, max];
+}
+
+/**
+ * Merge a collection of value arrays.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {Array>} params.values - The input value arrrays.
+ */
+function MultiValues(params) {
+ Operator.call(this, null, update$5, params);
+}
+
+inherits(MultiValues, Operator);
+
+function update$5(_) {
+ return (this.value && !_.modified())
+ ? this.value
+ : _.values.reduce(function(data, _) { return data.concat(_); }, []);
+}
+
+/**
+ * Operator whose value is simply its parameter hash. This operator is
+ * useful for enabling reactive updates to values of nested objects.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ */
+function Params(params) {
+ Transform.call(this, null, params);
+}
+
+inherits(Params, Transform);
+
+Params.prototype.transform = function(_, pulse) {
+ this.modified(_.modified());
+ this.value = _;
+ return pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS); // do not pass tuples
+};
+
+/**
+ * Aggregate and pivot selected field values to become new fields.
+ * This operator is useful to construction cross-tabulations.
+ * @constructor
+ * @param {Array} [params.groupby] - An array of accessors
+ * to groupby. These fields act just like groupby fields of an Aggregate transform.
+ * @param {function(object): *} params.field - The field to pivot on. The unique
+ * values of this field become new field names in the output stream.
+ * @param {function(object): *} params.value - The field to populate pivoted fields.
+ * The aggregate values of this field become the values of the new pivoted fields.
+ * @param {string} [params.op] - The aggregation operation for the value field,
+ * applied per cell in the output stream. The default is "sum".
+ * @param {number} [params.limit] - An optional parameter indicating the maximum
+ * number of pivoted fields to generate. The pivoted field names are sorted in
+ * ascending order prior to enforcing the limit.
+ */
+function Pivot(params) {
+ Aggregate.call(this, params);
+}
+
+Pivot.Definition = {
+ "type": "Pivot",
+ "metadata": {"generates": true, "changes": true},
+ "params": [
+ { "name": "groupby", "type": "field", "array": true },
+ { "name": "field", "type": "field", "required": true },
+ { "name": "value", "type": "field", "required": true },
+ { "name": "op", "type": "enum", "values": ValidAggregateOps, "default": "sum" },
+ { "name": "limit", "type": "number", "default": 0 },
+ { "name": "key", "type": "field" }
+ ]
+};
+
+var prototype$26 = inherits(Pivot, Aggregate);
+
+prototype$26._transform = prototype$26.transform;
+
+prototype$26.transform = function(_, pulse) {
+ return this._transform(aggregateParams(_, pulse), pulse);
+};
+
+// Shoehorn a pivot transform into an aggregate transform!
+// First collect all unique pivot field values.
+// Then generate aggregate fields for each output pivot field.
+function aggregateParams(_, pulse) {
+ var key$$1 = _.field,
+ value = _.value,
+ op = (_.op === 'count' ? '__count__' : _.op) || 'sum',
+ fields = accessorFields(key$$1).concat(accessorFields(value)),
+ keys = pivotKeys(key$$1, _.limit || 0, pulse);
+
+ return {
+ key: _.key,
+ groupby: _.groupby,
+ ops: keys.map(function() { return op; }),
+ fields: keys.map(function(k) { return get$1(k, key$$1, value, fields); }),
+ as: keys.map(function(k) { return k + ''; }),
+ modified: _.modified.bind(_)
+ };
+}
+
+// Generate aggregate field accessor.
+// Output NaN for non-existent values; aggregator will ignore!
+function get$1(k, key$$1, value, fields) {
+ return accessor(
+ function(d) { return key$$1(d) === k ? value(d) : NaN; },
+ fields,
+ k + ''
+ );
+}
+
+// Collect (and optionally limit) all unique pivot values.
+function pivotKeys(key$$1, limit, pulse) {
+ var map = {},
+ list = [];
+
+ pulse.visit(pulse.SOURCE, function(t) {
+ var k = key$$1(t);
+ if (!map[k]) {
+ map[k] = 1;
+ list.push(k);
+ }
+ });
+
+ // TODO? Move this comparator to vega-util?
+ list.sort(function(u, v) {
+ return (uv||v==null) && u!=null ? 1
+ : ((v=v instanceof Date?+v:v),(u=u instanceof Date?+u:u))!==u && v===v ? -1
+ : v!==v && u===u ? 1 : 0;
+ });
+
+ return limit ? list.slice(0, limit) : list;
+}
+
+/**
+ * Partitions pre-faceted data into tuple subflows.
+ * @constructor
+ * @param {object} params - The parameters for this operator.
+ * @param {function(Dataflow, string): Operator} params.subflow - A function
+ * that generates a subflow of operators and returns its root operator.
+ * @param {function(object): Array