Skip to content

Instantly share code, notes, and snippets.

@gnestor
Last active August 14, 2017 07:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gnestor/14cc8f9c112934cb5461f4e0b863afb1 to your computer and use it in GitHub Desktop.
Save gnestor/14cc8f9c112934cb5461f4e0b863afb1 to your computer and use it in GitHub Desktop.
requirebin sketch
// Welcome! require() some modules from npm (like you were using browserify)
// and then hit Run Code to run your code on the right side.
// Modules get downloaded from browserify-cdn and bundled in your browser.
const embed = require('vega-embed');
const data = {
"width": 400,
"height": 200,
"padding": 5,
"data": [
{
"name": "table",
"values": [
{
"category": "A",
"amount": 28
}, {
"category": "B",
"amount": 55
}, {
"category": "C",
"amount": 43
}, {
"category": "D",
"amount": 91
}, {
"category": "E",
"amount": 81
}, {
"category": "F",
"amount": 53
}, {
"category": "G",
"amount": 19
}, {
"category": "H",
"amount": 87
}
]
}
],
"signals": [
{
"name": "tooltip",
"value": {},
"on": [
{
"events": "rect:mouseover",
"update": "datum"
}, {
"events": "rect:mouseout",
"update": "{}"
}
]
}
],
"scales": [
{
"name": "xscale",
"type": "band",
"domain": {
"data": "table",
"field": "category"
},
"range": "width",
"padding": 0.05,
"round": true
}, {
"name": "yscale",
"domain": {
"data": "table",
"field": "amount"
},
"nice": true,
"range": "height"
}
],
"axes": [
{
"orient": "bottom",
"scale": "xscale"
}, {
"orient": "left",
"scale": "yscale"
}
],
"marks": [
{
"type": "rect",
"from": {
"data": "table"
},
"encode": {
"enter": {
"x": {
"scale": "xscale",
"field": "category"
},
"width": {
"scale": "xscale",
"band": 1
},
"y": {
"scale": "yscale",
"field": "amount"
},
"y2": {
"scale": "yscale",
"value": 0
}
},
"update": {
"fill": {
"value": "steelblue"
}
},
"hover": {
"fill": {
"value": "red"
}
}
}
}, {
"type": "text",
"encode": {
"enter": {
"align": {
"value": "center"
},
"baseline": {
"value": "bottom"
},
"fill": {
"value": "#333"
}
},
"update": {
"x": {
"scale": "xscale",
"signal": "tooltip.category",
"band": 0.5
},
"y": {
"scale": "yscale",
"signal": "tooltip.amount",
"offset": -2
},
"text": {
"signal": "tooltip.amount"
},
"fillOpacity": [
{
"test": "datum === tooltip",
"value": 0
}, {
"value": 1
}
]
}
}
}
]
};
const options = {
mode: "vega",
actions: true
};
const node = document.getElementById('root');
embed(node, data, options)
.then(result => {
console.log(result);
})
.catch(error => {
console.log(error);
});
This file has been truncated, but you can view the full file.
setTimeout(function(){
;require=(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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
},{}],2:[function(require,module,exports){
/* global define */
(function (root, factory) {
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.compareVersions = factory();
}
}(this, function () {
var semver = /^v?(?:0|[1-9]\d*)(\.(?:[x*]|0|[1-9]\d*)(\.(?:[x*]|0|[1-9]\d*)(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
var patch = /-([0-9A-Za-z-.]+)/;
function split(v) {
var temp = v.replace(/^v/, '').split('.');
var arr = temp.splice(0, 2);
arr.push(temp.join('.'));
return arr;
}
function tryParse(v) {
return isNaN(Number(v)) ? v : Number(v);
}
function validate(version) {
if (typeof version !== 'string') {
throw new TypeError('Invalid argument expected string');
}
if (!semver.test(version)) {
throw new Error('Invalid argument not valid semver');
}
}
return function compareVersions(v1, v2) {
[v1, v2].forEach(validate);
var s1 = split(v1);
var s2 = split(v2);
for (var i = 0; i < 3; i++) {
var n1 = parseInt(s1[i] || 0, 10);
var n2 = parseInt(s2[i] || 0, 10);
if (n1 > 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.1.0. Copyright 2017 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/"
};
var namespace = function(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);
};
}
var creator = function(name) {
var fullname = namespace(name);
return (fullname.local
? creatorFixed
: creatorInherit)(fullname);
};
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._;
}
};
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;
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);
};
}
var selection_on = function(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;
}
}
var sourceEvent = function() {
var current = exports.event, source;
while (source = current.sourceEvent) current = source;
return current;
};
var point = function(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];
};
var mouse = function(node) {
var event = sourceEvent();
if (event.changedTouches) event = event.changedTouches[0];
return point(node, event);
};
function none() {}
var selector = function(selector) {
return selector == null ? none : function() {
return this.querySelector(selector);
};
};
var selection_select = function(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 [];
}
var selectorAll = function(selector) {
return selector == null ? empty : function() {
return this.querySelectorAll(selector);
};
};
var selection_selectAll = function(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 selection_filter = function(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);
};
var sparse = function(update) {
return new Array(update.length);
};
var selection_enter = function() {
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); }
};
var constant = function(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;
}
}
}
var selection_data = function(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;
};
var selection_exit = function() {
return new Selection(this._exit || this._groups.map(sparse), this._parents);
};
var selection_merge = function(selection) {
for (var groups0 = this._groups, groups1 = selection._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);
};
var selection_order = function() {
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;
};
var selection_sort = function(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;
}
var selection_call = function() {
var callback = arguments[0];
arguments[0] = this;
callback.apply(null, arguments);
return this;
};
var selection_nodes = function() {
var nodes = new Array(this.size()), i = -1;
this.each(function() { nodes[++i] = this; });
return nodes;
};
var selection_node = function() {
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;
};
var selection_size = function() {
var size = 0;
this.each(function() { ++size; });
return size;
};
var selection_empty = function() {
return !this.node();
};
var selection_each = function(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);
};
}
var selection_attr = function(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));
};
var defaultView = function(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);
};
}
var selection_style = function(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;
};
}
var selection_property = function(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);
};
}
var selection_classed = function(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;
};
}
var selection_text = function(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;
};
}
var selection_html = function(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);
}
var selection_raise = function() {
return this.each(raise);
};
function lower() {
if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
}
var selection_lower = function() {
return this.each(lower);
};
var selection_append = function(name) {
var create = typeof name === "function" ? name : creator(name);
return this.select(function() {
return this.appendChild(create.apply(this, arguments));
});
};
function constantNull() {
return null;
}
var selection_insert = function(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);
}
var selection_remove = function() {
return this.each(remove);
};
var selection_datum = function(value) {
return arguments.length
? this.property("__data__", value)
: this.node().__data__;
};
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));
};
}
var selection_dispatch = function(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,
datum: selection_datum,
on: selection_on,
dispatch: selection_dispatch
};
var select = function(selector) {
return typeof selector === "string"
? new Selection([[document.querySelector(selector)]], [document.documentElement])
: new Selection([[selector]], root);
};
var selectAll = function(selector) {
return typeof selector === "string"
? new Selection([document.querySelectorAll(selector)], [document.documentElement])
: new Selection([selector == null ? [] : selector], root);
};
var touch = function(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;
};
var touches = function(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.creator = creator;
exports.local = local;
exports.matcher = matcher$1;
exports.mouse = mouse;
exports.namespace = namespace;
exports.namespaces = namespaces;
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){
module.exports={
"name": "vega-lite",
"author": "Jeffrey Heer, Dominik Moritz, Kanit \"Ham\" Wongsuphasawat",
"version": "2.0.0-beta.14",
"collaborators": [
"Kanit Wongsuphasawat <kanitw@gmail.com> (http://kanitw.yellowpigz.com)",
"Dominik Moritz <domoritz@cs.washington.edu> (https://www.domoritz.de)",
"Jeffrey Heer <jheer@uw.edu> (http://jheer.org)"
],
"homepage": "https://vega.github.io/vega-lite/",
"description": "Vega-lite provides a higher-level grammar for visual analysis, comparable to ggplot or Tableau, that generates complete Vega specifications.",
"main": "build/src/index.js",
"types": "typings/vega-lite.d.ts",
"bin": {
"vl2png": "./bin/vl2png",
"vl2svg": "./bin/vl2svg",
"vl2vg": "./bin/vl2vg"
},
"directories": {
"test": "test"
},
"scripts": {
"pretsc": "mkdir -p build && rm -rf build/*/** && cp package.json build/",
"tsc": "tsc",
"prebuild": "mkdir -p build/site build/test-gallery",
"build": "npm run build:only",
"build:only": "npm run tsc && cp package.json build && browserify src/index.ts -p tsify -d -s vl | exorcist build/vega-lite.js.map > build/vega-lite.js",
"postbuild": "node node_modules/uglify-js/bin/uglifyjs build/vega-lite.js -cm --source-map content=build/vega-lite.js.map,filename=build/vega-lite.min.js.map -o build/vega-lite.min.js && npm run schema",
"build:examples": "npm run data && npm run build:only && npm run build:examples-only",
"build:examples-only": "TZ=America/Los_Angeles ./scripts/build-examples.sh && rm -rf examples/specs/normalized/* && scripts/build-normalized-examples",
"build:toc": "bundle exec jekyll build -q && scripts/generate-toc",
"build:site": "browserify site/static/main.ts -p [tsify -p site] -d | exorcist build/site/main.js.map > build/site/main.js",
"build:versions": "scripts/update-version.sh",
"build:test-gallery": "browserify test-gallery/main.ts -p [tsify -p test-gallery] -d > build/test-gallery/main.js",
"check:examples": "scripts/check-examples.sh",
"check:schema": "scripts/check-schema.sh",
"clean": "rm -rf build && rm -f vega-lite.* & find -E src test site examples -regex '.*\\.(js|js.map|d.ts|vg.json)' -delete & rm -rf data",
"data": "rsync -r node_modules/vega-datasets/data/* data",
"link": "npm link && npm link vega-lite",
"deploy": "scripts/deploy.sh",
"deploy:gh": "scripts/deploy-gh.sh",
"deploy:schema": "scripts/deploy-schema.sh",
"prestart": "npm run data && npm run build && scripts/index-examples",
"start": "nodemon -x 'npm run build:test-gallery' & browser-sync start --server --files 'build/test-gallery/main.js' --index 'test-gallery/index.html'",
"poststart": "rm examples/all-examples.json",
"preschema": "npm run prebuild",
"schema": "typescript-to-json-schema --path tsconfig.json --type TopLevelExtendedSpec > build/vega-lite-schema.json && npm run renameschema && cp build/vega-lite-schema.json _data/",
"renameschema": "scripts/rename-schema.sh",
"presite": "npm run build && npm run data && npm run build:site && npm run build:toc && npm run build:versions",
"site": "bundle exec jekyll serve",
"lint": "tslint --project tsconfig.json -c tslint.json --type-check",
"test": "npm run build:only && npm run test:only && npm run test:runtime && npm run lint",
"posttest": "npm run schema && npm run data && npm run mocha:examples",
"test:nocompile": "npm run test:only && npm run test:runtime && npm run lint && npm run mocha:examples",
"test:only": "nyc --reporter=html --reporter=text-summary npm run mocha:test",
"test:runtime": "TZ=America/Los_Angeles wdio wdio.conf.js",
"test:runtime:generate": "rm -Rf test-runtime/resources && VL_GENERATE_TESTS=true npm run test:runtime",
"test:debug": "npm run tsc && mocha --recursive --debug-brk --inspect build/test",
"test:debug-examples": "npm run tsc && npm run schema && mocha --recursive --debug-brk --inspect build/examples",
"mocha:test": "mocha --require source-map-support/register --reporter dot --recursive build/test",
"mocha:examples": "mocha --require source-map-support/register --reporter dot --recursive build/examples",
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json",
"watch:build": "watchify src/index.ts -p tsify -v -d -s vl -o 'exorcist build/vega-lite.js.map > build/vega-lite.js'",
"watch:tsc": "npm run tsc -- -w",
"watch:test": "nodemon -x 'npm test'",
"watch": "nodemon -x 'npm run build && npm run test:nocompile' # already ran schema in build"
},
"repository": {
"type": "git",
"url": "https://github.com/vega/vega-lite.git"
},
"license": "BSD-3-Clause",
"bugs": {
"url": "https://github.com/vega/vega-lite/issues"
},
"devDependencies": {
"@types/chai": "^4.0.2",
"@types/d3": "^4.10.0",
"@types/highlight.js": "^9.1.9",
"@types/json-stable-stringify": "^1.0.31",
"@types/mkdirp": "^0.5.0",
"@types/mocha": "^2.2.41",
"@types/node": "^8.0.19",
"@types/webdriverio": "^4.8.4",
"ajv": "5.2.2",
"browser-sync": "^2.18.13",
"browserify": "^14.4.0",
"browserify-shim": "^3.8.14",
"chai": "^4.1.0",
"cheerio": "^1.0.0-rc.2",
"chromedriver": "^2.30.1",
"codecov": "^2.3.0",
"d3": "^4.10.0",
"exorcist": "^0.4.0",
"highlight.js": "^9.12.0",
"mkdirp": "^0.5.1",
"mocha": "^3.5.0",
"nodemon": "^1.11.0",
"nyc": "^11.1.0",
"source-map-support": "^0.4.15",
"ts-node": "^3.2.1",
"tsify": "^3.0.1",
"tslint": "5.4.3",
"tslint-eslint-rules": "^4.1.1",
"typescript": "^2.4.2",
"typescript-to-json-schema": "vega/typescript-to-json-schema#v0.8.0",
"uglify-js": "^3.0.27",
"vega": "^3.0.0",
"vega-datasets": "vega/vega-datasets#gh-pages",
"vega-embed": "^3.0.0-beta.20",
"vega-tooltip": "^0.4.2",
"watchify": "^3.9.0",
"wdio-chromedriver-service": "^0.1.0",
"wdio-dot-reporter": "0.0.8",
"wdio-mocha-framework": "^0.5.10",
"wdio-static-server-service": "^1.0.1",
"webdriverio": "^4.8.0",
"yaml-front-matter": "^3.4.0"
},
"dependencies": {
"json-stable-stringify": "^1.0.1",
"tslib": "^1.7.1",
"vega-event-selector": "^2.0.0",
"vega-util": "^1.5.0",
"yargs": "^8.0.2"
}
}
},{}],5:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = require("./util");
exports.AGGREGATE_OPS = [
'values',
'count',
'valid',
'missing',
'distinct',
'sum',
'mean',
'average',
'variance',
'variancep',
'stdev',
'stdevp',
'median',
'q1',
'q3',
'ci0',
'ci1',
'min',
'max',
'argmin',
'argmax',
];
exports.AGGREGATE_OP_INDEX = util_1.toSet(exports.AGGREGATE_OPS);
exports.COUNTING_OPS = ['count', 'valid', 'missing', 'distinct'];
function isCountingAggregateOp(aggregate) {
return aggregate && util_1.contains(exports.COUNTING_OPS, aggregate);
}
exports.isCountingAggregateOp = isCountingAggregateOp;
/** Additive-based aggregation operations. These can be applied to stack. */
exports.SUM_OPS = [
'count',
'sum',
'distinct',
'valid',
'missing'
];
/**
* Aggregation operators that always produce values within the range [domainMin, domainMax].
*/
exports.SHARED_DOMAIN_OPS = [
'mean',
'average',
'median',
'q1',
'q3',
'min',
'max',
];
exports.SHARED_DOMAIN_OP_INDEX = util_1.toSet(exports.SHARED_DOMAIN_OPS);
},{"./util":107}],6:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* A dictionary listing whether a certain axis property is applicable for only main axes or only grid axes.
* (Properties not listed are applicable for both)
*/
exports.AXIS_PROPERTY_TYPE = {
grid: 'grid',
labelOverlap: 'main',
offset: 'main',
title: 'main'
};
exports.AXIS_PROPERTIES = [
'domain', 'format', 'grid', 'labelPadding', 'labels', 'labelOverlap', 'maxExtent', 'minExtent', 'offset', 'orient', 'position', 'tickCount', 'tickExtra', 'ticks', 'tickSize', 'title', 'titlePadding', 'values', 'zindex'
];
exports.VG_AXIS_PROPERTIES = [].concat(exports.AXIS_PROPERTIES, ['gridScale']);
},{}],7:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("./channel");
var util_1 = require("./util");
function binToString(bin) {
if (util_1.isBoolean(bin)) {
return 'bin';
}
return 'bin' + util_1.keys(bin).map(function (p) { return "_" + p + "_" + bin[p]; }).join('');
}
exports.binToString = binToString;
function autoMaxBins(channel) {
switch (channel) {
case channel_1.ROW:
case channel_1.COLUMN:
case channel_1.SIZE:
case channel_1.COLOR:
case channel_1.OPACITY:
// Facets and Size shouldn't have too many bins
// We choose 6 like shape to simplify the rule
case channel_1.SHAPE:
return 6; // Vega's "shape" has 6 distinct values
default:
return 10;
}
}
exports.autoMaxBins = autoMaxBins;
},{"./channel":8,"./util":107}],8:[function(require,module,exports){
"use strict";
/*
* Constants and utilities for encoding channels (Visual variables)
* such as 'x', 'y', 'color'.
*/
Object.defineProperty(exports, "__esModule", { value: true });
var scale_1 = require("./scale");
var util_1 = require("./util");
var Channel;
(function (Channel) {
// Facet
Channel.ROW = 'row';
Channel.COLUMN = 'column';
// Position
Channel.X = 'x';
Channel.Y = 'y';
Channel.X2 = 'x2';
Channel.Y2 = 'y2';
// Mark property with scale
Channel.COLOR = 'color';
Channel.SHAPE = 'shape';
Channel.SIZE = 'size';
Channel.OPACITY = 'opacity';
// Non-scale channel
Channel.TEXT = 'text';
Channel.ORDER = 'order';
Channel.DETAIL = 'detail';
Channel.TOOLTIP = 'tooltip';
})(Channel = exports.Channel || (exports.Channel = {}));
exports.X = Channel.X;
exports.Y = Channel.Y;
exports.X2 = Channel.X2;
exports.Y2 = Channel.Y2;
exports.ROW = Channel.ROW;
exports.COLUMN = Channel.COLUMN;
exports.SHAPE = Channel.SHAPE;
exports.SIZE = Channel.SIZE;
exports.COLOR = Channel.COLOR;
exports.TEXT = Channel.TEXT;
exports.DETAIL = Channel.DETAIL;
exports.ORDER = Channel.ORDER;
exports.OPACITY = Channel.OPACITY;
exports.TOOLTIP = Channel.TOOLTIP;
exports.CHANNELS = [exports.X, exports.Y, exports.X2, exports.Y2, exports.ROW, exports.COLUMN, exports.SIZE, exports.SHAPE, exports.COLOR, exports.ORDER, exports.OPACITY, exports.TEXT, exports.DETAIL, exports.TOOLTIP];
var CHANNEL_INDEX = util_1.toSet(exports.CHANNELS);
/**
* Channels cannot have an array of channelDef.
* model.fieldDef, getFieldDef only work for these channels.
*
* (The only two channels that can have an array of channelDefs are "detail" and "order".
* Since there can be multiple fieldDefs for detail and order, getFieldDef/model.fieldDef
* are not applicable for them. Similarly, selection projecttion won't work with "detail" and "order".)
*/
exports.SINGLE_DEF_CHANNELS = [exports.X, exports.Y, exports.X2, exports.Y2, exports.ROW, exports.COLUMN, exports.SIZE, exports.SHAPE, exports.COLOR, exports.OPACITY, exports.TEXT, exports.TOOLTIP];
function isChannel(str) {
return !!CHANNEL_INDEX[str];
}
exports.isChannel = isChannel;
// CHANNELS without COLUMN, ROW
exports.UNIT_CHANNELS = [exports.X, exports.Y, exports.X2, exports.Y2, exports.SIZE, exports.SHAPE, exports.COLOR, exports.ORDER, exports.OPACITY, exports.TEXT, exports.DETAIL, exports.TOOLTIP];
/** List of channels with scales */
exports.SCALE_CHANNELS = [exports.X, exports.Y, exports.SIZE, exports.SHAPE, exports.COLOR, exports.OPACITY];
var SCALE_CHANNEL_INDEX = util_1.toSet(exports.SCALE_CHANNELS);
function isScaleChannel(channel) {
return !!SCALE_CHANNEL_INDEX[channel];
}
exports.isScaleChannel = isScaleChannel;
// UNIT_CHANNELS without X, Y, X2, Y2;
exports.NONSPATIAL_CHANNELS = [exports.SIZE, exports.SHAPE, exports.COLOR, exports.ORDER, exports.OPACITY, exports.TEXT, exports.DETAIL, exports.TOOLTIP];
// X and Y;
exports.SPATIAL_SCALE_CHANNELS = [exports.X, exports.Y];
// SCALE_CHANNELS without X, Y;
exports.NONSPATIAL_SCALE_CHANNELS = [exports.SIZE, exports.SHAPE, exports.COLOR, exports.OPACITY];
exports.LEVEL_OF_DETAIL_CHANNELS = util_1.without(exports.NONSPATIAL_CHANNELS, ['order']);
/** Channels that can serve as groupings for stacked charts. */
exports.STACK_BY_CHANNELS = [exports.COLOR, exports.DETAIL, exports.ORDER, exports.OPACITY, exports.SIZE];
/**
* Return whether a channel supports a particular mark type.
* @param channel channel name
* @param mark the mark type
* @return whether the mark supports the channel
*/
function supportMark(channel, mark) {
return mark in getSupportedMark(channel);
}
exports.supportMark = supportMark;
/**
* Return a dictionary showing whether a channel supports mark type.
* @param channel
* @return A dictionary mapping mark types to boolean values.
*/
function getSupportedMark(channel) {
switch (channel) {
case exports.X:
case exports.Y:
case exports.COLOR:
case exports.DETAIL:
case exports.TOOLTIP:
case exports.ORDER: // TODO: revise (order might not support rect, which is not stackable?)
case exports.OPACITY:
case exports.ROW:
case exports.COLUMN:
return {
point: true, tick: true, rule: true, circle: true, square: true,
bar: true, rect: true, line: true, area: true, text: true
};
case exports.X2:
case exports.Y2:
return {
rule: true, bar: true, rect: true, area: true
};
case exports.SIZE:
return {
point: true, tick: true, rule: true, circle: true, square: true,
bar: true, text: true, line: true
};
case exports.SHAPE:
return { point: true };
case exports.TEXT:
return { text: true };
}
}
exports.getSupportedMark = getSupportedMark;
function hasScale(channel) {
return !util_1.contains([exports.DETAIL, exports.TEXT, exports.ORDER, exports.TOOLTIP], channel);
}
exports.hasScale = hasScale;
// Position does not work with ordinal (lookup) scale and sequential (which is only for color)
var POSITION_SCALE_TYPE_INDEX = util_1.toSet(util_1.without(scale_1.SCALE_TYPES, ['ordinal', 'sequential']));
function supportScaleType(channel, scaleType) {
switch (channel) {
case exports.ROW:
case exports.COLUMN:
return scaleType === 'band'; // row / column currently supports band only
case exports.X:
case exports.Y:
case exports.SIZE: // TODO: size and opacity can support ordinal with more modification
case exports.OPACITY:
// Although it generally doesn't make sense to use band with size and opacity,
// it can also work since we use band: 0.5 to get midpoint.
return scaleType in POSITION_SCALE_TYPE_INDEX;
case exports.COLOR:
return scaleType !== 'band'; // band does not make sense with color
case exports.SHAPE:
return scaleType === 'ordinal'; // shape = lookup only
}
/* istanbul ignore next: it should never reach here */
return false;
}
exports.supportScaleType = supportScaleType;
function rangeType(channel) {
switch (channel) {
case exports.X:
case exports.Y:
case exports.SIZE:
case exports.OPACITY:
// X2 and Y2 use X and Y scales, so they similarly have continuous range.
case exports.X2:
case exports.Y2:
return 'continuous';
case exports.ROW:
case exports.COLUMN:
case exports.SHAPE:
// TEXT and TOOLTIP have no scale but have discrete output
case exports.TEXT:
case exports.TOOLTIP:
return 'discrete';
// Color can be either continuous or discrete, depending on scale type.
case exports.COLOR:
return 'flexible';
// No scale, no range type.
case exports.DETAIL:
case exports.ORDER:
return undefined;
}
/* istanbul ignore next: should never reach here. */
throw new Error('getSupportedRole not implemented for ' + channel);
}
exports.rangeType = rangeType;
},{"./scale":97,"./util":107}],9:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var mainAxisReducer = getAxisReducer('main');
var gridAxisReducer = getAxisReducer('grid');
function getAxisReducer(axisType) {
return function (axes, axis) {
if (axis[axisType]) {
// Need to cast here so it's not longer partial type.
axes.push(axis[axisType].combine());
}
return axes;
};
}
function assembleAxes(axisComponents) {
return [].concat(axisComponents.x ? [].concat(axisComponents.x.reduce(mainAxisReducer, []), axisComponents.x.reduce(gridAxisReducer, [])) : [], axisComponents.y ? [].concat(axisComponents.y.reduce(mainAxisReducer, []), axisComponents.y.reduce(gridAxisReducer, [])) : []);
}
exports.assembleAxes = assembleAxes;
},{}],10:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var split_1 = require("../split");
var AxisComponentPart = (function (_super) {
tslib_1.__extends(AxisComponentPart, _super);
function AxisComponentPart() {
return _super !== null && _super.apply(this, arguments) || this;
}
return AxisComponentPart;
}(split_1.Split));
exports.AxisComponentPart = AxisComponentPart;
},{"../split":79,"tslib":114}],11:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var scale_1 = require("../../scale");
var type_1 = require("../../type");
var util_1 = require("../../util");
var common_1 = require("../common");
function labels(model, channel, specifiedLabelsSpec, def) {
var fieldDef = model.fieldDef(channel) ||
(channel === 'x' ? model.fieldDef('x2') :
channel === 'y' ? model.fieldDef('y2') :
undefined);
var axis = model.axis(channel);
var config = model.config;
var labelsSpec = {};
// Text
if (fieldDef.type === type_1.TEMPORAL) {
var isUTCScale = model.getScaleComponent(channel).get('type') === scale_1.ScaleType.UTC;
labelsSpec.text = {
signal: common_1.timeFormatExpression('datum.value', fieldDef.timeUnit, axis.format, config.axis.shortTimeLabels, config.timeFormat, isUTCScale)
};
}
// Label Angle
var angle = labelAngle(axis, channel, fieldDef);
if (angle) {
labelsSpec.angle = { value: angle };
}
if (labelsSpec.angle && channel === 'x') {
var align = labelAlign(angle, def.get('orient'));
if (align) {
labelsSpec.align = { value: align };
}
// Auto set baseline if x is rotated by 90, or -90
if (util_1.contains([90, 270], angle)) {
labelsSpec.baseline = { value: 'middle' };
}
}
labelsSpec = tslib_1.__assign({}, labelsSpec, specifiedLabelsSpec);
return util_1.keys(labelsSpec).length === 0 ? undefined : labelsSpec;
}
exports.labels = labels;
function labelAngle(axis, channel, fieldDef) {
if (axis.labelAngle !== undefined) {
// Make angle within [0,360)
return ((axis.labelAngle % 360) + 360) % 360;
}
else {
// auto rotate for X
if (channel === channel_1.X && (util_1.contains([type_1.NOMINAL, type_1.ORDINAL], fieldDef.type) || !!fieldDef.bin || fieldDef.type === type_1.TEMPORAL)) {
return 270;
}
}
return undefined;
}
exports.labelAngle = labelAngle;
function labelAlign(angle, orient) {
if (angle && angle > 0) {
if (angle > 180) {
return orient === 'top' ? 'left' : 'right';
}
else if (angle < 180) {
return orient === 'top' ? 'right' : 'left';
}
}
return undefined;
}
exports.labelAlign = labelAlign;
},{"../../channel":8,"../../scale":97,"../../type":106,"../../util":107,"../common":16,"tslib":114}],12:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var axis_1 = require("../../axis");
var channel_1 = require("../../channel");
var util_1 = require("../../util");
var common_1 = require("../common");
var resolve_1 = require("../resolve");
var split_1 = require("../split");
var component_1 = require("./component");
var encode = require("./encode");
var rules = require("./rules");
var AXIS_PARTS = ['domain', 'grid', 'labels', 'ticks', 'title'];
function parseUnitAxis(model) {
return channel_1.SPATIAL_SCALE_CHANNELS.reduce(function (axis, channel) {
if (model.axis(channel)) {
var axisComponent = {};
// TODO: support multiple axis
var main = parseMainAxis(channel, model);
if (main && isVisibleAxis(main)) {
axisComponent.main = main;
}
var grid = parseGridAxis(channel, model);
if (grid && isVisibleAxis(grid)) {
axisComponent.grid = grid;
}
axis[channel] = [axisComponent];
}
return axis;
}, {});
}
exports.parseUnitAxis = parseUnitAxis;
var OPPOSITE_ORIENT = {
bottom: 'top',
top: 'bottom',
left: 'right',
right: 'left'
};
function parseLayerAxis(model) {
var _a = model.component, axes = _a.axes, resolve = _a.resolve;
var axisCount = { top: 0, bottom: 0, right: 0, left: 0 };
var _loop_1 = function (child) {
child.parseAxisAndHeader();
util_1.keys(child.component.axes).forEach(function (channel) {
var channelResolve = model.component.resolve[channel];
channelResolve.axis = resolve_1.parseGuideResolve(model.component.resolve, channel);
if (channelResolve.axis === 'shared') {
// If the resolve says shared (and has not been overridden)
// We will try to merge and see if there is a conflict
axes[channel] = mergeAxisComponents(axes[channel], child.component.axes[channel]);
if (!axes[channel]) {
// If merge returns nothing, there is a conflict so we cannot make the axis shared.
// Thus, mark axis as independent and remove the axis component.
channelResolve.axis = 'independent';
delete axes[channel];
}
}
});
};
for (var _i = 0, _b = model.children; _i < _b.length; _i++) {
var child = _b[_i];
_loop_1(child);
}
// Move axes to layer's axis component and merge shared axes
['x', 'y'].forEach(function (channel) {
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
if (!child.component.axes[channel]) {
// skip if the child does not have a particular axis
continue;
}
if (resolve[channel].axis === 'independent') {
// If axes are independent, concat the axisComponent array.
axes[channel] = (axes[channel] || []).concat(child.component.axes[channel]);
// Automatically adjust orient
child.component.axes[channel].forEach(function (axisComponent) {
var _a = axisComponent.main.getWithExplicit('orient'), orient = _a.value, explicit = _a.explicit;
if (axisCount[orient] > 0 && !explicit) {
// Change axis orient if the number do not match
var oppositeOrient = OPPOSITE_ORIENT[orient];
if (axisCount[orient] > axisCount[oppositeOrient]) {
axisComponent.main.set('orient', oppositeOrient, false);
}
}
axisCount[orient]++;
// TODO(https://github.com/vega/vega-lite/issues/2634): automaticaly add extra offset?
});
}
// After merging, make sure to remove axes from child
delete child.component.axes[channel];
}
});
}
exports.parseLayerAxis = parseLayerAxis;
function mergeAxisComponents(mergedAxisCmpts, childAxisCmpts) {
if (mergedAxisCmpts) {
if (mergedAxisCmpts.length !== childAxisCmpts.length) {
return undefined; // Cannot merge axis component with different number of axes.
}
var length_1 = mergedAxisCmpts.length;
for (var i = 0; i < length_1; i++) {
var mergedMain = mergedAxisCmpts[i].main;
var childMain = childAxisCmpts[i].main;
if ((!!mergedMain) !== (!!childMain)) {
return undefined;
}
else if (mergedMain && childMain) {
var mergedOrient = mergedMain.getWithExplicit('orient');
var childOrient = childMain.getWithExplicit('orient');
if (mergedOrient.explicit && childOrient.explicit && mergedOrient.value !== childOrient.value) {
// TODO: throw warning if resolve is explicit (We don't have info about explicit/implicit resolve yet.)
// Cannot merge due to inconsistent orient
return undefined;
}
else {
mergedAxisCmpts[i].main = mergeAxisComponentPart(mergedMain, childMain);
}
}
var mergedGrid = mergedAxisCmpts[i].grid;
var childGrid = childAxisCmpts[i].grid;
if ((!!mergedGrid) !== (!!childGrid)) {
return undefined;
}
else if (mergedGrid && childGrid) {
mergedAxisCmpts[i].grid = mergeAxisComponentPart(mergedGrid, childGrid);
}
}
}
else {
// For first one, return a copy of the child
return childAxisCmpts.map(function (axisComponent) { return (tslib_1.__assign({}, (axisComponent.main ? { main: axisComponent.main.clone() } : {}), (axisComponent.grid ? { grid: axisComponent.grid.clone() } : {}))); });
}
return mergedAxisCmpts;
}
function mergeAxisComponentPart(merged, child) {
var _loop_2 = function (prop) {
var mergedValueWithExplicit = split_1.mergeValuesWithExplicit(merged.getWithExplicit(prop), child.getWithExplicit(prop), prop, 'axis',
// Tie breaker function
function (v1, v2) {
switch (prop) {
case 'title':
return common_1.titleMerger(v1, v2);
case 'gridScale':
return {
explicit: v1.explicit,
value: v1.value || v2.value
};
}
return split_1.defaultTieBreaker(v1, v2, prop, 'axis');
});
merged.setWithExplicit(prop, mergedValueWithExplicit);
};
for (var _i = 0, VG_AXIS_PROPERTIES_1 = axis_1.VG_AXIS_PROPERTIES; _i < VG_AXIS_PROPERTIES_1.length; _i++) {
var prop = VG_AXIS_PROPERTIES_1[_i];
_loop_2(prop);
}
return merged;
}
function isFalseOrNull(v) {
return v === false || v === null;
}
/**
* Return if an axis is visible (shows at least one part of the axis).
*/
function isVisibleAxis(axis) {
return util_1.some(AXIS_PARTS, function (part) { return hasAxisPart(axis, part); });
}
function hasAxisPart(axis, part) {
// FIXME(https://github.com/vega/vega-lite/issues/2552) this method can be wrong if users use a Vega theme.
if (part === 'axis') {
return true;
}
if (part === 'grid' || part === 'title') {
return !!axis.get(part);
}
// Other parts are enabled by default, so they should not be false or null.
return !isFalseOrNull(axis.get(part));
}
/**
* Make an inner axis for showing grid for shared axis.
*/
function parseGridAxis(channel, model) {
// FIXME: support adding ticks for grid axis that are inner axes of faceted plots.
return parseAxis(channel, model, true);
}
exports.parseGridAxis = parseGridAxis;
function parseMainAxis(channel, model) {
return parseAxis(channel, model, false);
}
exports.parseMainAxis = parseMainAxis;
function parseAxis(channel, model, isGridAxis) {
var axis = model.axis(channel);
var axisComponent = new component_1.AxisComponentPart({}, { scale: model.scaleName(channel) } // implicit
);
// 1.2. Add properties
axis_1.AXIS_PROPERTIES.forEach(function (property) {
var value = getProperty(property, axis, channel, model, isGridAxis);
if (value !== undefined) {
var explicit = property === 'values' ?
!!axis.values :
value === axis[property];
axisComponent.set(property, value, explicit);
}
});
// Special case for gridScale since gridScale is not a Vega-Lite Axis property.
var gridScale = rules.gridScale(model, channel, isGridAxis);
if (gridScale !== undefined) {
axisComponent.set('gridScale', gridScale, false);
}
// 2) Add guide encode definition groups
var axisEncoding = axis.encoding || {};
var axisEncode = AXIS_PARTS.reduce(function (e, part) {
if (!hasAxisPart(axisComponent, part)) {
// No need to create encode for a disabled part.
return e;
}
var value = part === 'labels' ?
encode.labels(model, channel, axisEncoding.labels || {}, axisComponent) :
axisEncoding[part] || {};
if (value !== undefined && util_1.keys(value).length > 0) {
e[part] = { update: value };
}
return e;
}, {});
// FIXME: By having encode as one property, we won't have fine grained encode merging.
if (util_1.keys(axisEncode).length > 0) {
axisComponent.set('encode', axisEncode, !!axis.encoding || !!axis.labelAngle);
}
return axisComponent;
}
function getProperty(property, specifiedAxis, channel, model, isGridAxis) {
var fieldDef = model.fieldDef(channel);
if ((isGridAxis && axis_1.AXIS_PROPERTY_TYPE[property] === 'main') ||
(!isGridAxis && axis_1.AXIS_PROPERTY_TYPE[property] === 'grid')) {
// Do not apply unapplicable properties
return undefined;
}
switch (property) {
case 'domain':
return rules.domain(property, specifiedAxis, isGridAxis, channel);
case 'format':
return common_1.numberFormat(fieldDef, specifiedAxis.format, model.config);
case 'grid': {
var scaleType = model.component.scales[channel].get('type');
return common_1.getSpecifiedOrDefaultValue(specifiedAxis.grid, rules.grid(scaleType, fieldDef));
}
case 'labels':
return isGridAxis ? false : specifiedAxis.labels;
case 'labelOverlap': {
var scaleType = model.component.scales[channel].get('type');
return rules.labelOverlap(fieldDef, specifiedAxis, channel, scaleType);
}
case 'minExtent': {
var scaleType = model.component.scales[channel].get('type');
return rules.minMaxExtent(specifiedAxis.minExtent, isGridAxis);
}
case 'maxExtent': {
var scaleType = model.component.scales[channel].get('type');
return rules.minMaxExtent(specifiedAxis.maxExtent, isGridAxis);
}
case 'orient':
return common_1.getSpecifiedOrDefaultValue(specifiedAxis.orient, rules.orient(channel));
case 'tickCount': {
var scaleType = model.component.scales[channel].get('type');
var sizeType = channel === 'x' ? 'width' : channel === 'y' ? 'height' : undefined;
var size = sizeType ? model.getSizeSignalRef(sizeType)
: undefined;
return common_1.getSpecifiedOrDefaultValue(specifiedAxis.tickCount, rules.tickCount(channel, fieldDef, scaleType, size));
}
case 'ticks':
return rules.ticks(property, specifiedAxis, isGridAxis, channel);
case 'title':
return common_1.getSpecifiedOrDefaultValue(specifiedAxis.title, rules.title(specifiedAxis.titleMaxLength, fieldDef, model.config));
case 'values':
return rules.values(specifiedAxis, model, fieldDef);
case 'zindex':
return common_1.getSpecifiedOrDefaultValue(specifiedAxis.zindex, rules.zindex(isGridAxis));
}
// Otherwise, return specified property.
return specifiedAxis[property];
}
},{"../../axis":6,"../../channel":8,"../../util":107,"../common":16,"../resolve":59,"../split":79,"./component":10,"./encode":11,"./rules":13,"tslib":114}],13:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var bin_1 = require("../../bin");
var channel_1 = require("../../channel");
var datetime_1 = require("../../datetime");
var fielddef_1 = require("../../fielddef");
var log = require("../../log");
var scale_1 = require("../../scale");
var util_1 = require("../../util");
function domainAndTicks(property, specifiedAxis, isGridAxis, channel) {
if (isGridAxis) {
return false;
}
return specifiedAxis[property];
}
exports.domainAndTicks = domainAndTicks;
exports.domain = domainAndTicks;
exports.ticks = domainAndTicks;
// TODO: we need to refactor this method after we take care of config refactoring
/**
* Default rules for whether to show a grid should be shown for a channel.
* If `grid` is unspecified, the default value is `true` for ordinal scales that are not binned
*/
function grid(scaleType, fieldDef) {
return !scale_1.hasDiscreteDomain(scaleType) && !fieldDef.bin;
}
exports.grid = grid;
function gridScale(model, channel, isGridAxis) {
if (isGridAxis) {
var gridChannel = channel === 'x' ? 'y' : 'x';
if (model.getScaleComponent(gridChannel)) {
return model.scaleName(gridChannel);
}
}
return undefined;
}
exports.gridScale = gridScale;
function labelOverlap(fieldDef, specifiedAxis, channel, scaleType) {
// do not prevent overlap for nominal data because there is no way to infer what the missing labels are
if ((channel === 'x' || channel === 'y') && fieldDef.type !== 'nominal') {
if (scaleType === 'log') {
return 'greedy';
}
return true;
}
return undefined;
}
exports.labelOverlap = labelOverlap;
function minMaxExtent(specifiedExtent, isGridAxis) {
if (isGridAxis) {
// Always return 0 to make sure that `config.axis*.minExtent` and `config.axis*.maxExtent`
// would not affect gridAxis
return 0;
}
else {
return specifiedExtent;
}
}
exports.minMaxExtent = minMaxExtent;
function orient(channel) {
switch (channel) {
case channel_1.X:
return 'bottom';
case channel_1.Y:
return 'left';
}
/* istanbul ignore next: This should never happen. */
throw new Error(log.message.INVALID_CHANNEL_FOR_AXIS);
}
exports.orient = orient;
function tickCount(channel, fieldDef, scaleType, size) {
if (!fieldDef.bin && !scale_1.hasDiscreteDomain(scaleType) && scaleType !== 'log') {
// Vega's default tickCount often lead to a lot of label occlusion on X without 90 degree rotation
// Thus, we set it to 5 for width = 200
// and set the same value for y for consistency.
return { signal: "ceil(" + size.signal + "/40)" };
}
return undefined;
}
exports.tickCount = tickCount;
function title(maxLength, fieldDef, config) {
// if not defined, automatically determine axis title from field def
var fieldTitle = fielddef_1.title(fieldDef, config);
return maxLength ? util_1.truncate(fieldTitle, maxLength) : fieldTitle;
}
exports.title = title;
function values(specifiedAxis, model, fieldDef) {
var vals = specifiedAxis.values;
if (specifiedAxis.values && datetime_1.isDateTime(vals[0])) {
return vals.map(function (dt) {
// normalize = true as end user won't put 0 = January
return { signal: datetime_1.dateTimeExpr(dt, true) };
});
}
if (!vals && fieldDef.bin) {
var signal = model.getName(bin_1.binToString(fieldDef.bin) + "_" + fieldDef.field + "_bins");
return { signal: "sequence(" + signal + ".start, " + signal + ".stop + " + signal + ".step, " + signal + ".step)" };
}
return vals;
}
exports.values = values;
function zindex(isGridAxis) {
if (isGridAxis) {
// if grid is true, need to put layer on the back so that grid is behind marks
return 0;
}
return 1; // otherwise return undefined and use Vega's default.
}
exports.zindex = zindex;
},{"../../bin":7,"../../channel":8,"../../datetime":86,"../../fielddef":89,"../../log":94,"../../scale":97,"../../util":107}],14:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var util_1 = require("../util");
var parse_1 = require("./data/parse");
var assemble_1 = require("./layoutsize/assemble");
var model_1 = require("./model");
var assemble_2 = require("./scale/assemble");
var BaseConcatModel = (function (_super) {
tslib_1.__extends(BaseConcatModel, _super);
function BaseConcatModel(spec, parent, parentGivenName, config, resolve) {
return _super.call(this, spec, parent, parentGivenName, config, resolve) || this;
}
BaseConcatModel.prototype.parseData = function () {
this.component.data = parse_1.parseData(this);
this.children.forEach(function (child) {
child.parseData();
});
};
BaseConcatModel.prototype.parseSelection = function () {
var _this = this;
// Merge selections up the hierarchy so that they may be referenced
// across unit specs. Persist their definitions within each child
// to assemble signals which remain within output Vega unit groups.
this.component.selection = {};
var _loop_1 = function (child) {
child.parseSelection();
util_1.keys(child.component.selection).forEach(function (key) {
_this.component.selection[key] = child.component.selection[key];
});
};
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
_loop_1(child);
}
};
BaseConcatModel.prototype.parseMarkGroup = function () {
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
child.parseMarkGroup();
}
};
BaseConcatModel.prototype.parseAxisAndHeader = function () {
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
child.parseAxisAndHeader();
}
// TODO(#2415): support shared axes
};
BaseConcatModel.prototype.assembleScales = function () {
return assemble_2.assembleScaleForModelAndChildren(this);
};
BaseConcatModel.prototype.assembleSelectionTopLevelSignals = function (signals) {
return this.children.reduce(function (sg, child) { return child.assembleSelectionTopLevelSignals(sg); }, signals);
};
BaseConcatModel.prototype.assembleSelectionSignals = function () {
this.children.forEach(function (child) { return child.assembleSelectionSignals(); });
return [];
};
BaseConcatModel.prototype.assembleLayoutSignals = function () {
return this.children.reduce(function (signals, child) {
return signals.concat(child.assembleLayoutSignals());
}, assemble_1.assembleLayoutSignals(this));
};
BaseConcatModel.prototype.assembleSelectionData = function (data) {
return this.children.reduce(function (db, child) { return child.assembleSelectionData(db); }, []);
};
BaseConcatModel.prototype.assembleMarks = function () {
// only children have marks
return this.children.map(function (child) {
var title = child.assembleTitle();
var style = child.assembleGroupStyle();
var layoutSizeEncodeEntry = child.assembleLayoutSize();
return tslib_1.__assign({ type: 'group', name: child.getName('group') }, (title ? { title: title } : {}), (style ? { style: style } : {}), (layoutSizeEncodeEntry ? {
encode: {
update: layoutSizeEncodeEntry
}
} : {}), child.assembleGroup());
});
};
return BaseConcatModel;
}(model_1.Model));
exports.BaseConcatModel = BaseConcatModel;
},{"../util":107,"./data/parse":29,"./layoutsize/assemble":37,"./model":56,"./scale/assemble":60,"tslib":114}],15:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var log = require("../log");
var spec_1 = require("../spec");
var concat_1 = require("./concat");
var facet_1 = require("./facet");
var layer_1 = require("./layer");
var repeat_1 = require("./repeat");
var unit_1 = require("./unit");
function buildModel(spec, parent, parentGivenName, unitSize, repeater, config) {
if (spec_1.isFacetSpec(spec)) {
return new facet_1.FacetModel(spec, parent, parentGivenName, repeater, config);
}
if (spec_1.isLayerSpec(spec)) {
return new layer_1.LayerModel(spec, parent, parentGivenName, unitSize, repeater, config);
}
if (spec_1.isUnitSpec(spec)) {
return new unit_1.UnitModel(spec, parent, parentGivenName, unitSize, repeater, config);
}
if (spec_1.isRepeatSpec(spec)) {
return new repeat_1.RepeatModel(spec, parent, parentGivenName, repeater, config);
}
if (spec_1.isConcatSpec(spec)) {
return new concat_1.ConcatModel(spec, parent, parentGivenName, repeater, config);
}
throw new Error(log.message.INVALID_SPEC);
}
exports.buildModel = buildModel;
},{"../log":94,"../spec":100,"./concat":18,"./facet":34,"./layer":35,"./repeat":57,"./unit":80}],16:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var fielddef_1 = require("../fielddef");
var scale_1 = require("../scale");
var timeunit_1 = require("../timeunit");
var type_1 = require("../type");
var util_1 = require("../util");
function applyConfig(e, config, // TODO(#1842): consolidate MarkConfig | TextConfig?
propsList) {
for (var _i = 0, propsList_1 = propsList; _i < propsList_1.length; _i++) {
var property = propsList_1[_i];
var value = config[property];
if (value !== undefined) {
e[property] = { value: value };
}
}
return e;
}
exports.applyConfig = applyConfig;
function applyMarkConfig(e, model, propsList) {
for (var _i = 0, propsList_2 = propsList; _i < propsList_2.length; _i++) {
var property = propsList_2[_i];
var value = getMarkConfig(property, model.markDef, model.config);
if (value !== undefined) {
e[property] = { value: value };
}
}
return e;
}
exports.applyMarkConfig = applyMarkConfig;
function getStyles(mark) {
if (mark.style) {
return util_1.isArray(mark.style) ? mark.style : [mark.style];
}
return [mark.type];
}
exports.getStyles = getStyles;
/**
* Return value mark specific config property if exists.
* Otherwise, return general mark specific config.
*/
function getMarkConfig(prop, mark, config) {
// By default, read from mark config first!
var value = config.mark[prop];
// Then read mark specific config, which has higher precedence
var markSpecificConfig = config[mark.type];
if (markSpecificConfig[prop] !== undefined) {
value = markSpecificConfig[prop];
}
var styles = getStyles(mark);
for (var _i = 0, styles_1 = styles; _i < styles_1.length; _i++) {
var style = styles_1[_i];
var styleConfig = config.style[style];
// MarkConfig extends VgMarkConfig so a prop may not be a valid property for style
// However here we also check if it is defined, so it is okay to cast here
var p = prop;
if (styleConfig && styleConfig[p] !== undefined) {
value = styleConfig[p];
}
}
return value;
}
exports.getMarkConfig = getMarkConfig;
function formatSignalRef(fieldDef, specifiedFormat, expr, config, useBinRange) {
if (fieldDef.type === 'quantitative') {
var format = numberFormat(fieldDef, specifiedFormat, config);
if (fieldDef.bin) {
if (useBinRange) {
// For bin range, no need to apply format as the formula that creates range already include format
return { signal: fielddef_1.field(fieldDef, { expr: expr, binSuffix: 'range' }) };
}
else {
return {
signal: formatExpr(fielddef_1.field(fieldDef, { expr: expr, binSuffix: 'start' }), format) + " + '-' + " + formatExpr(fielddef_1.field(fieldDef, { expr: expr, binSuffix: 'end' }), format)
};
}
}
else {
return {
signal: "" + formatExpr(fielddef_1.field(fieldDef, { expr: expr }), format)
};
}
}
else if (fieldDef.type === 'temporal') {
var isUTCScale = fielddef_1.isScaleFieldDef(fieldDef) && fieldDef['scale'] && fieldDef['scale'].type === scale_1.ScaleType.UTC;
return {
signal: timeFormatExpression(fielddef_1.field(fieldDef, { expr: expr }), fieldDef.timeUnit, specifiedFormat, config.text.shortTimeLabels, config.timeFormat, isUTCScale)
};
}
else {
return { signal: "''+" + fielddef_1.field(fieldDef, { expr: expr }) };
}
}
exports.formatSignalRef = formatSignalRef;
function getSpecifiedOrDefaultValue(specifiedValue, defaultValue) {
if (specifiedValue !== undefined) {
return specifiedValue;
}
return defaultValue;
}
exports.getSpecifiedOrDefaultValue = getSpecifiedOrDefaultValue;
/**
* Returns number format for a fieldDef
*
* @param format explicitly specified format
*/
function numberFormat(fieldDef, specifiedFormat, config) {
if (fieldDef.type === type_1.QUANTITATIVE) {
// add number format for quantitative type only
// Specified format in axis/legend has higher precedence than fieldDef.format
if (specifiedFormat) {
return specifiedFormat;
}
// TODO: need to make this work correctly for numeric ordinal / nominal type
return config.numberFormat;
}
return undefined;
}
exports.numberFormat = numberFormat;
function formatExpr(field, format) {
return "format(" + field + ", '" + (format || '') + "')";
}
function numberFormatExpr(field, specifiedFormat, config) {
return formatExpr(field, specifiedFormat || config.numberFormat);
}
exports.numberFormatExpr = numberFormatExpr;
/**
* Returns the time expression used for axis/legend labels or text mark for a temporal field
*/
function timeFormatExpression(field, timeUnit, format, shortTimeLabels, timeFormatConfig, isUTCScale) {
if (!timeUnit || format) {
// If there is not time unit, or if user explicitly specify format for axis/legend/text.
var _format = format || timeFormatConfig; // only use config.timeFormat if there is no timeUnit.
if (isUTCScale) {
return "utcFormat(" + field + ", '" + _format + "')";
}
else {
return "timeFormat(" + field + ", '" + _format + "')";
}
}
else {
return timeunit_1.formatExpression(timeUnit, field, shortTimeLabels, isUTCScale);
}
}
exports.timeFormatExpression = timeFormatExpression;
/**
* Return Vega sort parameters (tuple of field and order).
*/
function sortParams(orderDef, fieldRefOption) {
return (util_1.isArray(orderDef) ? orderDef : [orderDef]).reduce(function (s, orderChannelDef) {
s.field.push(fielddef_1.field(orderChannelDef, tslib_1.__assign({ binSuffix: 'start' }, fieldRefOption)));
s.order.push(orderChannelDef.sort || 'ascending');
return s;
}, { field: [], order: [] });
}
exports.sortParams = sortParams;
function titleMerger(v1, v2) {
return {
explicit: v1.explicit,
value: v1.value === v2.value ?
v1.value :
v1.value + ', ' + v2.value // join title with comma if different
};
}
exports.titleMerger = titleMerger;
},{"../fielddef":89,"../scale":97,"../timeunit":102,"../type":106,"../util":107,"tslib":114}],17:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
/**
* Module for compiling Vega-lite spec into Vega spec.
*/
var config_1 = require("../config");
var log = require("../log");
var spec_1 = require("../spec");
var toplevelprops_1 = require("../toplevelprops");
var buildmodel_1 = require("./buildmodel");
var assemble_1 = require("./data/assemble");
var optimize_1 = require("./data/optimize");
function compile(inputSpec, logger) {
if (logger) {
// set the singleton logger to the provided logger
log.set(logger);
}
try {
// 1. initialize config
var config = config_1.initConfig(inputSpec.config);
// 2. Convert input spec into a normalized form
// (Decompose all extended unit specs into composition of unit spec.)
var spec = spec_1.normalize(inputSpec, config);
// 3. Instantiate the models with default config by doing a top-down traversal.
// This allows us to pass properties that child models derive from their parents via their constructors.
var model = buildmodel_1.buildModel(spec, null, '', undefined, undefined, config);
// 4. Parse parts of each model to produce components that can be merged
// and assembled easily as a part of a model.
// In this phase, we do a bottom-up traversal over the whole tree to
// parse for each type of components once (e.g., data, layout, mark, scale).
// By doing bottom-up traversal, we start parsing components of unit specs and
// then merge child components of parent composite specs.
//
// Please see inside model.parse() for order of different components parsed.
model.parse();
// 5. Optimize the datafow.
optimize_1.optimizeDataflow(model.component.data);
// 6. Assemble a Vega Spec from the parsed components.
return assembleTopLevelModel(model, getTopLevelProperties(inputSpec, config));
}
finally {
// Reset the singleton logger if a logger is provided
if (logger) {
log.reset();
}
}
}
exports.compile = compile;
function getTopLevelProperties(topLevelSpec, config) {
return tslib_1.__assign({}, toplevelprops_1.extractTopLevelProperties(config), toplevelprops_1.extractTopLevelProperties(topLevelSpec));
}
/*
* Assemble the top-level model.
*
* Note: this couldn't be `model.assemble()` since the top-level model
* needs some special treatment to generate top-level properties.
*/
function assembleTopLevelModel(model, topLevelProperties) {
// TODO: change type to become VgSpec
// Config with Vega-Lite only config removed.
var vgConfig = model.config ? config_1.stripAndRedirectConfig(model.config) : undefined;
// autoResize has to be put under autosize
var autoResize = topLevelProperties.autoResize, topLevelProps = tslib_1.__rest(topLevelProperties, ["autoResize"]);
var title = model.assembleTitle();
var style = model.assembleGroupStyle();
var output = tslib_1.__assign({ $schema: 'https://vega.github.io/schema/vega/v3.0.json' }, (model.description ? { description: model.description } : {}), {
// By using Vega layout, we don't support custom autosize
autosize: topLevelProperties.autoResize ? { type: 'pad', resize: true } : 'pad' }, topLevelProps, (title ? { title: title } : {}), (style ? { style: style } : {}), { data: [].concat(model.assembleSelectionData([]),
// only assemble data in the root
assemble_1.assembleRootData(model.component.data)) }, model.assembleGroup(model.assembleLayoutSignals().concat(model.assembleSelectionTopLevelSignals([]))), (vgConfig ? { config: vgConfig } : {}));
return {
spec: output
// TODO: add warning / errors here
};
}
},{"../config":84,"../log":94,"../spec":100,"../toplevelprops":104,"./buildmodel":15,"./data/assemble":20,"./data/optimize":27,"tslib":114}],18:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var spec_1 = require("../spec");
var baseconcat_1 = require("./baseconcat");
var buildmodel_1 = require("./buildmodel");
var parse_1 = require("./layoutsize/parse");
var ConcatModel = (function (_super) {
tslib_1.__extends(ConcatModel, _super);
function ConcatModel(spec, parent, parentGivenName, repeater, config) {
var _this = _super.call(this, spec, parent, parentGivenName, config, spec.resolve) || this;
_this.type = 'concat';
_this.isVConcat = spec_1.isVConcatSpec(spec);
_this.children = (spec_1.isVConcatSpec(spec) ? spec.vconcat : spec.hconcat).map(function (child, i) {
return buildmodel_1.buildModel(child, _this, _this.getName('concat_' + i), undefined, repeater, config);
});
return _this;
}
ConcatModel.prototype.parseLayoutSize = function () {
parse_1.parseConcatLayoutSize(this);
};
ConcatModel.prototype.parseAxisGroup = function () {
return null;
};
ConcatModel.prototype.assembleLayout = function () {
// TODO: allow customization
return tslib_1.__assign({ padding: { row: 10, column: 10 }, offset: 10 }, (this.isVConcat ? { columns: 1 } : {}), { bounds: 'full', align: 'all' });
};
return ConcatModel;
}(baseconcat_1.BaseConcatModel));
exports.ConcatModel = ConcatModel;
},{"../spec":100,"./baseconcat":14,"./buildmodel":15,"./layoutsize/parse":38,"tslib":114}],19:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var fielddef_1 = require("../../fielddef");
var log = require("../../log");
var type_1 = require("../../type");
var util_1 = require("../../util");
var dataflow_1 = require("./dataflow");
function addDimension(dims, fieldDef) {
if (fieldDef.bin) {
dims[fielddef_1.field(fieldDef, { binSuffix: 'start' })] = true;
dims[fielddef_1.field(fieldDef, { binSuffix: 'end' })] = true;
// We need the range only when the user explicitly forces a binned field to be ordinal (range used in axis and legend labels).
// We could check whether the axis or legend exists but that seems overkill. In axes and legends, we check hasDiscreteDomain(scaleType).
if (fieldDef.type === type_1.ORDINAL) {
dims[fielddef_1.field(fieldDef, { binSuffix: 'range' })] = true;
}
}
else {
dims[fielddef_1.field(fieldDef)] = true;
}
return dims;
}
function mergeMeasures(parentMeasures, childMeasures) {
for (var f in childMeasures) {
if (childMeasures.hasOwnProperty(f)) {
// when we merge a measure, we either have to add an aggregation operator or even a new field
var ops = childMeasures[f];
for (var op in ops) {
if (ops.hasOwnProperty(op)) {
if (f in parentMeasures) {
// add operator to existing measure field
parentMeasures[f][op] = ops[op];
}
else {
parentMeasures[f] = { op: ops[op] };
}
}
}
}
}
}
var AggregateNode = (function (_super) {
tslib_1.__extends(AggregateNode, _super);
/**
* @param dimensions string set for dimensions
* @param measures dictionary mapping field name => dict of aggregation functions and names to use
*/
function AggregateNode(dimensions, measures) {
var _this = _super.call(this) || this;
_this.dimensions = dimensions;
_this.measures = measures;
return _this;
}
AggregateNode.prototype.clone = function () {
return new AggregateNode(util_1.extend({}, this.dimensions), util_1.duplicate(this.measures));
};
AggregateNode.makeFromEncoding = function (model) {
var isAggregate = false;
model.forEachFieldDef(function (fd) {
if (fd.aggregate) {
isAggregate = true;
}
});
var meas = {};
var dims = {};
if (!isAggregate) {
// no need to create this node if the model has no aggregation
return null;
}
model.forEachFieldDef(function (fieldDef, channel) {
if (fieldDef.aggregate) {
if (fieldDef.aggregate === 'count') {
meas['*'] = meas['*'] || {};
meas['*']['count'] = fielddef_1.field(fieldDef, { aggregate: 'count' });
}
else {
meas[fieldDef.field] = meas[fieldDef.field] || {};
meas[fieldDef.field][fieldDef.aggregate] = fielddef_1.field(fieldDef);
// For scale channel with domain === 'unaggregated', add min/max so we can use their union as unaggregated domain
if (channel_1.isScaleChannel(channel) && model.scaleDomain(channel) === 'unaggregated') {
meas[fieldDef.field]['min'] = fielddef_1.field(fieldDef, { aggregate: 'min' });
meas[fieldDef.field]['max'] = fielddef_1.field(fieldDef, { aggregate: 'max' });
}
}
}
else {
addDimension(dims, fieldDef);
}
});
if ((util_1.keys(dims).length + util_1.keys(meas).length) === 0) {
return null;
}
return new AggregateNode(dims, meas);
};
AggregateNode.makeFromTransform = function (model, t) {
var dims = {};
var meas = {};
for (var _i = 0, _a = t.summarize; _i < _a.length; _i++) {
var s = _a[_i];
if (s.aggregate) {
if (s.aggregate === 'count') {
meas['*'] = meas['*'] || {};
meas['*']['count'] = s.as || fielddef_1.field(s);
}
else {
meas[s.field] = meas[s.field] || {};
meas[s.field][s.aggregate] = s.as || fielddef_1.field(s);
}
}
}
for (var _b = 0, _c = t.groupby; _b < _c.length; _b++) {
var s = _c[_b];
dims[s] = true;
}
if ((util_1.keys(dims).length + util_1.keys(meas).length) === 0) {
return null;
}
return new AggregateNode(dims, meas);
};
AggregateNode.prototype.merge = function (other) {
if (!util_1.differ(this.dimensions, other.dimensions)) {
mergeMeasures(this.measures, other.measures);
other.remove();
}
else {
log.debug('different dimensions, cannot merge');
}
};
AggregateNode.prototype.addDimensions = function (fields) {
var _this = this;
fields.forEach(function (f) { return _this.dimensions[f] = true; });
};
AggregateNode.prototype.dependentFields = function () {
var out = {};
util_1.keys(this.dimensions).forEach(function (f) { return out[f] = true; });
util_1.keys(this.measures).forEach(function (m) { return out[m] = true; });
return out;
};
AggregateNode.prototype.producedFields = function () {
var _this = this;
var out = {};
util_1.keys(this.measures).forEach(function (field) {
util_1.keys(_this.measures[field]).forEach(function (op) {
out[op + "_" + field] = true;
});
});
return out;
};
AggregateNode.prototype.assemble = function () {
var _this = this;
var ops = [];
var fields = [];
var as = [];
util_1.keys(this.measures).forEach(function (field) {
util_1.keys(_this.measures[field]).forEach(function (op) {
as.push(_this.measures[field][op]);
ops.push(op);
fields.push(field);
});
});
var result = {
type: 'aggregate',
groupby: util_1.keys(this.dimensions),
ops: ops,
fields: fields,
as: as
};
return result;
};
return AggregateNode;
}(dataflow_1.DataFlowNode));
exports.AggregateNode = AggregateNode;
},{"../../channel":8,"../../fielddef":89,"../../log":94,"../../type":106,"../../util":107,"./dataflow":22,"tslib":114}],20:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var data_1 = require("../../data");
var util_1 = require("../../util");
var aggregate_1 = require("./aggregate");
var bin_1 = require("./bin");
var dataflow_1 = require("./dataflow");
var facet_1 = require("./facet");
var formatparse_1 = require("./formatparse");
var nonpositivefilter_1 = require("./nonpositivefilter");
var nullfilter_1 = require("./nullfilter");
var source_1 = require("./source");
var stack_1 = require("./stack");
var timeunit_1 = require("./timeunit");
var transforms_1 = require("./transforms");
/**
* Print debug information for dataflow tree.
*/
function debug(node) {
console.log("" + node.constructor.name + (node.debugName ? " (" + node.debugName + ")" : '') + " -> " + (node.children.map(function (c) {
return "" + c.constructor.name + (c.debugName ? " (" + c.debugName + ")" : '');
})));
console.log(node);
node.children.forEach(debug);
}
function makeWalkTree(data) {
// to name datasources
var datasetIndex = 0;
/**
* Recursively walk down the tree.
*/
function walkTree(node, dataSource) {
if (node instanceof source_1.SourceNode) {
// If the source is a named data source or a data source with values, we need
// to put it in a different data source. Otherwise, Vega may override the data.
if (!data_1.isUrlData(node.data)) {
data.push(dataSource);
var newData = {
name: null,
source: dataSource.name,
transform: []
};
dataSource = newData;
}
}
if (node instanceof formatparse_1.ParseNode) {
if (node.parent instanceof source_1.SourceNode && !dataSource.source) {
// If node's parent is a root source and the data source does not refer to another data source, use normal format parse
dataSource.format = tslib_1.__assign({}, dataSource.format || {}, { parse: node.assembleFormatParse() });
}
else {
// Otherwise use Vega expression to parse
dataSource.transform = dataSource.transform.concat(node.assembleTransforms());
}
}
if (node instanceof facet_1.FacetNode) {
if (!dataSource.name) {
dataSource.name = "data_" + datasetIndex++;
}
if (!dataSource.source || dataSource.transform.length > 0) {
data.push(dataSource);
node.data = dataSource.name;
}
else {
node.data = dataSource.source;
}
node.assemble().forEach(function (d) { return data.push(d); });
// break here because the rest of the tree has to be taken care of by the facet.
return;
}
if (node instanceof transforms_1.FilterNode ||
node instanceof nullfilter_1.NullFilterNode ||
node instanceof transforms_1.CalculateNode ||
node instanceof aggregate_1.AggregateNode ||
node instanceof transforms_1.LookupNode ||
node instanceof transforms_1.IdentifierNode) {
dataSource.transform.push(node.assemble());
}
if (node instanceof nonpositivefilter_1.NonPositiveFilterNode ||
node instanceof bin_1.BinNode ||
node instanceof timeunit_1.TimeUnitNode ||
node instanceof stack_1.StackNode) {
dataSource.transform = dataSource.transform.concat(node.assemble());
}
if (node instanceof aggregate_1.AggregateNode) {
if (!dataSource.name) {
dataSource.name = "data_" + datasetIndex++;
}
}
if (node instanceof dataflow_1.OutputNode) {
if (dataSource.source && dataSource.transform.length === 0) {
node.setSource(dataSource.source);
}
else if (node.parent instanceof dataflow_1.OutputNode) {
// Note that an output node may be required but we still do not assemble a
// separate data source for it.
node.setSource(dataSource.name);
}
else {
if (!dataSource.name) {
dataSource.name = "data_" + datasetIndex++;
}
// Here we set the name of the datasource we generated. From now on
// other assemblers can use it.
node.setSource(dataSource.name);
// if this node has more than one child, we will add a datasource automatically
if (node.numChildren() === 1) {
data.push(dataSource);
var newData = {
name: null,
source: dataSource.name,
transform: []
};
dataSource = newData;
}
}
}
switch (node.numChildren()) {
case 0:
// done
if (node instanceof dataflow_1.OutputNode && (!dataSource.source || dataSource.transform.length > 0)) {
// do not push empty datasources that are simply references
data.push(dataSource);
}
break;
case 1:
walkTree(node.children[0], dataSource);
break;
default:
if (!dataSource.name) {
dataSource.name = "data_" + datasetIndex++;
}
var source_2 = dataSource.name;
if (!dataSource.source || dataSource.transform.length > 0) {
data.push(dataSource);
}
else {
source_2 = dataSource.source;
}
node.children.forEach(function (child) {
var newData = {
name: null,
source: source_2,
transform: []
};
walkTree(child, newData);
});
break;
}
}
return walkTree;
}
/**
* Assemble data sources that are derived from faceted data.
*/
function assembleFacetData(root) {
var data = [];
var walkTree = makeWalkTree(data);
root.children.forEach(function (child) { return walkTree(child, {
source: root.name,
name: null,
transform: []
}); });
return data;
}
exports.assembleFacetData = assembleFacetData;
/**
* Create Vega Data array from a given compiled model and append all of them to the given array
*
* @param model
* @param data array
* @return modified data array
*/
function assembleRootData(dataComponent) {
var roots = util_1.vals(dataComponent.sources);
var data = [];
// roots.forEach(debug);
var walkTree = makeWalkTree(data);
var sourceIndex = 0;
roots.forEach(function (root) {
// assign a name if the source does not have a name yet
if (!root.hasName()) {
root.dataName = "source_" + sourceIndex++;
}
var newData = root.assemble();
walkTree(root, newData);
});
// remove empty transform arrays for cleaner output
data.forEach(function (d) {
if (d.transform.length === 0) {
delete d.transform;
}
});
// move sources without transforms (the ones that are potentially used in lookups) to the beginning
data.sort(function (a, b) { return (a.transform || []).length === 0 ? -1 : ((b.transform || []).length === 0 ? 1 : 0); });
// now fix the from references in lookup transforms
for (var _i = 0, data_2 = data; _i < data_2.length; _i++) {
var d = data_2[_i];
for (var _a = 0, _b = d.transform || []; _a < _b.length; _a++) {
var t = _b[_a];
if (t.type === 'lookup') {
t.from = dataComponent.outputNodes[t.from].getSource();
}
}
}
return data;
}
exports.assembleRootData = assembleRootData;
},{"../../data":85,"../../util":107,"./aggregate":19,"./bin":21,"./dataflow":22,"./facet":23,"./formatparse":24,"./nonpositivefilter":25,"./nullfilter":26,"./source":30,"./stack":31,"./timeunit":32,"./transforms":33,"tslib":114}],21:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var bin_1 = require("../../bin");
var fielddef_1 = require("../../fielddef");
var util_1 = require("../../util");
var common_1 = require("../common");
var model_1 = require("../model");
var dataflow_1 = require("./dataflow");
function rangeFormula(model, fieldDef, channel, config) {
var discreteDomain = model.hasDiscreteDomain(channel);
if (discreteDomain) {
// read format from axis or legend, if there is no format then use config.numberFormat
var guide = model_1.isUnitModel(model) ? (model.axis(channel) || model.legend(channel) || {}) : {};
var startField = fielddef_1.field(fieldDef, { expr: 'datum', binSuffix: 'start' });
var endField = fielddef_1.field(fieldDef, { expr: 'datum', binSuffix: 'end' });
return {
formulaAs: fielddef_1.field(fieldDef, { binSuffix: 'range' }),
formula: common_1.numberFormatExpr(startField, guide.format, config) + " + \" - \" + " + common_1.numberFormatExpr(endField, guide.format, config)
};
}
return {};
}
function binKey(bin, field) {
return bin_1.binToString(bin) + "_" + field;
}
function createBinComponent(bin, t, model, key) {
return {
bin: bin,
field: t.field,
as: [fielddef_1.field(t, { binSuffix: 'start' }), fielddef_1.field(t, { binSuffix: 'end' })],
signal: model.getName(key + "_bins"),
extentSignal: model.getName(key + '_extent')
};
}
var BinNode = (function (_super) {
tslib_1.__extends(BinNode, _super);
function BinNode(bins) {
var _this = _super.call(this) || this;
_this.bins = bins;
return _this;
}
BinNode.prototype.clone = function () {
return new BinNode(util_1.duplicate(this.bins));
};
BinNode.makeBinFromEncoding = function (model) {
var bins = model.reduceFieldDef(function (binComponent, fieldDef, channel) {
var fieldDefBin = fieldDef.bin;
if (fieldDefBin) {
var bin = fielddef_1.normalizeBin(fieldDefBin, undefined) || {};
var key = binKey(bin, fieldDef.field);
if (!(key in binComponent)) {
binComponent[key] = createBinComponent(bin, fieldDef, model, key);
}
binComponent[key] = tslib_1.__assign({}, binComponent[key], rangeFormula(model, fieldDef, channel, model.config));
}
return binComponent;
}, {});
if (util_1.keys(bins).length === 0) {
return null;
}
return new BinNode(bins);
};
BinNode.makeFromTransform = function (model, t) {
var bins = {};
var bin = fielddef_1.normalizeBin(t.bin, undefined) || {};
var key = binKey(bin, t.field);
return new BinNode((_a = {},
_a[key] = createBinComponent(bin, t, model, key),
_a));
var _a;
};
BinNode.prototype.merge = function (other) {
this.bins = util_1.extend(other.bins);
other.remove();
};
BinNode.prototype.producedFields = function () {
var out = {};
util_1.vals(this.bins).forEach(function (c) {
c.as.forEach(function (f) { return out[f] = true; });
});
return out;
};
BinNode.prototype.dependentFields = function () {
var out = {};
util_1.vals(this.bins).forEach(function (c) {
out[c.field] = true;
});
return out;
};
BinNode.prototype.assemble = function () {
return util_1.flatten(util_1.vals(this.bins).map(function (bin) {
var transform = [];
var binTrans = tslib_1.__assign({ type: 'bin', field: bin.field, as: bin.as, signal: bin.signal }, bin.bin);
if (!bin.bin.extent) {
transform.push({
type: 'extent',
field: bin.field,
signal: bin.extentSignal
});
binTrans.extent = { signal: bin.extentSignal };
}
transform.push(binTrans);
if (bin.formula) {
transform.push({
type: 'formula',
expr: bin.formula,
as: bin.formulaAs
});
}
return transform;
}));
};
return BinNode;
}(dataflow_1.DataFlowNode));
exports.BinNode = BinNode;
},{"../../bin":7,"../../fielddef":89,"../../util":107,"../common":16,"../model":56,"./dataflow":22,"tslib":114}],22:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
/**
* A node in the dataflow tree.
*/
var DataFlowNode = (function () {
function DataFlowNode(debugName) {
this.debugName = debugName;
this._children = [];
this._parent = null;
}
/**
* Clone this node with a deep copy but don't clone links to children or parents.
*/
DataFlowNode.prototype.clone = function () {
throw new Error('Cannot clone node');
};
/**
* Set of fields that are being created by this node.
*/
DataFlowNode.prototype.producedFields = function () {
return {};
};
DataFlowNode.prototype.dependentFields = function () {
return {};
};
Object.defineProperty(DataFlowNode.prototype, "parent", {
get: function () {
return this._parent;
},
/**
* Set the parent of the node and also add this not to the parent's children.
*/
set: function (parent) {
this._parent = parent;
parent.addChild(this);
},
enumerable: true,
configurable: true
});
Object.defineProperty(DataFlowNode.prototype, "children", {
get: function () {
return this._children;
},
enumerable: true,
configurable: true
});
DataFlowNode.prototype.numChildren = function () {
return this._children.length;
};
DataFlowNode.prototype.addChild = function (child) {
this._children.push(child);
};
DataFlowNode.prototype.removeChild = function (oldChild) {
this._children.splice(this._children.indexOf(oldChild), 1);
};
/**
* Remove node from the dataflow.
*/
DataFlowNode.prototype.remove = function () {
for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
var child = _a[_i];
child.parent = this._parent;
}
this._parent.removeChild(this);
};
/**
* Insert another node as a parent of this node.
*/
DataFlowNode.prototype.insertAsParentOf = function (other) {
var parent = other.parent;
parent.removeChild(this);
this.parent = parent;
other.parent = this;
};
DataFlowNode.prototype.swapWithParent = function () {
var parent = this._parent;
var newParent = parent.parent;
// reconnect the children
for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
var child = _a[_i];
child.parent = parent;
}
// remove old links
this._children = []; // equivalent to removing every child link one by one
parent.removeChild(this);
parent.parent.removeChild(parent);
// swap two nodes
this.parent = newParent;
parent.parent = this;
};
return DataFlowNode;
}());
exports.DataFlowNode = DataFlowNode;
var OutputNode = (function (_super) {
tslib_1.__extends(OutputNode, _super);
/**
* @param source The name of the source. Will change in assemble.
* @param type The type of the output node.
* @param refCounts A global ref counter map.
*/
function OutputNode(source, type, refCounts) {
var _this = _super.call(this, source) || this;
_this.type = type;
_this.refCounts = refCounts;
_this._source = _this._name = source;
if (_this.refCounts && !(_this._name in _this.refCounts)) {
_this.refCounts[_this._name] = 0;
}
return _this;
}
OutputNode.prototype.clone = function () {
var cloneObj = new this.constructor;
cloneObj.debugName = 'clone_' + this.debugName;
cloneObj._source = this._source;
cloneObj._name = 'clone_' + this._name;
cloneObj.type = this.type;
cloneObj.refCounts = this.refCounts;
cloneObj.refCounts[cloneObj._name] = 0;
return cloneObj;
};
/**
* Request the datasource name and increase the ref counter.
*
* During the parsing phase, this will return the simple name such as 'main' or 'raw'.
* It is crucial to request the name from an output node to mark it as a required node.
* If nobody ever requests the name, this datasource will not be instantiated in the assemble phase.
*
* In the assemble phase, this will return the correct name.
*/
OutputNode.prototype.getSource = function () {
this.refCounts[this._name]++;
return this._source;
};
OutputNode.prototype.isRequired = function () {
return !!this.refCounts[this._name];
};
OutputNode.prototype.setSource = function (source) {
this._source = source;
};
return OutputNode;
}(DataFlowNode));
exports.OutputNode = OutputNode;
},{"tslib":114}],23:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var scale_1 = require("../../scale");
var vega_schema_1 = require("../../vega.schema");
var domain_1 = require("../scale/domain");
var dataflow_1 = require("./dataflow");
/**
* A node that helps us track what fields we are faceting by.
*/
var FacetNode = (function (_super) {
tslib_1.__extends(FacetNode, _super);
/**
* @param model The facet model.
* @param name The name that this facet source will have.
* @param data The source data for this facet data.
*/
function FacetNode(model, name, data) {
var _this = _super.call(this) || this;
_this.model = model;
_this.name = name;
_this.data = data;
_this.childIndependentFieldWithStep = {};
if (model.facet.column) {
_this.columnField = model.field(channel_1.COLUMN);
_this.columnName = model.getName('column_domain');
}
if (model.facet.row) {
_this.rowField = model.field(channel_1.ROW);
_this.rowName = model.getName('row_domain');
}
for (var _i = 0, _a = ['x', 'y']; _i < _a.length; _i++) {
var channel = _a[_i];
var childScaleComponent = model.child.component.scales[channel];
if (childScaleComponent && !childScaleComponent.merged) {
var type = childScaleComponent.get('type');
var range = childScaleComponent.get('range');
if (scale_1.hasDiscreteDomain(type) && vega_schema_1.isVgRangeStep(range)) {
var field = domain_1.getFieldFromDomains(childScaleComponent.domains);
if (field) {
_this.childIndependentFieldWithStep[channel] = field;
}
else {
throw new Error('We do not yet support calculation of size for faceted union domain.');
}
}
}
}
return _this;
}
Object.defineProperty(FacetNode.prototype, "fields", {
get: function () {
var fields = [];
if (this.columnField) {
fields.push(this.columnField);
}
if (this.rowField) {
fields.push(this.rowField);
}
return fields;
},
enumerable: true,
configurable: true
});
/**
* The name to reference this source is its name.
*/
FacetNode.prototype.getSource = function () {
return this.name;
};
FacetNode.prototype.assembleRowColumnData = function (channel, crossedDataName) {
var childChannel = channel === 'row' ? 'y' : 'x';
var aggregateChildField = {};
if (this.childIndependentFieldWithStep[childChannel]) {
if (crossedDataName) {
aggregateChildField = {
// If there is a crossed data, calculate max
fields: ["distinct_" + this.childIndependentFieldWithStep[childChannel]],
ops: ['max'],
// Although it is technically a max, just name it distinct so it's easier to refer to it
as: ["distinct_" + this.childIndependentFieldWithStep[childChannel]]
};
}
else {
aggregateChildField = {
// If there is no crossed data, just calculate distinct
fields: [this.childIndependentFieldWithStep[childChannel]],
ops: ['distinct']
};
}
}
return {
name: channel === 'row' ? this.rowName : this.columnName,
// Use data from the crossed one if it exist
source: crossedDataName || this.data,
transform: [tslib_1.__assign({ type: 'aggregate', groupby: [channel === 'row' ? this.rowField : this.columnField] }, aggregateChildField)]
};
};
FacetNode.prototype.assemble = function () {
var data = [];
var crossedDataName = null;
if (this.columnName && this.rowName && (this.childIndependentFieldWithStep.x || this.childIndependentFieldWithStep.y)) {
// Need to create a cross dataset to correctly calculate cardinality
crossedDataName = "cross_" + this.columnName + "_" + this.rowName;
var fields = [].concat(this.childIndependentFieldWithStep.x ? [this.childIndependentFieldWithStep.x] : [], this.childIndependentFieldWithStep.y ? [this.childIndependentFieldWithStep.y] : []);
var ops = fields.map(function () { return 'distinct'; });
data.push({
name: crossedDataName,
source: this.data,
transform: [{
type: 'aggregate',
groupby: [this.columnField, this.rowField],
fields: fields,
ops: ops
}]
});
}
if (this.columnName) {
data.push(this.assembleRowColumnData('column', crossedDataName));
}
if (this.rowName) {
data.push(this.assembleRowColumnData('row', crossedDataName));
}
return data;
};
return FacetNode;
}(dataflow_1.DataFlowNode));
exports.FacetNode = FacetNode;
},{"../../channel":8,"../../scale":97,"../../vega.schema":109,"../scale/domain":62,"./dataflow":22,"tslib":114}],24:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var aggregate_1 = require("../../aggregate");
var filter_1 = require("../../filter");
var log = require("../../log");
var logical_1 = require("../../logical");
var transform_1 = require("../../transform");
var type_1 = require("../../type");
var util_1 = require("../../util");
var model_1 = require("../model");
var dataflow_1 = require("./dataflow");
function parseExpression(field, parse) {
var f = "datum[" + util_1.stringValue(field) + "]";
if (parse === 'number') {
return "toNumber(" + f + ")";
}
else if (parse === 'boolean') {
return "toBoolean(" + f + ")";
}
else if (parse === 'string') {
return "toString(" + f + ")";
}
else if (parse === 'date') {
return "toDate(" + f + ")";
}
else if (parse.indexOf('date:') === 0) {
var specifier = parse.slice(5, parse.length);
return "timeParse(" + f + "," + specifier + ")";
}
else if (parse.indexOf('utc:') === 0) {
var specifier = parse.slice(4, parse.length);
return "utcParse(" + f + "," + specifier + ")";
}
else {
log.warn(log.message.unrecognizedParse(parse));
return null;
}
}
var ParseNode = (function (_super) {
tslib_1.__extends(ParseNode, _super);
function ParseNode(parse) {
var _this = _super.call(this) || this;
_this._parse = {};
_this._parse = parse;
return _this;
}
ParseNode.prototype.clone = function () {
return new ParseNode(util_1.duplicate(this.parse));
};
ParseNode.make = function (model) {
var parse = {};
var calcFieldMap = {};
(model.transforms || []).forEach(function (transform) {
if (transform_1.isCalculate(transform)) {
calcFieldMap[transform.as] = true;
}
else if (transform_1.isFilter(transform)) {
logical_1.forEachLeave(transform.filter, function (filter) {
if (filter_1.isEqualFilter(filter) || filter_1.isRangeFilter(filter) || filter_1.isOneOfFilter(filter)) {
if (filter.timeUnit) {
parse[filter.field] = 'date';
}
}
});
}
}, {});
if (model_1.isUnitModel(model) || model_1.isFacetModel(model)) {
// Parse encoded fields
model.forEachFieldDef(function (fieldDef) {
if (fieldDef.type === type_1.TEMPORAL) {
parse[fieldDef.field] = 'date';
}
else if (fieldDef.type === type_1.QUANTITATIVE) {
if (calcFieldMap[fieldDef.field] || aggregate_1.isCountingAggregateOp(fieldDef.aggregate)) {
return;
}
parse[fieldDef.field] = 'number';
}
});
}
// Custom parse should override inferred parse
var data = model.data;
if (data && data.format && data.format.parse) {
var p_1 = data.format.parse;
util_1.keys(p_1).forEach(function (field) {
parse[field] = p_1[field];
});
}
// We should not parse what has already been parsed in a parent
var modelParse = model.component.data.ancestorParse;
util_1.keys(modelParse).forEach(function (field) {
if (parse[field] !== modelParse[field]) {
log.warn(log.message.differentParse(field, parse[field], modelParse[field]));
}
else {
delete parse[field];
}
});
if (util_1.keys(parse).length === 0) {
return null;
}
return new ParseNode(parse);
};
Object.defineProperty(ParseNode.prototype, "parse", {
get: function () {
return this._parse;
},
enumerable: true,
configurable: true
});
ParseNode.prototype.merge = function (other) {
this._parse = util_1.extend(this._parse, other.parse);
other.remove();
};
ParseNode.prototype.assembleFormatParse = function () {
return this._parse;
};
// format parse depends and produces all fields in its parse
ParseNode.prototype.producedFields = function () {
return util_1.toSet(util_1.keys(this.parse));
};
ParseNode.prototype.dependentFields = function () {
return util_1.toSet(util_1.keys(this.parse));
};
ParseNode.prototype.assembleTransforms = function () {
var _this = this;
return util_1.keys(this._parse).map(function (field) {
var expr = parseExpression(field, _this._parse[field]);
if (!expr) {
return null;
}
var formula = {
type: 'formula',
expr: expr,
as: field
};
return formula;
}).filter(function (t) { return t !== null; });
};
return ParseNode;
}(dataflow_1.DataFlowNode));
exports.ParseNode = ParseNode;
},{"../../aggregate":5,"../../filter":90,"../../log":94,"../../logical":95,"../../transform":105,"../../type":106,"../../util":107,"../model":56,"./dataflow":22,"tslib":114}],25:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var scale_1 = require("../../scale");
var util_1 = require("../../util");
var dataflow_1 = require("./dataflow");
var NonPositiveFilterNode = (function (_super) {
tslib_1.__extends(NonPositiveFilterNode, _super);
function NonPositiveFilterNode(filter) {
var _this = _super.call(this) || this;
_this._filter = filter;
return _this;
}
NonPositiveFilterNode.prototype.clone = function () {
return new NonPositiveFilterNode(util_1.extend({}, this._filter));
};
NonPositiveFilterNode.make = function (model) {
var filter = channel_1.SCALE_CHANNELS.reduce(function (nonPositiveComponent, channel) {
var scale = model.getScaleComponent(channel);
if (!scale || !model.field(channel)) {
// don't set anything
return nonPositiveComponent;
}
nonPositiveComponent[model.field(channel)] = scale.get('type') === scale_1.ScaleType.LOG;
return nonPositiveComponent;
}, {});
if (!util_1.keys(filter).length) {
return null;
}
return new NonPositiveFilterNode(filter);
};
Object.defineProperty(NonPositiveFilterNode.prototype, "filter", {
get: function () {
return this._filter;
},
enumerable: true,
configurable: true
});
NonPositiveFilterNode.prototype.assemble = function () {
var _this = this;
return util_1.keys(this._filter).filter(function (field) {
// Only filter fields (keys) with value = true
return _this._filter[field];
}).map(function (field) {
return {
type: 'filter',
expr: "datum[" + util_1.stringValue(field) + "] > 0"
};
});
};
return NonPositiveFilterNode;
}(dataflow_1.DataFlowNode));
exports.NonPositiveFilterNode = NonPositiveFilterNode;
},{"../../channel":8,"../../scale":97,"../../util":107,"./dataflow":22,"tslib":114}],26:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var scale_1 = require("../../scale");
var type_1 = require("../../type");
var util_1 = require("../../util");
var dataflow_1 = require("./dataflow");
var NullFilterNode = (function (_super) {
tslib_1.__extends(NullFilterNode, _super);
function NullFilterNode(fields) {
var _this = _super.call(this) || this;
_this._filteredFields = fields;
return _this;
}
NullFilterNode.prototype.clone = function () {
return new NullFilterNode(util_1.duplicate(this._filteredFields));
};
NullFilterNode.make = function (model) {
var fields = model.reduceFieldDef(function (aggregator, fieldDef, channel) {
if (model.config.invalidValues === 'filter' && !fieldDef.aggregate && fieldDef.field) {
// Vega's aggregate operator already handle invalid values, so we only have to consider non-aggregate field here.
var scaleComponent = channel_1.isScaleChannel(channel) && model.getScaleComponent(channel);
if (scaleComponent) {
var scaleType = scaleComponent.get('type');
// only automatically filter null for continuous domain since discrete domain scales can handle invalid values.
if (scale_1.hasContinuousDomain(scaleType)) {
aggregator[fieldDef.field] = fieldDef;
}
}
}
return aggregator;
}, {});
if (util_1.keys(fields).length === 0) {
return null;
}
return new NullFilterNode(fields);
};
Object.defineProperty(NullFilterNode.prototype, "filteredFields", {
get: function () {
return this._filteredFields;
},
enumerable: true,
configurable: true
});
NullFilterNode.prototype.merge = function (other) {
var _this = this;
var t = util_1.keys(this._filteredFields).map(function (k) { return k + ' ' + util_1.hash(_this._filteredFields[k]); });
var o = util_1.keys(other.filteredFields).map(function (k) { return k + ' ' + util_1.hash(other.filteredFields[k]); });
if (!util_1.differArray(t, o)) {
this._filteredFields = util_1.extend(this._filteredFields, other._filteredFields);
other.remove();
}
};
NullFilterNode.prototype.assemble = function () {
var _this = this;
var filters = util_1.keys(this._filteredFields).reduce(function (_filters, field) {
var fieldDef = _this._filteredFields[field];
if (fieldDef !== null) {
_filters.push("datum[" + util_1.stringValue(fieldDef.field) + "] !== null");
if (util_1.contains([type_1.QUANTITATIVE, type_1.TEMPORAL], fieldDef.type)) {
// TODO(https://github.com/vega/vega-lite/issues/1436):
// We can be even smarter and add NaN filter for N,O that are numbers
// based on the `parse` property once we have it.
_filters.push("!isNaN(datum[" + util_1.stringValue(fieldDef.field) + "])");
}
}
return _filters;
}, []);
return filters.length > 0 ?
{
type: 'filter',
expr: filters.join(' && ')
} : null;
};
return NullFilterNode;
}(dataflow_1.DataFlowNode));
exports.NullFilterNode = NullFilterNode;
},{"../../channel":8,"../../scale":97,"../../type":106,"../../util":107,"./dataflow":22,"tslib":114}],27:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var data_1 = require("../../data");
var util_1 = require("../../util");
var aggregate_1 = require("./aggregate");
var dataflow_1 = require("./dataflow");
var facet_1 = require("./facet");
var nonpositivefilter_1 = require("./nonpositivefilter");
var nullfilter_1 = require("./nullfilter");
var optimizers = require("./optimizers");
var stack_1 = require("./stack");
exports.FACET_SCALE_PREFIX = 'scale_';
/**
* Clones the subtree and ignores output nodes except for the leafs, which are renamed.
*/
function cloneSubtree(facet) {
function clone(node) {
if (!(node instanceof facet_1.FacetNode)) {
var copy_1 = node.clone();
if (copy_1 instanceof dataflow_1.OutputNode) {
var newName = exports.FACET_SCALE_PREFIX + copy_1.getSource();
copy_1.setSource(newName);
facet.model.component.data.outputNodes[newName] = copy_1;
}
else if (copy_1 instanceof aggregate_1.AggregateNode || copy_1 instanceof stack_1.StackNode) {
copy_1.addDimensions(facet.fields);
}
util_1.flatten(node.children.map(clone)).forEach(function (n) { return n.parent = copy_1; });
return [copy_1];
}
return util_1.flatten(node.children.map(clone));
}
return clone;
}
/**
* Move facet nodes down to the next fork or output node. Also pull the main output with the facet node.
* After moving down the facet node, make a copy of the subtree and make it a child of the main output.
*/
function moveFacetDown(node) {
if (node instanceof facet_1.FacetNode) {
if (node.numChildren() === 1 && !(node.children[0] instanceof dataflow_1.OutputNode)) {
// move down until we hit a fork or output node
var child = node.children[0];
if (child instanceof aggregate_1.AggregateNode || child instanceof stack_1.StackNode) {
child.addDimensions(node.fields);
}
child.swapWithParent();
moveFacetDown(node);
}
else {
// move main to facet
moveMainDownToFacet(node.model.component.data.main);
// replicate the subtree and place it before the facet's main node
var copy = util_1.flatten(node.children.map(cloneSubtree(node)));
copy.forEach(function (c) { return c.parent = node.model.component.data.main; });
}
}
else {
node.children.forEach(moveFacetDown);
}
}
function moveMainDownToFacet(node) {
if (node instanceof dataflow_1.OutputNode && node.type === data_1.MAIN) {
if (node.numChildren() === 1) {
var child = node.children[0];
if (!(child instanceof facet_1.FacetNode)) {
child.swapWithParent();
moveMainDownToFacet(node);
}
}
}
}
/**
* Start optimization path from the root. Useful for removing nodes.
*/
function removeUnnecessaryNodes(node) {
// remove empty non positive filter
if (node instanceof nonpositivefilter_1.NonPositiveFilterNode && util_1.every(util_1.vals(node.filter), function (b) { return b === false; })) {
node.remove();
}
// remove empty null filter nodes
if (node instanceof nullfilter_1.NullFilterNode && util_1.every(util_1.vals(node.filteredFields), function (f) { return f === null; })) {
node.remove();
}
// remove output nodes that are not required
if (node instanceof dataflow_1.OutputNode && !node.isRequired()) {
node.remove();
}
node.children.forEach(removeUnnecessaryNodes);
}
/**
* Return all leaf nodes.
*/
function getLeaves(roots) {
var leaves = [];
function append(node) {
if (node.numChildren() === 0) {
leaves.push(node);
}
else {
node.children.forEach(append);
}
}
roots.forEach(append);
return leaves;
}
/**
* Optimizes the dataflow of the passed in data component.
*/
function optimizeDataflow(dataComponent) {
var roots = util_1.vals(dataComponent.sources);
roots.forEach(removeUnnecessaryNodes);
// remove source nodes that don't have any children because they also don't have output nodes
roots = roots.filter(function (r) { return r.numChildren() > 0; });
getLeaves(roots).forEach(optimizers.iterateFromLeaves(optimizers.removeUnusedSubtrees));
roots = roots.filter(function (r) { return r.numChildren() > 0; });
getLeaves(roots).forEach(optimizers.iterateFromLeaves(optimizers.moveParseUp));
getLeaves(roots).forEach(optimizers.removeDuplicateTimeUnits);
roots.forEach(moveFacetDown);
util_1.keys(dataComponent.sources).forEach(function (s) {
if (dataComponent.sources[s].numChildren() === 0) {
delete dataComponent.sources[s];
}
});
}
exports.optimizeDataflow = optimizeDataflow;
},{"../../data":85,"../../util":107,"./aggregate":19,"./dataflow":22,"./facet":23,"./nonpositivefilter":25,"./nullfilter":26,"./optimizers":28,"./stack":31}],28:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var util_1 = require("../../util");
var dataflow_1 = require("./dataflow");
var formatparse_1 = require("./formatparse");
var source_1 = require("./source");
var timeunit_1 = require("./timeunit");
/**
* Start optimization path at the leaves. Useful for merging up or removing things.
*
* If the callback returns true, the recursion continues.
*/
function iterateFromLeaves(f) {
function optimizeNextFromLeaves(node) {
if (node instanceof source_1.SourceNode) {
return;
}
var next = node.parent;
if (f(node)) {
optimizeNextFromLeaves(next);
}
}
return optimizeNextFromLeaves;
}
exports.iterateFromLeaves = iterateFromLeaves;
/**
* Move parse nodes up to forks.
*/
function moveParseUp(node) {
var parent = node.parent;
// move parse up by merging or swapping
if (node instanceof formatparse_1.ParseNode) {
if (parent instanceof source_1.SourceNode) {
return false;
}
if (parent.numChildren() > 1) {
// don't move parse further up but continue with parent.
return true;
}
if (parent instanceof formatparse_1.ParseNode) {
parent.merge(node);
}
else {
// don't swap with nodes that produce something that the parse node depends on (e.g. lookup)
if (util_1.hasIntersection(parent.producedFields(), node.dependentFields())) {
return true;
}
node.swapWithParent();
}
}
return true;
}
exports.moveParseUp = moveParseUp;
/**
* Repeatedly remove leaf nodes that are not output nodes.
* The reason is that we don't need subtrees that don't have any output nodes.
*/
function removeUnusedSubtrees(node) {
var parent = node.parent;
if (node instanceof dataflow_1.OutputNode || node.numChildren() > 0) {
// no need to continue with parent because it is output node or will have children (there was a fork)
return false;
}
else {
node.remove();
}
return true;
}
exports.removeUnusedSubtrees = removeUnusedSubtrees;
/**
* Removes duplicate time unit nodes (as determined by the name of the
* output field) that may be generated due to selections projected over
* time units.
*/
function removeDuplicateTimeUnits(leaf) {
var fields = {};
return iterateFromLeaves(function (node) {
if (node instanceof timeunit_1.TimeUnitNode) {
var pfields = node.producedFields();
var dupe = util_1.keys(pfields).every(function (k) { return !!fields[k]; });
if (dupe) {
node.remove();
}
else {
fields = tslib_1.__assign({}, fields, pfields);
}
}
return true;
})(leaf);
}
exports.removeDuplicateTimeUnits = removeDuplicateTimeUnits;
},{"../../util":107,"./dataflow":22,"./formatparse":24,"./source":30,"./timeunit":32,"tslib":114}],29:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var data_1 = require("../../data");
var model_1 = require("../model");
var selection_1 = require("../selection/selection");
var aggregate_1 = require("./aggregate");
var bin_1 = require("./bin");
var dataflow_1 = require("./dataflow");
var facet_1 = require("./facet");
var formatparse_1 = require("./formatparse");
var nonpositivefilter_1 = require("./nonpositivefilter");
var nullfilter_1 = require("./nullfilter");
var source_1 = require("./source");
var stack_1 = require("./stack");
var timeunit_1 = require("./timeunit");
var transforms_1 = require("./transforms");
function parseRoot(model, sources) {
if (model.data || !model.parent) {
// if the model defines a data source or is the root, create a source node
var source = new source_1.SourceNode(model.data);
var hash = source.hash();
if (hash in sources) {
// use a reference if we already have a source
return sources[hash];
}
else {
// otherwise add a new one
sources[hash] = source;
return source;
}
}
else {
// If we don't have a source defined (overriding parent's data), use the parent's facet root or main.
return model.parent.component.data.facetRoot ? model.parent.component.data.facetRoot : model.parent.component.data.main;
}
}
/*
Description of the dataflow (http://asciiflow.com/):
+--------+
| Source |
+---+----+
|
v
Transforms
(Filter, Calculate, ...)
|
v
FormatParse
|
v
Null Filter
|
v
Binning
|
v
Timeunit
|
v
+--+--+
| Raw |
+-----+
|
v
Aggregate
|
v
Stack
|
v
>0 Filter
|
v
Path Order
|
v
+----------+
| Main |
+----------+
|
v
+-------+
| Facet |----> "column", "column-layout", and "row"
+-------+
|
v
...Child data...
*/
function parseData(model) {
var root = parseRoot(model, model.component.data.sources);
var outputNodes = model.component.data.outputNodes;
var outputNodeRefCounts = model.component.data.outputNodeRefCounts;
// the current head of the tree that we are appending to
var head = root;
// Default discrete selections require an identifier transform to
// uniquely identify data points as the _id field is volatile. Add
// this transform at the head of our pipeline such that the identifier
// field is available for all subsequent datasets. Additional identifier
// transforms will be necessary when new tuples are constructed
// (e.g., post-aggregation).
if (selection_1.requiresSelectionId(model) && !model.parent) {
var ident = new transforms_1.IdentifierNode();
ident.parent = head;
head = ident;
}
// HACK: This is equivalent for merging bin extent for union scale.
// FIXME(https://github.com/vega/vega-lite/issues/2270): Correctly merge extent / bin node for shared bin scale
var parentIsLayer = model.parent && model_1.isLayerModel(model.parent);
if (model_1.isUnitModel(model) || model_1.isFacetModel(model)) {
if (parentIsLayer) {
var bin = bin_1.BinNode.makeBinFromEncoding(model);
if (bin) {
bin.parent = head;
head = bin;
}
}
}
if (model.transforms.length > 0) {
var _a = transforms_1.parseTransformArray(model), first = _a.first, last = _a.last;
first.parent = head;
head = last;
}
var parse = formatparse_1.ParseNode.make(model);
if (parse) {
parse.parent = head;
head = parse;
}
if (model_1.isUnitModel(model) || model_1.isFacetModel(model)) {
var nullFilter = nullfilter_1.NullFilterNode.make(model);
if (nullFilter) {
nullFilter.parent = head;
head = nullFilter;
}
if (!parentIsLayer) {
var bin = bin_1.BinNode.makeBinFromEncoding(model);
if (bin) {
bin.parent = head;
head = bin;
}
}
var tu = timeunit_1.TimeUnitNode.makeFromEncoding(model);
if (tu) {
tu.parent = head;
head = tu;
}
}
// add an output node pre aggregation
var rawName = model.getName(data_1.RAW);
var raw = new dataflow_1.OutputNode(rawName, data_1.RAW, outputNodeRefCounts);
outputNodes[rawName] = raw;
raw.parent = head;
head = raw;
if (model_1.isUnitModel(model)) {
var agg = aggregate_1.AggregateNode.makeFromEncoding(model);
if (agg) {
agg.parent = head;
head = agg;
if (selection_1.requiresSelectionId(model)) {
var ident = new transforms_1.IdentifierNode();
ident.parent = head;
head = ident;
}
}
var stack = stack_1.StackNode.make(model);
if (stack) {
stack.parent = head;
head = stack;
}
var nonPosFilter = nonpositivefilter_1.NonPositiveFilterNode.make(model);
if (nonPosFilter) {
nonPosFilter.parent = head;
head = nonPosFilter;
}
}
// output node for marks
var mainName = model.getName(data_1.MAIN);
var main = new dataflow_1.OutputNode(mainName, data_1.MAIN, outputNodeRefCounts);
outputNodes[mainName] = main;
main.parent = head;
head = main;
// add facet marker
var facetRoot = null;
if (model_1.isFacetModel(model)) {
var facetName = model.getName('facet');
facetRoot = new facet_1.FacetNode(model, facetName, main.getSource());
outputNodes[facetName] = facetRoot;
facetRoot.parent = head;
head = facetRoot;
}
// add the format parse from this model so that children don't parse the same field again
var ancestorParse = tslib_1.__assign({}, model.component.data.ancestorParse, (parse ? parse.parse : {}));
return tslib_1.__assign({}, model.component.data, { outputNodes: outputNodes,
outputNodeRefCounts: outputNodeRefCounts,
raw: raw,
main: main,
facetRoot: facetRoot,
ancestorParse: ancestorParse });
}
exports.parseData = parseData;
},{"../../data":85,"../model":56,"../selection/selection":69,"./aggregate":19,"./bin":21,"./dataflow":22,"./facet":23,"./formatparse":24,"./nonpositivefilter":25,"./nullfilter":26,"./source":30,"./stack":31,"./timeunit":32,"./transforms":33,"tslib":114}],30:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var data_1 = require("../../data");
var util_1 = require("../../util");
var dataflow_1 = require("./dataflow");
var SourceNode = (function (_super) {
tslib_1.__extends(SourceNode, _super);
function SourceNode(data) {
var _this = _super.call(this) || this;
data = data || { name: 'source' };
if (data_1.isInlineData(data)) {
_this._data = {
values: data.values
};
}
else if (data_1.isUrlData(data)) {
// Extract extension from URL using snippet from
// http://stackoverflow.com/questions/680929/how-to-extract-extension-from-filename-string-in-javascript
var defaultExtension = /(?:\.([^.]+))?$/.exec(data.url)[1];
if (!util_1.contains(['json', 'csv', 'tsv', 'topojson'], defaultExtension)) {
defaultExtension = 'json';
}
var dataFormat = data.format || {};
// For backward compatibility for former `data.formatType` property
var formatType = dataFormat.type || data['formatType'];
var property = dataFormat.property, feature = dataFormat.feature, mesh = dataFormat.mesh;
var format = tslib_1.__assign({ type: formatType ? formatType : defaultExtension }, (property ? { property: property } : {}), (feature ? { feature: feature } : {}), (mesh ? { mesh: mesh } : {}));
_this._data = {
url: data.url,
format: format
};
}
else if (data_1.isNamedData(data)) {
_this._name = data.name;
_this._data = {};
}
return _this;
}
Object.defineProperty(SourceNode.prototype, "data", {
get: function () {
return this._data;
},
enumerable: true,
configurable: true
});
SourceNode.prototype.hasName = function () {
return !!this._name;
};
Object.defineProperty(SourceNode.prototype, "dataName", {
get: function () {
return this._name;
},
set: function (name) {
this._name = name;
},
enumerable: true,
configurable: true
});
Object.defineProperty(SourceNode.prototype, "parent", {
set: function (parent) {
throw new Error('Source nodes have to be roots.');
},
enumerable: true,
configurable: true
});
SourceNode.prototype.remove = function () {
throw new Error('Source nodes are roots and cannot be removed.');
};
/**
* Return a unique identifir for this data source.
*/
SourceNode.prototype.hash = function () {
if (data_1.isInlineData(this._data)) {
return util_1.hash(this._data);
}
else if (data_1.isUrlData(this._data)) {
return this._data.url + " " + util_1.hash(this._data.format);
}
else {
return this._name;
}
};
SourceNode.prototype.assemble = function () {
return tslib_1.__assign({ name: this._name }, this._data, { transform: [] });
};
return SourceNode;
}(dataflow_1.DataFlowNode));
exports.SourceNode = SourceNode;
},{"../../data":85,"../../util":107,"./dataflow":22,"tslib":114}],31:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_util_1 = require("vega-util");
var channel_1 = require("../../channel");
var fielddef_1 = require("../../fielddef");
var scale_1 = require("../../scale");
var util_1 = require("../../util");
var common_1 = require("../common");
var dataflow_1 = require("./dataflow");
function getStackByFields(model) {
return model.stack.stackBy.reduce(function (fields, by) {
var channel = by.channel;
var fieldDef = by.fieldDef;
var scale = channel_1.isScaleChannel(channel) ? model.getScaleComponent(channel) : undefined;
var _field = fielddef_1.field(fieldDef, {
binSuffix: scale && scale_1.hasDiscreteDomain(scale.get('type')) ? 'range' : 'start'
});
if (_field) {
fields.push(_field);
}
return fields;
}, []);
}
var StackNode = (function (_super) {
tslib_1.__extends(StackNode, _super);
function StackNode(stack) {
var _this = _super.call(this) || this;
_this._stack = stack;
return _this;
}
StackNode.prototype.clone = function () {
return new StackNode(util_1.duplicate(this._stack));
};
StackNode.make = function (model) {
var stackProperties = model.stack;
if (!stackProperties) {
return null;
}
var dimensionFieldDef;
if (stackProperties.groupbyChannel) {
dimensionFieldDef = model.fieldDef(stackProperties.groupbyChannel);
}
var stackby = getStackByFields(model);
var orderDef = model.encoding.order;
var sort;
if (orderDef) {
sort = common_1.sortParams(orderDef);
}
else {
// default = descending by stackFields
// FIXME is the default here correct for binned fields?
sort = stackby.reduce(function (s, field) {
s.field.push(field);
s.order.push('descending');
return s;
}, { field: [], order: [] });
}
return new StackNode({
dimensionFieldDef: dimensionFieldDef,
field: model.field(stackProperties.fieldChannel),
facetby: [],
stackby: stackby,
sort: sort,
offset: stackProperties.offset,
impute: stackProperties.impute,
});
};
Object.defineProperty(StackNode.prototype, "stack", {
get: function () {
return this._stack;
},
enumerable: true,
configurable: true
});
StackNode.prototype.addDimensions = function (fields) {
this._stack.facetby = this._stack.facetby.concat(fields);
};
StackNode.prototype.dependentFields = function () {
var out = {};
out[this._stack.field] = true;
this.getGroupbyFields().forEach(function (f) { return out[f] = true; });
this._stack.facetby.forEach(function (f) { return out[f] = true; });
var field = this._stack.sort.field;
vega_util_1.isArray(field) ? field.forEach(function (f) { return out[f] = true; }) : out[field] = true;
return out;
};
StackNode.prototype.producedFields = function () {
var out = {};
out[this._stack.field + '_start'] = true;
out[this._stack.field + '_end'] = true;
return out;
};
StackNode.prototype.getGroupbyFields = function () {
var _a = this._stack, dimensionFieldDef = _a.dimensionFieldDef, impute = _a.impute;
if (dimensionFieldDef) {
if (dimensionFieldDef.bin) {
if (impute) {
// For binned group by field with impute, we calculate bin_mid
// as we cannot impute two fields simultaneously
return [fielddef_1.field(dimensionFieldDef, { binSuffix: 'mid' })];
}
return [
// For binned group by field without impute, we need both bin_start and bin_end
fielddef_1.field(dimensionFieldDef, { binSuffix: 'start' }),
fielddef_1.field(dimensionFieldDef, { binSuffix: 'end' })
];
}
return [fielddef_1.field(dimensionFieldDef)];
}
return [];
};
StackNode.prototype.assemble = function () {
var transform = [];
var _a = this._stack, facetby = _a.facetby, stackField = _a.field, dimensionFieldDef = _a.dimensionFieldDef, impute = _a.impute, offset = _a.offset, sort = _a.sort, stackby = _a.stackby;
// Impute
if (impute && dimensionFieldDef) {
var dimensionField = dimensionFieldDef ? fielddef_1.field(dimensionFieldDef, { binSuffix: 'mid' }) : undefined;
if (dimensionFieldDef.bin) {
// As we can only impute one field at a time, we need to calculate
// mid point for a binned field
transform.push({
type: 'formula',
expr: '(' +
fielddef_1.field(dimensionFieldDef, { expr: 'datum', binSuffix: 'start' }) +
'+' +
fielddef_1.field(dimensionFieldDef, { expr: 'datum', binSuffix: 'end' }) +
')/2',
as: dimensionField
});
}
transform.push({
type: 'impute',
field: stackField,
groupby: stackby,
key: dimensionField,
method: 'value',
value: 0
});
}
// Stack
transform.push({
type: 'stack',
groupby: this.getGroupbyFields().concat(facetby),
field: stackField,
sort: sort,
as: [
stackField + '_start',
stackField + '_end'
],
offset: offset
});
return transform;
};
return StackNode;
}(dataflow_1.DataFlowNode));
exports.StackNode = StackNode;
},{"../../channel":8,"../../fielddef":89,"../../scale":97,"../../util":107,"../common":16,"./dataflow":22,"tslib":114,"vega-util":116}],32:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var fielddef_1 = require("../../fielddef");
var timeunit_1 = require("../../timeunit");
var type_1 = require("../../type");
var util_1 = require("../../util");
var dataflow_1 = require("./dataflow");
var TimeUnitNode = (function (_super) {
tslib_1.__extends(TimeUnitNode, _super);
function TimeUnitNode(formula) {
var _this = _super.call(this) || this;
_this.formula = formula;
return _this;
}
TimeUnitNode.prototype.clone = function () {
return new TimeUnitNode(util_1.duplicate(this.formula));
};
TimeUnitNode.makeFromEncoding = function (model) {
var formula = model.reduceFieldDef(function (timeUnitComponent, fieldDef) {
if (fieldDef.type === type_1.TEMPORAL && fieldDef.timeUnit) {
var f = fielddef_1.field(fieldDef);
timeUnitComponent[f] = {
as: f,
timeUnit: fieldDef.timeUnit,
field: fieldDef.field
};
}
return timeUnitComponent;
}, {});
if (util_1.keys(formula).length === 0) {
return null;
}
return new TimeUnitNode(formula);
};
TimeUnitNode.makeFromTransform = function (model, t) {
return new TimeUnitNode((_a = {},
_a[t.field] = {
as: t.as,
timeUnit: t.timeUnit,
field: t.field
},
_a));
var _a;
};
TimeUnitNode.prototype.merge = function (other) {
this.formula = util_1.extend(this.formula, other.formula);
other.remove();
};
TimeUnitNode.prototype.producedFields = function () {
var out = {};
util_1.vals(this.formula).forEach(function (f) {
out[f.as] = true;
});
return out;
};
TimeUnitNode.prototype.dependentFields = function () {
var out = {};
util_1.vals(this.formula).forEach(function (f) {
out[f.field] = true;
});
return out;
};
TimeUnitNode.prototype.assemble = function () {
return util_1.vals(this.formula).map(function (c) {
return {
type: 'formula',
as: c.as,
expr: timeunit_1.fieldExpr(c.timeUnit, c.field)
};
});
};
return TimeUnitNode;
}(dataflow_1.DataFlowNode));
exports.TimeUnitNode = TimeUnitNode;
},{"../../fielddef":89,"../../timeunit":102,"../../type":106,"../../util":107,"./dataflow":22,"tslib":114}],33:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_util_1 = require("vega-util");
var datetime_1 = require("../../datetime");
var filter_1 = require("../../filter");
var log = require("../../log");
var selection_1 = require("../../selection");
var transform_1 = require("../../transform");
var util_1 = require("../../util");
var selection_2 = require("../selection/selection");
var aggregate_1 = require("./aggregate");
var bin_1 = require("./bin");
var dataflow_1 = require("./dataflow");
var formatparse_1 = require("./formatparse");
var source_1 = require("./source");
var timeunit_1 = require("./timeunit");
var FilterNode = (function (_super) {
tslib_1.__extends(FilterNode, _super);
function FilterNode(model, filter) {
var _this = _super.call(this) || this;
_this.model = model;
_this.filter = filter;
_this.expr = filter_1.expression(_this.model, _this.filter, _this);
return _this;
}
FilterNode.prototype.clone = function () {
return new FilterNode(this.model, util_1.duplicate(this.filter));
};
FilterNode.prototype.assemble = function () {
return {
type: 'filter',
expr: this.expr
};
};
return FilterNode;
}(dataflow_1.DataFlowNode));
exports.FilterNode = FilterNode;
/**
* We don't know what a calculate node depends on so we should never move it beyond anything that produces fields.
*/
var CalculateNode = (function (_super) {
tslib_1.__extends(CalculateNode, _super);
function CalculateNode(transform) {
var _this = _super.call(this) || this;
_this.transform = transform;
return _this;
}
CalculateNode.prototype.clone = function () {
return new CalculateNode(util_1.duplicate(this.transform));
};
CalculateNode.prototype.producedFields = function () {
var out = {};
out[this.transform.as] = true;
return out;
};
CalculateNode.prototype.assemble = function () {
return {
type: 'formula',
expr: this.transform.calculate,
as: this.transform.as
};
};
return CalculateNode;
}(dataflow_1.DataFlowNode));
exports.CalculateNode = CalculateNode;
var LookupNode = (function (_super) {
tslib_1.__extends(LookupNode, _super);
function LookupNode(transform, secondary) {
var _this = _super.call(this) || this;
_this.transform = transform;
_this.secondary = secondary;
return _this;
}
LookupNode.make = function (model, transform, counter) {
var sources = model.component.data.sources;
var s = new source_1.SourceNode(transform.from.data);
var fromSource = sources[s.hash()];
if (!fromSource) {
sources[s.hash()] = s;
fromSource = s;
}
var fromOutputName = model.getName("lookup_" + counter);
var fromOutputNode = new dataflow_1.OutputNode(fromOutputName, 'lookup', model.component.data.outputNodeRefCounts);
fromOutputNode.parent = fromSource;
model.component.data.outputNodes[fromOutputName] = fromOutputNode;
return new LookupNode(transform, fromOutputNode.getSource());
};
LookupNode.prototype.producedFields = function () {
return util_1.toSet(this.transform.from.fields || ((this.transform.as instanceof Array) ? this.transform.as : [this.transform.as]));
};
LookupNode.prototype.assemble = function () {
var foreign;
if (this.transform.from.fields) {
// lookup a few fields and add create a flat output
foreign = tslib_1.__assign({ values: this.transform.from.fields }, this.transform.as ? { as: ((this.transform.as instanceof Array) ? this.transform.as : [this.transform.as]) } : {});
}
else {
// lookup full record and nest it
var asName = this.transform.as;
if (!vega_util_1.isString(asName)) {
log.warn(log.message.NO_FIELDS_NEEDS_AS);
asName = '_lookup';
}
foreign = {
as: [asName]
};
}
return tslib_1.__assign({ type: 'lookup', from: this.secondary, key: this.transform.from.key, fields: [this.transform.lookup] }, foreign, (this.transform.default ? { default: this.transform.default } : {}));
};
return LookupNode;
}(dataflow_1.DataFlowNode));
exports.LookupNode = LookupNode;
var IdentifierNode = (function (_super) {
tslib_1.__extends(IdentifierNode, _super);
function IdentifierNode() {
return _super.call(this) || this;
}
IdentifierNode.prototype.clone = function () {
return new IdentifierNode();
};
IdentifierNode.prototype.producedFields = function () {
return _a = {}, _a[selection_1.SELECTION_ID] = true, _a;
var _a;
};
IdentifierNode.prototype.assemble = function () {
return { type: 'identifier', as: selection_1.SELECTION_ID };
};
return IdentifierNode;
}(dataflow_1.DataFlowNode));
exports.IdentifierNode = IdentifierNode;
/**
* Parses a transforms array into a chain of connected dataflow nodes.
*/
function parseTransformArray(model) {
var first = null;
var node;
var previous;
var lookupCounter = 0;
function insert(newNode) {
if (!first) {
// A parent may be inserted during node construction
// (e.g., selection FilterNodes may add a TimeUnitNode).
first = newNode.parent || newNode;
}
else if (newNode.parent) {
previous.insertAsParentOf(newNode);
}
else {
newNode.parent = previous;
}
previous = newNode;
}
model.transforms.forEach(function (t) {
if (transform_1.isCalculate(t)) {
node = new CalculateNode(t);
}
else if (transform_1.isFilter(t)) {
// Automatically add a parse node for filters with filter objects
var parse = {};
var filter = t.filter;
var val = null;
// For EqualFilter, just use the equal property.
// For RangeFilter and OneOfFilter, all array members should have
// the same type, so we only use the first one.
if (filter_1.isEqualFilter(filter)) {
val = filter.equal;
}
else if (filter_1.isRangeFilter(filter)) {
val = filter.range[0];
}
else if (filter_1.isOneOfFilter(filter)) {
val = (filter.oneOf || filter['in'])[0];
} // else -- for filter expression, we can't infer anything
if (val) {
if (datetime_1.isDateTime(val)) {
parse[filter['field']] = 'date';
}
else if (vega_util_1.isNumber(val)) {
parse[filter['field']] = 'number';
}
else if (vega_util_1.isString(val)) {
parse[filter['field']] = 'string';
}
}
if (util_1.keys(parse).length > 0) {
var parseNode = new formatparse_1.ParseNode(parse);
insert(parseNode);
}
node = new FilterNode(model, t.filter);
}
else if (transform_1.isBin(t)) {
node = bin_1.BinNode.makeFromTransform(model, t);
}
else if (transform_1.isTimeUnit(t)) {
node = timeunit_1.TimeUnitNode.makeFromTransform(model, t);
}
else if (transform_1.isSummarize(t)) {
node = aggregate_1.AggregateNode.makeFromTransform(model, t);
if (selection_2.requiresSelectionId(model)) {
insert(node);
node = new IdentifierNode();
}
}
else if (transform_1.isLookup(t)) {
node = LookupNode.make(model, t, lookupCounter++);
}
else {
log.warn(log.message.invalidTransformIgnored(t));
return;
}
insert(node);
});
var last = node;
return { first: first, last: last };
}
exports.parseTransformArray = parseTransformArray;
},{"../../datetime":86,"../../filter":90,"../../log":94,"../../selection":98,"../../transform":105,"../../util":107,"../selection/selection":69,"./aggregate":19,"./bin":21,"./dataflow":22,"./formatparse":24,"./source":30,"./timeunit":32,"tslib":114,"vega-util":116}],34:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../channel");
var encoding_1 = require("../encoding");
var fielddef_1 = require("../fielddef");
var log = require("../log");
var scale_1 = require("../scale");
var util_1 = require("../util");
var vega_schema_1 = require("../vega.schema");
var buildmodel_1 = require("./buildmodel");
var assemble_1 = require("./data/assemble");
var parse_1 = require("./data/parse");
var header_1 = require("./layout/header");
var parse_2 = require("./layoutsize/parse");
var model_1 = require("./model");
var repeater_1 = require("./repeater");
var resolve_1 = require("./resolve");
var assemble_2 = require("./scale/assemble");
var domain_1 = require("./scale/domain");
var FacetModel = (function (_super) {
tslib_1.__extends(FacetModel, _super);
function FacetModel(spec, parent, parentGivenName, repeater, config) {
var _this = _super.call(this, spec, parent, parentGivenName, config, spec.resolve) || this;
_this.type = 'facet';
_this.child = buildmodel_1.buildModel(spec.spec, _this, _this.getName('child'), undefined, repeater, config);
_this.children = [_this.child];
var facet = repeater_1.replaceRepeaterInFacet(spec.facet, repeater);
_this.facet = _this.initFacet(facet);
return _this;
}
FacetModel.prototype.initFacet = function (facet) {
// clone to prevent side effect to the original spec
return encoding_1.reduce(facet, function (normalizedFacet, fieldDef, channel) {
if (!util_1.contains([channel_1.ROW, channel_1.COLUMN], channel)) {
// Drop unsupported channel
log.warn(log.message.incompatibleChannel(channel, 'facet'));
return normalizedFacet;
}
if (fieldDef.field === undefined) {
log.warn(log.message.emptyFieldDef(fieldDef, channel));
return normalizedFacet;
}
// Convert type to full, lowercase type, or augment the fieldDef with a default type if missing.
normalizedFacet[channel] = fielddef_1.normalize(fieldDef, channel);
return normalizedFacet;
}, {});
};
FacetModel.prototype.channelHasField = function (channel) {
return !!this.facet[channel];
};
FacetModel.prototype.hasDiscreteDomain = function (channel) {
return true;
};
FacetModel.prototype.fieldDef = function (channel) {
return this.facet[channel];
};
FacetModel.prototype.parseData = function () {
this.component.data = parse_1.parseData(this);
this.child.parseData();
};
FacetModel.prototype.parseLayoutSize = function () {
parse_2.parseChildrenLayoutSize(this);
};
FacetModel.prototype.parseSelection = function () {
// As a facet has a single child, the selection components are the same.
// The child maintains its selections to assemble signals, which remain
// within its unit.
this.child.parseSelection();
this.component.selection = this.child.component.selection;
};
FacetModel.prototype.parseMarkGroup = function () {
this.child.parseMarkGroup();
};
FacetModel.prototype.parseAxisAndHeader = function () {
this.child.parseAxisAndHeader();
this.parseHeader('column');
this.parseHeader('row');
this.mergeChildAxis('x');
this.mergeChildAxis('y');
};
FacetModel.prototype.parseHeader = function (channel) {
if (this.channelHasField(channel)) {
var fieldDef = this.facet[channel];
var header = fieldDef.header || {};
var title = header.title !== undefined ? header.title : fielddef_1.title(fieldDef, this.config);
if (this.child.component.layoutHeaders[channel].title) {
// merge title with child to produce "Title / Subtitle / Sub-subtitle"
title += ' / ' + this.child.component.layoutHeaders[channel].title;
this.child.component.layoutHeaders[channel].title = null;
}
this.component.layoutHeaders[channel] = {
title: title,
facetFieldDef: fieldDef,
// TODO: support adding label to footer as well
header: [this.makeHeaderComponent(channel, true)]
};
}
};
FacetModel.prototype.makeHeaderComponent = function (channel, labels) {
var sizeType = channel === 'row' ? 'height' : 'width';
return {
labels: labels,
sizeSignal: this.child.component.layoutSize.get(sizeType) ? this.child.getSizeSignalRef(sizeType) : undefined,
axes: []
};
};
FacetModel.prototype.mergeChildAxis = function (channel) {
var child = this.child;
if (child.component.axes[channel]) {
var _a = this.component, layoutHeaders = _a.layoutHeaders, resolve = _a.resolve;
var channelResolve = resolve[channel];
channelResolve.axis = resolve_1.parseGuideResolve(resolve, channel);
if (channelResolve.axis === 'shared') {
// For shared axis, move the axes to facet's header or footer
var headerChannel = channel === 'x' ? 'column' : 'row';
var layoutHeader = layoutHeaders[headerChannel];
for (var _i = 0, _b = child.component.axes[channel]; _i < _b.length; _i++) {
var axisComponent = _b[_i];
var mainAxis = axisComponent.main;
var headerType = header_1.getHeaderType(mainAxis.get('orient'));
layoutHeader[headerType] = layoutHeader[headerType] ||
[this.makeHeaderComponent(headerChannel, false)];
// LayoutHeader no longer keep track of property precedence, thus let's combine.
layoutHeader[headerType][0].axes.push(mainAxis.combine());
delete axisComponent.main;
}
}
else {
// Otherwise do nothing for independent axes
}
}
};
FacetModel.prototype.assembleScales = function () {
return assemble_2.assembleScalesForModel(this);
};
FacetModel.prototype.assembleSelectionTopLevelSignals = function (signals) {
return this.child.assembleSelectionTopLevelSignals(signals);
};
FacetModel.prototype.assembleSelectionSignals = function () {
this.child.assembleSelectionSignals();
return [];
};
FacetModel.prototype.assembleSelectionData = function (data) {
return this.child.assembleSelectionData(data);
};
FacetModel.prototype.getLayoutBandMixins = function (headerType) {
var bandMixins = {};
var bandType = headerType === 'header' ? 'headerBand' : 'footerBand';
for (var _i = 0, _a = ['row', 'column']; _i < _a.length; _i++) {
var channel = _a[_i];
var layoutHeaderComponent = this.component.layoutHeaders[channel];
var headerComponent = layoutHeaderComponent[headerType];
if (headerComponent && headerComponent[0]) {
var sizeType = channel === 'row' ? 'height' : 'width';
if (!this.child.component.layoutSize.get(sizeType)) {
// If facet child does not have size signal, then apply headerBand
bandMixins[bandType] = bandMixins[bandType] || {};
bandMixins[bandType][channel] = 0.5;
}
}
}
return bandMixins;
};
FacetModel.prototype.assembleLayout = function () {
var columns = this.channelHasField('column') ? this.columnDistinctSignal() : 1;
// TODO: determine default align based on shared / independent scales
return tslib_1.__assign({ padding: { row: 10, column: 10 } }, this.getLayoutBandMixins('header'), this.getLayoutBandMixins('footer'), {
// TODO: support offset for rowHeader/rowFooter/rowTitle/columnHeader/columnFooter/columnTitle
offset: 10, columns: columns, bounds: 'full', align: 'all' });
};
FacetModel.prototype.assembleLayoutSignals = function () {
// FIXME(https://github.com/vega/vega-lite/issues/1193): this can be incorrect if we have independent scales.
return this.child.assembleLayoutSignals();
};
FacetModel.prototype.columnDistinctSignal = function () {
if (this.parent && (this.parent instanceof FacetModel)) {
// For nested facet, we will add columns to group mark instead
// See discussion in https://github.com/vega/vega/issues/952
// and https://github.com/vega/vega-view/releases/tag/v1.2.6
return undefined;
}
else {
// In facetNode.assemble(), the name is always this.getName('column') + '_layout'.
var facetLayoutDataName = this.getName('column_domain');
return { signal: "length(data('" + facetLayoutDataName + "'))" };
}
};
FacetModel.prototype.assembleGroup = function (signals) {
if (this.parent && (this.parent instanceof FacetModel)) {
// Provide number of columns for layout.
// See discussion in https://github.com/vega/vega/issues/952
// and https://github.com/vega/vega-view/releases/tag/v1.2.6
return tslib_1.__assign({}, (this.channelHasField('column') ? {
encode: {
update: {
// TODO(https://github.com/vega/vega-lite/issues/2759):
// Correct the signal for facet of concat of facet_column
columns: { field: fielddef_1.field(this.facet.column, { binSuffix: 'start', prefix: 'distinct' }) }
}
}
} : {}), _super.prototype.assembleGroup.call(this, signals));
}
return _super.prototype.assembleGroup.call(this, signals);
};
/**
* Aggregate cardinality for calculating size
*/
FacetModel.prototype.getCardinalityAggregateForChild = function () {
var fields = [];
var ops = [];
if (this.child instanceof FacetModel) {
if (this.child.channelHasField('column')) {
fields.push(fielddef_1.field(this.child.facet.column));
ops.push('distinct');
}
}
else {
for (var _i = 0, _a = ['x', 'y']; _i < _a.length; _i++) {
var channel = _a[_i];
var childScaleComponent = this.child.component.scales[channel];
if (childScaleComponent && !childScaleComponent.merged) {
var type = childScaleComponent.get('type');
var range = childScaleComponent.get('range');
if (scale_1.hasDiscreteDomain(type) && vega_schema_1.isVgRangeStep(range)) {
var field_1 = domain_1.getFieldFromDomains(childScaleComponent.domains);
if (field_1) {
fields.push(field_1);
ops.push('distinct');
}
else {
throw new Error('We do not yet support calculation of size for faceted union domain.');
}
}
}
}
}
return fields.length ? { fields: fields, ops: ops } : undefined;
};
FacetModel.prototype.assembleMarks = function () {
var _a = this, child = _a.child, facet = _a.facet;
var facetRoot = this.component.data.facetRoot;
var data = assemble_1.assembleFacetData(facetRoot);
// If we facet by two dimensions, we need to add a cross operator to the aggregation
// so that we create all groups
var hasRow = this.channelHasField(channel_1.ROW);
var hasColumn = this.channelHasField(channel_1.COLUMN);
var layoutSizeEncodeEntry = child.assembleLayoutSize();
var aggregateMixins = {};
if (hasRow && hasColumn) {
aggregateMixins.aggregate = { cross: true };
}
var cardinalityAggregateForChild = this.getCardinalityAggregateForChild();
if (cardinalityAggregateForChild) {
aggregateMixins.aggregate = tslib_1.__assign({}, aggregateMixins.aggregate, cardinalityAggregateForChild);
}
var title = child.assembleTitle();
var markGroup = tslib_1.__assign({ name: this.getName('cell'), type: 'group' }, (title ? { title: title } : {}), { from: {
facet: tslib_1.__assign({ name: facetRoot.name, data: facetRoot.data, groupby: [].concat(hasRow ? [this.field(channel_1.ROW)] : [], hasColumn ? [this.field(channel_1.COLUMN)] : []) }, aggregateMixins)
}, sort: {
field: [].concat(hasRow ? [this.field(channel_1.ROW, { expr: 'datum' })] : [], hasColumn ? [this.field(channel_1.COLUMN, { expr: 'datum' })] : []),
order: [].concat(hasRow ? [(facet.row.header && facet.row.header.sort) || 'ascending'] : [], hasColumn ? [(facet.column.header && facet.column.header.sort) || 'ascending'] : [])
} }, (data.length > 0 ? { data: data } : {}), (layoutSizeEncodeEntry ? { encode: { update: layoutSizeEncodeEntry } } : {}), child.assembleGroup());
return [markGroup];
};
FacetModel.prototype.getMapping = function () {
return this.facet;
};
return FacetModel;
}(model_1.ModelWithField));
exports.FacetModel = FacetModel;
},{"../channel":8,"../encoding":87,"../fielddef":89,"../log":94,"../scale":97,"../util":107,"../vega.schema":109,"./buildmodel":15,"./data/assemble":20,"./data/parse":29,"./layout/header":36,"./layoutsize/parse":38,"./model":56,"./repeater":58,"./resolve":59,"./scale/assemble":60,"./scale/domain":62,"tslib":114}],35:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var log = require("../log");
var spec_1 = require("../spec");
var util_1 = require("../util");
var parse_1 = require("./axis/parse");
var parse_2 = require("./data/parse");
var assemble_1 = require("./layoutsize/assemble");
var parse_3 = require("./layoutsize/parse");
var model_1 = require("./model");
var assemble_2 = require("./scale/assemble");
var selection_1 = require("./selection/selection");
var unit_1 = require("./unit");
var LayerModel = (function (_super) {
tslib_1.__extends(LayerModel, _super);
function LayerModel(spec, parent, parentGivenName, parentGivenSize, repeater, config) {
var _this = _super.call(this, spec, parent, parentGivenName, config, spec.resolve) || this;
_this.type = 'layer';
var layoutSize = tslib_1.__assign({}, parentGivenSize, (spec.width ? { width: spec.width } : {}), (spec.height ? { height: spec.height } : {}));
_this.initSize(layoutSize);
_this.children = spec.layer.map(function (layer, i) {
if (spec_1.isLayerSpec(layer)) {
return new LayerModel(layer, _this, _this.getName('layer_' + i), layoutSize, repeater, config);
}
if (spec_1.isUnitSpec(layer)) {
return new unit_1.UnitModel(layer, _this, _this.getName('layer_' + i), layoutSize, repeater, config);
}
throw new Error(log.message.INVALID_SPEC);
});
return _this;
}
LayerModel.prototype.parseData = function () {
this.component.data = parse_2.parseData(this);
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
child.parseData();
}
};
LayerModel.prototype.parseLayoutSize = function () {
parse_3.parseLayerLayoutSize(this);
};
LayerModel.prototype.parseSelection = function () {
var _this = this;
// Merge selections up the hierarchy so that they may be referenced
// across unit specs. Persist their definitions within each child
// to assemble signals which remain within output Vega unit groups.
this.component.selection = {};
var _loop_1 = function (child) {
child.parseSelection();
util_1.keys(child.component.selection).forEach(function (key) {
_this.component.selection[key] = child.component.selection[key];
});
};
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
_loop_1(child);
}
};
LayerModel.prototype.parseMarkGroup = function () {
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
child.parseMarkGroup();
}
};
LayerModel.prototype.parseAxisAndHeader = function () {
parse_1.parseLayerAxis(this);
};
LayerModel.prototype.assembleSelectionTopLevelSignals = function (signals) {
return this.children.reduce(function (sg, child) { return child.assembleSelectionTopLevelSignals(sg); }, signals);
};
// TODO: Support same named selections across children.
LayerModel.prototype.assembleSelectionSignals = function () {
return this.children.reduce(function (signals, child) {
return signals.concat(child.assembleSelectionSignals());
}, []);
};
LayerModel.prototype.assembleLayoutSignals = function () {
return this.children.reduce(function (signals, child) {
return signals.concat(child.assembleLayoutSignals());
}, assemble_1.assembleLayoutSignals(this));
};
LayerModel.prototype.assembleSelectionData = function (data) {
return this.children.reduce(function (db, child) { return child.assembleSelectionData(db); }, []);
};
LayerModel.prototype.assembleTitle = function () {
var title = _super.prototype.assembleTitle.call(this);
if (title) {
return title;
}
// If title does not provide layer, look into children
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
title = child.assembleTitle();
if (title) {
return title;
}
}
return undefined;
};
LayerModel.prototype.assembleScales = function () {
return assemble_2.assembleScaleForModelAndChildren(this);
};
LayerModel.prototype.assembleLayout = function () {
return null;
};
LayerModel.prototype.assembleMarks = function () {
return selection_1.assembleLayerSelectionMarks(this, util_1.flatten(this.children.map(function (child) {
return child.assembleMarks();
})));
};
return LayerModel;
}(model_1.Model));
exports.LayerModel = LayerModel;
},{"../log":94,"../spec":100,"../util":107,"./axis/parse":12,"./data/parse":29,"./layoutsize/assemble":37,"./layoutsize/parse":38,"./model":56,"./scale/assemble":60,"./selection/selection":69,"./unit":80,"tslib":114}],36:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var fielddef_1 = require("../../fielddef");
var common_1 = require("../common");
exports.HEADER_CHANNELS = ['row', 'column'];
exports.HEADER_TYPES = ['header', 'footer'];
function getHeaderType(orient) {
if (orient === 'top' || orient === 'left') {
return 'header';
}
return 'footer';
}
exports.getHeaderType = getHeaderType;
function getTitleGroup(model, channel) {
var title = model.component.layoutHeaders[channel].title;
var textOrient = channel === 'row' ? 'vertical' : undefined;
return {
name: model.getName(channel + "_title"),
role: channel + "-title",
type: 'group',
marks: [{
type: 'text',
role: channel + "-title-text",
encode: {
update: tslib_1.__assign({
// TODO: add title align
align: { value: 'center' }, text: { value: title }, fill: { value: 'black' }, fontWeight: { value: 'bold' } }, (textOrient === 'vertical' ? { angle: { value: 270 } } : {}))
}
}]
};
}
exports.getTitleGroup = getTitleGroup;
function getHeaderGroup(model, channel, headerType, layoutHeader, header) {
if (header) {
var title = null;
if (layoutHeader.facetFieldDef && header.labels) {
var facetFieldDef = layoutHeader.facetFieldDef;
var format = facetFieldDef.header ? facetFieldDef.header.format : undefined;
title = {
text: common_1.formatSignalRef(facetFieldDef, format, 'parent', model.config, true),
offset: 10,
orient: channel === 'row' ? 'left' : 'top',
encode: {
update: tslib_1.__assign({ fontWeight: { value: 'normal' }, angle: { value: 0 }, fontSize: { value: 10 } }, (channel === 'row' ? {
align: { value: 'right' },
baseline: { value: 'middle' }
} : {}))
}
};
}
var axes = header.axes;
var hasAxes = axes && axes.length > 0;
if (title || hasAxes) {
var sizeChannel = channel === 'row' ? 'height' : 'width';
return tslib_1.__assign({ name: model.getName(channel + "_" + headerType), type: 'group', role: channel + "-" + headerType }, (layoutHeader.facetFieldDef ? {
from: { data: model.getName(channel + '_domain') },
sort: {
field: fielddef_1.field(layoutHeader.facetFieldDef, { expr: 'datum' }),
order: (layoutHeader.facetFieldDef.header && layoutHeader.facetFieldDef.header.sort) || 'ascending'
}
} : {}), (title ? { title: title } : {}), (header.sizeSignal ? {
encode: {
update: (_a = {},
_a[sizeChannel] = header.sizeSignal,
_a)
}
} : {}), (hasAxes ? { axes: axes } : {}));
}
}
return null;
var _a;
}
exports.getHeaderGroup = getHeaderGroup;
},{"../../fielddef":89,"../common":16,"tslib":114}],37:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var scale_1 = require("../../scale");
var vega_schema_1 = require("../../vega.schema");
var model_1 = require("../model");
function assembleLayoutSignals(model) {
return [].concat(sizeSignals(model, 'width'), sizeSignals(model, 'height'));
}
exports.assembleLayoutSignals = assembleLayoutSignals;
function sizeSignals(model, sizeType) {
var channel = sizeType === 'width' ? 'x' : 'y';
var size = model.component.layoutSize.get(sizeType);
if (!size || size === 'merged') {
return [];
}
// Read size signal name from name map, just in case it is the top-level size signal that got renamed.
var name = model.getSizeSignalRef(sizeType).signal;
if (size === 'range-step') {
var scaleComponent = model.getScaleComponent(channel);
if (scaleComponent) {
var type = scaleComponent.get('type');
var range = scaleComponent.get('range');
if (scale_1.hasDiscreteDomain(type) && vega_schema_1.isVgRangeStep(range)) {
var scaleName = model.scaleName(channel);
if (model_1.isFacetModel(model.parent)) {
// If parent is facet and this is an independent scale, return only signal signal
// as the width/height will be calculated using the cardinality from
// facet's aggregate rather than reading from scale domain
var parentChannelResolve = model.parent.component.resolve[channel];
if (parentChannelResolve.scale === 'independent') {
return [stepSignal(scaleName, range)];
}
}
return [
stepSignal(scaleName, range),
{
name: name,
update: sizeExpr(scaleName, scaleComponent, "domain('" + scaleName + "').length")
}
];
}
}
/* istanbul ignore next: Condition should not happen -- only for warning in development. */
throw new Error('layout size is range step although there is no rangeStep.');
}
else {
return [{
name: name,
update: "" + size
}];
}
}
exports.sizeSignals = sizeSignals;
function stepSignal(scaleName, range) {
return {
name: scaleName + '_step',
value: range.step,
};
}
function sizeExpr(scaleName, scaleComponent, cardinality) {
var type = scaleComponent.get('type');
var padding = scaleComponent.get('padding');
var paddingOuter = scaleComponent.get('paddingOuter');
paddingOuter = paddingOuter !== undefined ? paddingOuter : padding;
var paddingInner = scaleComponent.get('paddingInner');
paddingInner = type === 'band' ?
// only band has real paddingInner
(paddingInner !== undefined ? paddingInner : padding) :
// For point, as calculated in https://github.com/vega/vega-scale/blob/master/src/band.js#L128,
// it's equivalent to have paddingInner = 1 since there is only n-1 steps between n points.
1;
return "bandspace(" + cardinality + ", " + paddingInner + ", " + paddingOuter + ") * " + scaleName + "_step";
}
exports.sizeExpr = sizeExpr;
},{"../../scale":97,"../../vega.schema":109,"../model":56}],38:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var scale_1 = require("../../scale");
var vega_schema_1 = require("../../vega.schema");
var split_1 = require("../split");
function parseLayerLayoutSize(model) {
parseChildrenLayoutSize(model);
var layoutSizeCmpt = model.component.layoutSize;
layoutSizeCmpt.setWithExplicit('width', parseNonUnitLayoutSizeForChannel(model, 'width'));
layoutSizeCmpt.setWithExplicit('height', parseNonUnitLayoutSizeForChannel(model, 'height'));
}
exports.parseLayerLayoutSize = parseLayerLayoutSize;
exports.parseRepeatLayoutSize = parseLayerLayoutSize;
function parseConcatLayoutSize(model) {
parseChildrenLayoutSize(model);
var layoutSizeCmpt = model.component.layoutSize;
var sizeTypeToMerge = model.isVConcat ? 'width' : 'height';
layoutSizeCmpt.setWithExplicit(sizeTypeToMerge, parseNonUnitLayoutSizeForChannel(model, sizeTypeToMerge));
}
exports.parseConcatLayoutSize = parseConcatLayoutSize;
function parseChildrenLayoutSize(model) {
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
child.parseLayoutSize();
}
}
exports.parseChildrenLayoutSize = parseChildrenLayoutSize;
function parseNonUnitLayoutSizeForChannel(model, sizeType) {
var channel = sizeType === 'width' ? 'x' : 'y';
var resolve = model.component.resolve;
var mergedSize;
// Try to merge layout size
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
var childSize = child.component.layoutSize.getWithExplicit(sizeType);
var scaleResolve = resolve[channel] ? resolve[channel].scale : undefined;
if (scaleResolve === 'independent' && childSize.value === 'range-step') {
// Do not merge independent scales with range-step as their size depends
// on the scale domains, which can be different between scales.
mergedSize = undefined;
break;
}
if (mergedSize) {
if (scaleResolve === 'independent' && mergedSize.value !== childSize.value) {
// For independent scale, only merge if all the sizes are the same.
// If the values are different, abandon the merge!
mergedSize = undefined;
break;
}
mergedSize = split_1.mergeValuesWithExplicit(mergedSize, childSize, sizeType, '', split_1.defaultTieBreaker);
}
else {
mergedSize = childSize;
}
}
if (mergedSize) {
// If merged, rename size and set size of all children.
for (var _b = 0, _c = model.children; _b < _c.length; _b++) {
var child = _c[_b];
model.renameLayoutSize(child.getName(sizeType), model.getName(sizeType));
child.component.layoutSize.set(sizeType, 'merged', false);
}
return mergedSize;
}
else {
// Otherwise, there is no merged size.
return {
explicit: false,
value: undefined
};
}
}
function parseUnitLayoutSize(model) {
var layoutSizeComponent = model.component.layoutSize;
if (!layoutSizeComponent.explicit.width) {
var width = defaultUnitSize(model, 'width');
layoutSizeComponent.set('width', width, false);
}
if (!layoutSizeComponent.explicit.height) {
var height = defaultUnitSize(model, 'height');
layoutSizeComponent.set('height', height, false);
}
}
exports.parseUnitLayoutSize = parseUnitLayoutSize;
function defaultUnitSize(model, sizeType) {
var channel = sizeType === 'width' ? 'x' : 'y';
var config = model.config;
var scaleComponent = model.getScaleComponent(channel);
if (scaleComponent) {
var scaleType = scaleComponent.get('type');
var range = scaleComponent.get('range');
if (scale_1.hasDiscreteDomain(scaleType) && vega_schema_1.isVgRangeStep(range)) {
// For discrete domain with range.step, use dynamic width/height
return 'range-step';
}
else {
// FIXME(https://github.com/vega/vega-lite/issues/1975): revise config.cell name
// Otherwise, read this from cell config
return config.cell[sizeType];
}
}
else {
// No scale - set default size
if (sizeType === 'width' && model.mark() === 'text') {
// width for text mark without x-field is a bit wider than typical range step
return config.scale.textXRangeStep;
}
// Set width/height equal to rangeStep config or if rangeStep is null, use value from default scale config.
return config.scale.rangeStep || scale_1.defaultScaleConfig.rangeStep;
}
}
},{"../../scale":97,"../../vega.schema":109,"../split":79}],39:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var stringify = require("json-stable-stringify");
var util_1 = require("../../util");
var parse_1 = require("./parse");
function assembleLegends(model) {
var legendComponentIndex = model.component.legends;
var legendByDomain = {};
util_1.keys(legendComponentIndex).forEach(function (channel) {
var scaleComponent = model.getScaleComponent(channel);
var domainHash = stringify(scaleComponent.domains);
if (legendByDomain[domainHash]) {
for (var _i = 0, _a = legendByDomain[domainHash]; _i < _a.length; _i++) {
var mergedLegendComponent = _a[_i];
var merged = parse_1.mergeLegendComponent(mergedLegendComponent, legendComponentIndex[channel]);
if (!merged) {
// If cannot merge, need to add this legend separately
legendByDomain[domainHash].push(legendComponentIndex[channel]);
}
}
}
else {
legendByDomain[domainHash] = [legendComponentIndex[channel].clone()];
}
});
return util_1.flatten(util_1.vals(legendByDomain)).map(function (legendCmpt) { return legendCmpt.combine(); });
}
exports.assembleLegends = assembleLegends;
},{"../../util":107,"./parse":42,"json-stable-stringify":110}],40:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var split_1 = require("../split");
var LegendComponent = (function (_super) {
tslib_1.__extends(LegendComponent, _super);
function LegendComponent() {
return _super !== null && _super.apply(this, arguments) || this;
}
return LegendComponent;
}(split_1.Split));
exports.LegendComponent = LegendComponent;
},{"../split":79,"tslib":114}],41:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("../../channel");
var fielddef_1 = require("../../fielddef");
var mark_1 = require("../../mark");
var scale_1 = require("../../scale");
var type_1 = require("../../type");
var util_1 = require("../../util");
var common_1 = require("../common");
var mixins = require("../mark/mixins");
function symbols(fieldDef, symbolsSpec, model, channel) {
var symbols = {};
var mark = model.mark();
switch (mark) {
case mark_1.BAR:
case mark_1.TICK:
case mark_1.TEXT:
symbols.shape = { value: 'square' };
break;
case mark_1.CIRCLE:
case mark_1.SQUARE:
symbols.shape = { value: mark };
break;
case mark_1.POINT:
case mark_1.LINE:
case mark_1.AREA:
// use default circle
break;
}
var cfg = model.config;
var filled = model.markDef.filled;
var config = channel === channel_1.COLOR ?
/* For color's legend, do not set fill (when filled) or stroke (when unfilled) property from config because the legend's `fill` or `stroke` scale should have precedence */
util_1.without(mark_1.FILL_STROKE_CONFIG, [filled ? 'fill' : 'stroke', 'strokeDash', 'strokeDashOffset']) :
/* For other legend, no need to omit. */
mark_1.FILL_STROKE_CONFIG;
config = util_1.without(config, ['strokeDash', 'strokeDashOffset']);
common_1.applyMarkConfig(symbols, model, config);
if (channel !== channel_1.COLOR) {
var colorMixins = mixins.color(model);
// If there are field for fill or stroke, remove them as we already apply channels.
if (colorMixins.fill && (colorMixins.fill['field'] || colorMixins.fill['value'] === 'transparent')) {
delete colorMixins.fill;
}
if (colorMixins.stroke && (colorMixins.stroke['field'] || colorMixins.stroke['value'] === 'transparent')) {
delete colorMixins.stroke;
}
util_1.extend(symbols, colorMixins);
}
if (channel !== channel_1.SHAPE) {
var shapeDef = model.encoding.shape;
if (fielddef_1.isValueDef(shapeDef)) {
symbols.shape = { value: shapeDef.value };
}
}
symbols = util_1.extend(symbols, symbolsSpec || {});
return util_1.keys(symbols).length > 0 ? symbols : undefined;
}
exports.symbols = symbols;
function labels(fieldDef, labelsSpec, model, channel) {
var legend = model.legend(channel);
var config = model.config;
var labels = {};
if (fieldDef.type === type_1.TEMPORAL) {
var isUTCScale = model.getScaleComponent(channel).get('type') === scale_1.ScaleType.UTC;
labelsSpec = util_1.extend({
text: {
signal: common_1.timeFormatExpression('datum.value', fieldDef.timeUnit, legend.format, config.legend.shortTimeLabels, config.timeFormat, isUTCScale)
}
}, labelsSpec || {});
}
labels = util_1.extend(labels, labelsSpec || {});
return util_1.keys(labels).length > 0 ? labels : undefined;
}
exports.labels = labels;
},{"../../channel":8,"../../fielddef":89,"../../mark":96,"../../scale":97,"../../type":106,"../../util":107,"../common":16,"../mark/mixins":49}],42:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("../../channel");
var fielddef_1 = require("../../fielddef");
var legend_1 = require("../../legend");
var util_1 = require("../../util");
var common_1 = require("../common");
var model_1 = require("../model");
var resolve_1 = require("../resolve");
var split_1 = require("../split");
var split_2 = require("../split");
var component_1 = require("./component");
var encode = require("./encode");
var rules = require("./rules");
function parseLegend(model) {
if (model_1.isUnitModel(model)) {
model.component.legends = parseUnitLegend(model);
}
else {
model.component.legends = parseNonUnitLegend(model);
}
}
exports.parseLegend = parseLegend;
function parseUnitLegend(model) {
return [channel_1.COLOR, channel_1.SIZE, channel_1.SHAPE, channel_1.OPACITY].reduce(function (legendComponent, channel) {
if (model.legend(channel)) {
legendComponent[channel] = parseLegendForChannel(model, channel);
}
return legendComponent;
}, {});
}
function getLegendDefWithScale(model, channel) {
// For binned field with continuous scale, use a special scale so we can overrride the mark props and labels
switch (channel) {
case channel_1.COLOR:
var scale = model.scaleName(channel_1.COLOR);
return model.markDef.filled ? { fill: scale } : { stroke: scale };
case channel_1.SIZE:
return { size: model.scaleName(channel_1.SIZE) };
case channel_1.SHAPE:
return { shape: model.scaleName(channel_1.SHAPE) };
case channel_1.OPACITY:
return { opacity: model.scaleName(channel_1.OPACITY) };
}
return null;
}
function parseLegendForChannel(model, channel) {
var fieldDef = model.fieldDef(channel);
var legend = model.legend(channel);
var legendCmpt = new component_1.LegendComponent({}, getLegendDefWithScale(model, channel));
legend_1.LEGEND_PROPERTIES.forEach(function (property) {
var value = getProperty(property, legend, channel, model);
if (value !== undefined) {
var explicit = property === 'values' ?
!!legend.values :
value === legend[property];
legendCmpt.set(property, value, explicit);
}
});
// 2) Add mark property definition groups
var legendEncoding = legend.encoding || {};
var legendEncode = ['labels', 'legend', 'title', 'symbols'].reduce(function (e, part) {
var value = encode[part] ?
encode[part](fieldDef, legendEncoding[part], model, channel) :
legendEncoding[part]; // no rule -- just default values
if (value !== undefined && util_1.keys(value).length > 0) {
e[part] = { update: value };
}
return e;
}, {});
if (util_1.keys(legendEncode).length > 0) {
legendCmpt.set('encode', legendEncode, !!legend.encoding);
}
return legendCmpt;
}
exports.parseLegendForChannel = parseLegendForChannel;
function getProperty(property, specifiedLegend, channel, model) {
var fieldDef = model.fieldDef(channel);
switch (property) {
case 'format':
return common_1.numberFormat(fieldDef, specifiedLegend.format, model.config);
case 'title':
return common_1.getSpecifiedOrDefaultValue(specifiedLegend.title, fielddef_1.title(fieldDef, model.config));
case 'values':
return rules.values(specifiedLegend);
case 'type':
return common_1.getSpecifiedOrDefaultValue(specifiedLegend.type, rules.type(fieldDef.type, channel, model.getScaleComponent(channel).get('type')));
}
// Otherwise, return specified property.
return specifiedLegend[property];
}
function parseNonUnitLegend(model) {
var _a = model.component, legends = _a.legends, resolve = _a.resolve;
var _loop_1 = function (child) {
parseLegend(child);
util_1.keys(child.component.legends).forEach(function (channel) {
var channelResolve = model.component.resolve[channel];
channelResolve.legend = resolve_1.parseGuideResolve(model.component.resolve, channel);
if (channelResolve.legend === 'shared') {
// If the resolve says shared (and has not been overridden)
// We will try to merge and see if there is a conflict
legends[channel] = mergeLegendComponent(legends[channel], child.component.legends[channel]);
if (!legends[channel]) {
// If merge returns nothing, there is a conflict so we cannot make the legend shared.
// Thus, mark legend as independent and remove the legend component.
channelResolve.legend = 'independent';
delete legends[channel];
}
}
});
};
for (var _i = 0, _b = model.children; _i < _b.length; _i++) {
var child = _b[_i];
_loop_1(child);
}
util_1.keys(legends).forEach(function (channel) {
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
if (!child.component.legends[channel]) {
// skip if the child does not have a particular legend
continue;
}
if (resolve[channel].legend === 'shared') {
// After merging shared legend, make sure to remove legend from child
delete child.component.legends[channel];
}
}
});
return legends;
}
function mergeLegendComponent(mergedLegend, childLegend) {
if (!mergedLegend) {
return childLegend.clone();
}
var mergedOrient = mergedLegend.getWithExplicit('orient');
var childOrient = childLegend.getWithExplicit('orient');
if (mergedOrient.explicit && childOrient.explicit && mergedOrient.value !== childOrient.value) {
// TODO: throw warning if resolve is explicit (We don't have info about explicit/implicit resolve yet.)
// Cannot merge due to inconsistent orient
return undefined;
}
var _loop_2 = function (prop) {
var mergedValueWithExplicit = split_2.mergeValuesWithExplicit(mergedLegend.getWithExplicit(prop), childLegend.getWithExplicit(prop), prop, 'legend',
// Tie breaker function
function (v1, v2) {
switch (prop) {
case 'title':
return common_1.titleMerger(v1, v2);
case 'type':
// There are only two types. If we have different types, then prefer symbol over gradient.
return split_1.makeImplicit('symbol');
}
return split_2.defaultTieBreaker(v1, v2, prop, 'legend');
});
mergedLegend.setWithExplicit(prop, mergedValueWithExplicit);
};
// Otherwise, let's merge
for (var _i = 0, VG_LEGEND_PROPERTIES_1 = legend_1.VG_LEGEND_PROPERTIES; _i < VG_LEGEND_PROPERTIES_1.length; _i++) {
var prop = VG_LEGEND_PROPERTIES_1[_i];
_loop_2(prop);
}
return mergedLegend;
}
exports.mergeLegendComponent = mergeLegendComponent;
},{"../../channel":8,"../../fielddef":89,"../../legend":93,"../../util":107,"../common":16,"../model":56,"../resolve":59,"../split":79,"./component":40,"./encode":41,"./rules":43}],43:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("../../channel");
var datetime_1 = require("../../datetime");
var scale_1 = require("../../scale");
var util_1 = require("../../util");
function values(legend) {
var vals = legend.values;
if (vals && datetime_1.isDateTime(vals[0])) {
return vals.map(function (dt) {
// normalize = true as end user won't put 0 = January
return { signal: datetime_1.dateTimeExpr(dt, true) };
});
}
return vals;
}
exports.values = values;
function type(type, channel, scaleType) {
if (channel === channel_1.COLOR && ((type === 'quantitative' && !scale_1.isBinScale(scaleType)) ||
(type === 'temporal' && util_1.contains(['time', 'utc'], scaleType)))) {
return 'gradient';
}
return undefined;
}
exports.type = type;
},{"../../channel":8,"../../datetime":86,"../../scale":97,"../../util":107}],44:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var mixins = require("./mixins");
exports.area = {
vgMark: 'area',
encodeEntry: function (model) {
return tslib_1.__assign({}, mixins.pointPosition('x', model, 'zeroOrMin'), mixins.pointPosition('y', model, 'zeroOrMin'), mixins.pointPosition2(model, 'zeroOrMin'), mixins.color(model), mixins.text(model, 'tooltip'), mixins.nonPosition('opacity', model), mixins.markDefProperties(model.markDef, ['orient', 'interpolate', 'tension']));
}
};
},{"./mixins":49,"tslib":114}],45:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_util_1 = require("vega-util");
var channel_1 = require("../../channel");
var fielddef_1 = require("../../fielddef");
var log = require("../../log");
var scale_1 = require("../../scale");
var vega_schema_1 = require("../../vega.schema");
var mixins = require("./mixins");
var ref = require("./valueref");
exports.bar = {
vgMark: 'rect',
encodeEntry: function (model) {
var stack = model.stack;
return tslib_1.__assign({}, x(model, stack), y(model, stack), mixins.color(model), mixins.text(model, 'tooltip'), mixins.nonPosition('opacity', model));
}
};
function x(model, stack) {
var config = model.config, width = model.width;
var orient = model.markDef.orient;
var sizeDef = model.encoding.size;
var xDef = model.encoding.x;
var xScaleName = model.scaleName(channel_1.X);
var xScale = model.getScaleComponent(channel_1.X);
// x, x2, and width -- we must specify two of these in all conditions
if (orient === 'horizontal') {
return tslib_1.__assign({}, mixins.pointPosition('x', model, 'zeroOrMin'), mixins.pointPosition2(model, 'zeroOrMin'));
}
else {
if (fielddef_1.isFieldDef(xDef)) {
if (xDef.bin && !sizeDef) {
return mixins.binnedPosition(xDef, 'x', model.scaleName('x'), config.bar.binSpacing);
}
else {
var xScaleType = xScale.get('type');
if (xScaleType === scale_1.ScaleType.BAND) {
return mixins.bandPosition(xDef, 'x', model);
}
}
}
// sized bin, normal point-ordinal axis, quantitative x-axis, or no x
return mixins.centeredBandPosition('x', model, tslib_1.__assign({}, ref.mid(width)), defaultSizeRef(xScaleName, xScale, config));
}
}
function y(model, stack) {
var config = model.config, encoding = model.encoding, height = model.height;
var orient = model.markDef.orient;
var sizeDef = encoding.size;
var yDef = encoding.y;
var yScaleName = model.scaleName(channel_1.Y);
var yScale = model.getScaleComponent(channel_1.Y);
// y, y2 & height -- we must specify two of these in all conditions
if (orient === 'vertical') {
return tslib_1.__assign({}, mixins.pointPosition('y', model, 'zeroOrMin'), mixins.pointPosition2(model, 'zeroOrMin'));
}
else {
if (fielddef_1.isFieldDef(yDef)) {
var yScaleType = yScale.get('type');
if (yDef.bin && !sizeDef) {
return mixins.binnedPosition(yDef, 'y', model.scaleName('y'), config.bar.binSpacing);
}
else if (yScaleType === scale_1.ScaleType.BAND) {
return mixins.bandPosition(yDef, 'y', model);
}
}
return mixins.centeredBandPosition('y', model, ref.mid(height), defaultSizeRef(yScaleName, yScale, config));
}
}
function defaultSizeRef(scaleName, scale, config) {
if (config.bar.discreteBandSize) {
return { value: config.bar.discreteBandSize };
}
if (scale) {
var scaleType = scale.get('type');
if (scaleType === scale_1.ScaleType.POINT) {
var scaleRange = scale.get('range');
if (vega_schema_1.isVgRangeStep(scaleRange) && vega_util_1.isNumber(scaleRange.step)) {
return { value: scaleRange.step - 1 };
}
log.warn(log.message.BAR_WITH_POINT_SCALE_AND_RANGESTEP_NULL);
}
else if (scaleType === scale_1.ScaleType.BAND) {
return ref.band(scaleName);
}
else {
return { value: config.bar.continuousBandSize };
}
}
if (config.scale.rangeStep && config.scale.rangeStep !== null) {
return { value: config.scale.rangeStep - 1 };
}
// TODO: this should depends on cell's width / height?
return { value: 20 };
}
},{"../../channel":8,"../../fielddef":89,"../../log":94,"../../scale":97,"../../vega.schema":109,"./mixins":49,"./valueref":55,"tslib":114,"vega-util":116}],46:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var encoding_1 = require("../../encoding");
var fielddef_1 = require("../../fielddef");
var log = require("../../log");
var mark_1 = require("../../mark");
var scale_1 = require("../../scale");
var type_1 = require("../../type");
var util_1 = require("../../util");
var common_1 = require("../common");
function normalizeMarkDef(markDef, encoding, scales, config) {
var specifiedOrient = markDef.orient || common_1.getMarkConfig('orient', markDef, config);
markDef.orient = orient(markDef.type, encoding, scales, specifiedOrient);
if (specifiedOrient !== undefined && specifiedOrient !== markDef.orient) {
log.warn(log.message.orientOverridden(markDef.orient, specifiedOrient));
}
var specifiedFilled = markDef.filled;
if (specifiedFilled === undefined) {
markDef.filled = filled(markDef, config);
}
}
exports.normalizeMarkDef = normalizeMarkDef;
/**
* Initialize encoding's value with some special default values
*/
function initEncoding(mark, encoding, stacked, config) {
var opacityConfig = common_1.getMarkConfig('opacity', mark, config);
if (!encoding.opacity && opacityConfig === undefined) {
var opacity = defaultOpacity(mark.type, encoding, stacked);
if (opacity !== undefined) {
encoding.opacity = { value: opacity };
}
}
return encoding;
}
exports.initEncoding = initEncoding;
function defaultOpacity(mark, encoding, stacked) {
if (util_1.contains([mark_1.POINT, mark_1.TICK, mark_1.CIRCLE, mark_1.SQUARE], mark)) {
// point-based marks
if (!encoding_1.isAggregate(encoding)) {
return 0.7;
}
}
return undefined;
}
function filled(markDef, config) {
var filledConfig = common_1.getMarkConfig('filled', markDef, config);
var mark = markDef.type;
return filledConfig !== undefined ? filledConfig : mark !== mark_1.POINT && mark !== mark_1.LINE && mark !== mark_1.RULE;
}
function orient(mark, encoding, scales, specifiedOrient) {
switch (mark) {
case mark_1.POINT:
case mark_1.CIRCLE:
case mark_1.SQUARE:
case mark_1.TEXT:
case mark_1.RECT:
// orient is meaningless for these marks.
return undefined;
}
var yIsRange = encoding.y && encoding.y2;
var xIsRange = encoding.x && encoding.x2;
switch (mark) {
case mark_1.TICK:
var xScaleType = scales.x ? scales.x.get('type') : null;
var yScaleType = scales.y ? scales.y.get('type') : null;
// Tick is opposite to bar, line, area and never have ranged mark.
if (!scale_1.hasDiscreteDomain(xScaleType) && (!encoding.y ||
scale_1.hasDiscreteDomain(yScaleType) ||
(fielddef_1.isFieldDef(encoding.y) && encoding.y.bin))) {
return 'vertical';
}
// y:Q or Ambiguous case, return horizontal
return 'horizontal';
case mark_1.RULE:
case mark_1.BAR:
case mark_1.AREA:
// If there are range for both x and y, y (vertical) has higher precedence.
if (yIsRange) {
return 'vertical';
}
else if (xIsRange) {
return 'horizontal';
}
else if (mark === mark_1.RULE) {
if (encoding.x && !encoding.y) {
return 'vertical';
}
else if (encoding.y && !encoding.x) {
return 'horizontal';
}
}
/* tslint:disable */
case mark_1.LINE:// intentional fall through
/* tslint:enable */
var xIsContinuous = fielddef_1.isFieldDef(encoding.x) && fielddef_1.isContinuous(encoding.x);
var yIsContinuous = fielddef_1.isFieldDef(encoding.y) && fielddef_1.isContinuous(encoding.y);
if (xIsContinuous && !yIsContinuous) {
return 'horizontal';
}
else if (!xIsContinuous && yIsContinuous) {
return 'vertical';
}
else if (xIsContinuous && yIsContinuous) {
var xDef = encoding.x; // we can cast here since they are surely fieldDef
var yDef = encoding.y;
var xIsTemporal = xDef.type === type_1.TEMPORAL;
var yIsTemporal = yDef.type === type_1.TEMPORAL;
// temporal without timeUnit is considered continuous, but better serves as dimension
if (xIsTemporal && !yIsTemporal) {
return 'vertical';
}
else if (!xIsTemporal && yIsTemporal) {
return 'horizontal';
}
if (!xDef.aggregate && yDef.aggregate) {
return 'vertical';
}
else if (xDef.aggregate && !yDef.aggregate) {
return 'horizontal';
}
if (specifiedOrient) {
// When ambiguous, use user specified one.
return specifiedOrient;
}
if (!(mark === mark_1.LINE && encoding.order)) {
// Except for connected scatterplot, we should log warning for unclear orientation of QxQ plots.
log.warn(log.message.unclearOrientContinuous(mark));
}
return 'vertical';
}
else {
// For Discrete x Discrete case, return undefined.
log.warn(log.message.unclearOrientDiscreteOrEmpty(mark));
return undefined;
}
}
return 'vertical';
}
},{"../../encoding":87,"../../fielddef":89,"../../log":94,"../../mark":96,"../../scale":97,"../../type":106,"../../util":107,"../common":16}],47:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var mixins = require("./mixins");
exports.line = {
vgMark: 'line',
encodeEntry: function (model) {
return tslib_1.__assign({}, mixins.pointPosition('x', model, 'zeroOrMin'), mixins.pointPosition('y', model, 'zeroOrMin'), mixins.color(model), mixins.text(model, 'tooltip'), mixins.nonPosition('opacity', model), mixins.nonPosition('size', model, {
vgChannel: 'strokeWidth' // VL's line size is strokeWidth
}), mixins.markDefProperties(model.markDef, ['interpolate', 'tension']));
}
};
},{"./mixins":49,"tslib":114}],48:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_util_1 = require("vega-util");
var channel_1 = require("../../channel");
var data_1 = require("../../data");
var encoding_1 = require("../../encoding");
var fielddef_1 = require("../../fielddef");
var mark_1 = require("../../mark");
var sort_1 = require("../../sort");
var util_1 = require("../../util");
var common_1 = require("../common");
var model_1 = require("../model");
var area_1 = require("./area");
var bar_1 = require("./bar");
var init_1 = require("./init");
var line_1 = require("./line");
var point_1 = require("./point");
var rect_1 = require("./rect");
var rule_1 = require("./rule");
var text_1 = require("./text");
var tick_1 = require("./tick");
var markCompiler = {
area: area_1.area,
bar: bar_1.bar,
line: line_1.line,
point: point_1.point,
text: text_1.text,
tick: tick_1.tick,
rect: rect_1.rect,
rule: rule_1.rule,
circle: point_1.circle,
square: point_1.square
};
function parseMarkDef(model) {
if (model_1.isUnitModel(model)) {
init_1.normalizeMarkDef(model.markDef, model.encoding, model.component.scales, model.config);
}
else {
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
parseMarkDef(child);
}
}
}
exports.parseMarkDef = parseMarkDef;
function parseMarkGroup(model) {
if (util_1.contains([mark_1.LINE, mark_1.AREA], model.mark())) {
return parsePathMark(model);
}
else {
return parseNonPathMark(model);
}
}
exports.parseMarkGroup = parseMarkGroup;
var FACETED_PATH_PREFIX = 'faceted_path_';
function parsePathMark(model) {
var mark = model.mark();
// FIXME: replace this with more general case for composition
var details = detailFields(model);
var clip = model.markDef.clip !== undefined ? !!model.markDef.clip : scaleClip(model);
var style = common_1.getStyles(model.markDef);
var sort = getPathSort(model);
var pathMarks = [
tslib_1.__assign({ name: model.getName('marks'), type: markCompiler[mark].vgMark }, (clip ? { clip: true } : {}), (style ? { style: style } : {}), (sort ? { sort: sort } : {}), {
// If has subfacet for line/area group, need to use faceted data from below.
// FIXME: support sorting path order (in connected scatterplot)
from: { data: (details.length > 0 ? FACETED_PATH_PREFIX : '') + model.requestDataName(data_1.MAIN) }, encode: { update: markCompiler[mark].encodeEntry(model) } })
];
if (details.length > 0) {
// TODO: for non-stacked plot, map order to zindex. (Maybe rename order for layer to zindex?)
return [{
name: model.getName('pathgroup'),
type: 'group',
from: {
facet: {
name: FACETED_PATH_PREFIX + model.requestDataName(data_1.MAIN),
data: model.requestDataName(data_1.MAIN),
groupby: details,
}
},
encode: {
update: {
width: { field: { group: 'width' } },
height: { field: { group: 'height' } }
}
},
marks: pathMarks
}];
}
else {
return pathMarks;
}
}
function getPathSort(model) {
if (model.mark() === 'line' && model.channelHasField('order')) {
// For only line, sort by the order field if it is specified.
return common_1.sortParams(model.encoding.order, { expr: 'datum' });
}
else {
// For both line and area, we sort values based on dimension by default
var dimensionChannel = model.markDef.orient === 'horizontal' ? 'y' : 'x';
var s = model.sort(dimensionChannel);
var sortField = sort_1.isSortField(s) ?
fielddef_1.field({
// FIXME: this op might not already exist?
// FIXME: what if dimensionChannel (x or y) contains custom domain?
aggregate: encoding_1.isAggregate(model.encoding) ? s.op : undefined,
field: s.field
}, { expr: 'datum' }) :
model.field(dimensionChannel, {
// For stack with imputation, we only have bin_mid
binSuffix: model.stack && model.stack.impute ? 'mid' : 'start',
expr: 'datum'
});
return {
field: sortField,
order: 'descending'
};
}
}
exports.getPathSort = getPathSort;
function parseNonPathMark(model) {
var mark = model.mark();
var style = common_1.getStyles(model.markDef);
var clip = model.markDef.clip !== undefined ? !!model.markDef.clip : scaleClip(model);
var marks = []; // TODO: vgMarks
// TODO: for non-stacked plot, map order to zindex. (Maybe rename order for layer to zindex?)
marks.push(tslib_1.__assign({ name: model.getName('marks'), type: markCompiler[mark].vgMark }, (clip ? { clip: true } : {}), (style ? { style: style } : {}), { from: { data: model.requestDataName(data_1.MAIN) }, encode: { update: markCompiler[mark].encodeEntry(model) } }));
return marks;
}
/**
* Returns list of detail (group-by) fields
* that the model's spec contains.
*/
function detailFields(model) {
return channel_1.LEVEL_OF_DETAIL_CHANNELS.reduce(function (details, channel) {
var encoding = model.encoding;
if (channel === 'detail' || channel === 'order') {
var channelDef = encoding[channel];
if (channelDef) {
(vega_util_1.isArray(channelDef) ? channelDef : [channelDef]).forEach(function (fieldDef) {
if (!fieldDef.aggregate) {
details.push(fielddef_1.field(fieldDef, { binSuffix: 'start' }));
}
});
}
}
else {
var fieldDef = fielddef_1.getFieldDef(encoding[channel]);
if (fieldDef && !fieldDef.aggregate) {
details.push(fielddef_1.field(fieldDef, { binSuffix: 'start' }));
}
}
return details;
}, []);
}
/**
* If scales are bound to interval selections, we want to automatically clip
* marks to account for panning/zooming interactions. We identify bound scales
* by the domainRaw property, which gets added during scale parsing.
*/
function scaleClip(model) {
var xScale = model.getScaleComponent('x');
var yScale = model.getScaleComponent('y');
return (xScale && xScale.get('domainRaw')) ||
(yScale && yScale.get('domainRaw')) ? true : false;
}
},{"../../channel":8,"../../data":85,"../../encoding":87,"../../fielddef":89,"../../mark":96,"../../sort":99,"../../util":107,"../common":16,"../model":56,"./area":44,"./bar":45,"./init":46,"./line":47,"./point":50,"./rect":51,"./rule":52,"./text":53,"./tick":54,"tslib":114,"vega-util":116}],49:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var fielddef_1 = require("../../fielddef");
var log = require("../../log");
var util = require("../../util");
var common_1 = require("../common");
var selection_1 = require("../selection/selection");
var ref = require("./valueref");
function color(model) {
var config = model.config;
var filled = model.markDef.filled;
var e = nonPosition('color', model, {
vgChannel: filled ? 'fill' : 'stroke',
defaultValue: common_1.getMarkConfig('color', model.markDef, config)
});
// If there is no fill, always fill symbols
// with transparent fills https://github.com/vega/vega-lite/issues/1316
if (!e.fill && util.contains(['bar', 'point', 'circle', 'square'], model.mark())) {
e.fill = { value: 'transparent' };
}
return e;
}
exports.color = color;
function markDefProperties(mark, props) {
return props.reduce(function (m, prop) {
if (mark[prop]) {
m[prop] = { value: mark[prop] };
}
return m;
}, {});
}
exports.markDefProperties = markDefProperties;
function valueIfDefined(prop, value) {
if (value !== undefined) {
return _a = {}, _a[prop] = { value: value }, _a;
}
return undefined;
var _a;
}
exports.valueIfDefined = valueIfDefined;
/**
* Return mixins for non-positional channels with scales. (Text doesn't have scale.)
*/
function nonPosition(channel, model, opt) {
// TODO: refactor how we refer to scale as discussed in https://github.com/vega/vega-lite/pull/1613
if (opt === void 0) { opt = {}; }
var defaultValue = opt.defaultValue, vgChannel = opt.vgChannel;
var defaultRef = opt.defaultRef || (defaultValue !== undefined ? { value: defaultValue } : undefined);
var channelDef = model.encoding[channel];
return wrapCondition(model, channelDef, vgChannel || channel, function (cDef) {
return ref.midPoint(channel, cDef, model.scaleName(channel), model.getScaleComponent(channel), null, // No need to provide stack for non-position as it does not affect mid point
defaultRef);
});
}
exports.nonPosition = nonPosition;
/**
* Return a mixin that include a Vega production rule for a Vega-Lite conditional channel definition.
* or a simple mixin if channel def has no condition.
*/
function wrapCondition(model, channelDef, vgChannel, refFn) {
var condition = channelDef && channelDef.condition;
var valueRef = refFn(channelDef);
if (condition) {
var conditionValueRef = refFn(condition);
return _a = {},
_a[vgChannel] = [
tslib_1.__assign({ test: selection_1.predicate(model, condition.selection) }, conditionValueRef)
].concat((valueRef !== undefined ? [valueRef] : [])),
_a;
}
else {
return valueRef !== undefined ? (_b = {}, _b[vgChannel] = valueRef, _b) : {};
}
var _a, _b;
}
function text(model, channel) {
if (channel === void 0) { channel = 'text'; }
var channelDef = model.encoding[channel];
return wrapCondition(model, channelDef, channel, function (cDef) { return ref.text(cDef, model.config); });
}
exports.text = text;
function bandPosition(fieldDef, channel, model) {
var scaleName = model.scaleName(channel);
var sizeChannel = channel === 'x' ? 'width' : 'height';
if (model.encoding.size) {
var orient = model.markDef.orient;
if (orient) {
var centeredBandPositionMixins = (_a = {},
// Use xc/yc and place the mark at the middle of the band
// This way we never have to deal with size's condition for x/y position.
_a[channel + 'c'] = ref.fieldRef(fieldDef, scaleName, {}, { band: 0.5 }),
_a);
if (fielddef_1.getFieldDef(model.encoding.size)) {
log.warn(log.message.cannotUseSizeFieldWithBandSize(channel));
// TODO: apply size to band and set scale range to some values between 0-1.
// return {
// ...centeredBandPositionMixins,
// ...bandSize('size', model, {vgChannel: sizeChannel})
// };
}
else if (fielddef_1.isValueDef(model.encoding.size)) {
return tslib_1.__assign({}, centeredBandPositionMixins, nonPosition('size', model, { vgChannel: sizeChannel }));
}
}
else {
log.warn(log.message.cannotApplySizeToNonOrientedMark(model.markDef.type));
}
}
return _b = {},
_b[channel] = ref.fieldRef(fieldDef, scaleName, {}),
_b[sizeChannel] = ref.band(scaleName),
_b;
var _a, _b;
}
exports.bandPosition = bandPosition;
function centeredBandPosition(channel, model, defaultPosRef, defaultSizeRef) {
var centerChannel = channel === 'x' ? 'xc' : 'yc';
var sizeChannel = channel === 'x' ? 'width' : 'height';
return tslib_1.__assign({}, pointPosition(channel, model, defaultPosRef, centerChannel), nonPosition('size', model, { defaultRef: defaultSizeRef, vgChannel: sizeChannel }));
}
exports.centeredBandPosition = centeredBandPosition;
function binnedPosition(fieldDef, channel, scaleName, spacing) {
if (channel === 'x') {
return {
x2: ref.bin(fieldDef, scaleName, 'start', spacing),
x: ref.bin(fieldDef, scaleName, 'end')
};
}
else {
return {
y2: ref.bin(fieldDef, scaleName, 'start'),
y: ref.bin(fieldDef, scaleName, 'end', spacing)
};
}
}
exports.binnedPosition = binnedPosition;
/**
* Return mixins for point (non-band) position channels.
*/
function pointPosition(channel, model, defaultRef, vgChannel) {
// TODO: refactor how refer to scale as discussed in https://github.com/vega/vega-lite/pull/1613
var encoding = model.encoding, stack = model.stack;
var valueRef = ref.stackable(channel, encoding[channel], model.scaleName(channel), model.getScaleComponent(channel), stack, defaultRef);
return _a = {},
_a[vgChannel || channel] = valueRef,
_a;
var _a;
}
exports.pointPosition = pointPosition;
/**
* Return mixins for x2, y2.
* If channel is not specified, return one channel based on orientation.
*/
function pointPosition2(model, defaultRef, channel) {
var encoding = model.encoding, markDef = model.markDef, stack = model.stack;
channel = channel || (markDef.orient === 'horizontal' ? 'x2' : 'y2');
var baseChannel = channel === 'x2' ? 'x' : 'y';
var valueRef = ref.stackable2(channel, encoding[baseChannel], encoding[channel], model.scaleName(baseChannel), model.getScaleComponent(baseChannel), stack, defaultRef);
return _a = {}, _a[channel] = valueRef, _a;
var _a;
}
exports.pointPosition2 = pointPosition2;
},{"../../fielddef":89,"../../log":94,"../../util":107,"../common":16,"../selection/selection":69,"./valueref":55,"tslib":114}],50:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var common_1 = require("../common");
var mixins = require("./mixins");
var ref = require("./valueref");
function encodeEntry(model, fixedShape) {
var config = model.config, width = model.width, height = model.height;
return tslib_1.__assign({}, mixins.pointPosition('x', model, ref.mid(width)), mixins.pointPosition('y', model, ref.mid(height)), mixins.color(model), mixins.text(model, 'tooltip'), mixins.nonPosition('size', model), shapeMixins(model, config, fixedShape), mixins.nonPosition('opacity', model));
}
function shapeMixins(model, config, fixedShape) {
if (fixedShape) {
return { shape: { value: fixedShape } };
}
return mixins.nonPosition('shape', model, { defaultValue: common_1.getMarkConfig('shape', model.markDef, config) });
}
exports.shapeMixins = shapeMixins;
exports.point = {
vgMark: 'symbol',
encodeEntry: function (model) {
return encodeEntry(model);
}
};
exports.circle = {
vgMark: 'symbol',
encodeEntry: function (model) {
return encodeEntry(model, 'circle');
}
};
exports.square = {
vgMark: 'symbol',
encodeEntry: function (model) {
return encodeEntry(model, 'square');
}
};
},{"../common":16,"./mixins":49,"./valueref":55,"tslib":114}],51:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var fielddef_1 = require("../../fielddef");
var log = require("../../log");
var mark_1 = require("../../mark");
var scale_1 = require("../../scale");
var mixins = require("./mixins");
exports.rect = {
vgMark: 'rect',
encodeEntry: function (model) {
return tslib_1.__assign({}, x(model), y(model), mixins.color(model), mixins.text(model, 'tooltip'), mixins.nonPosition('opacity', model));
}
};
function x(model) {
var xDef = model.encoding.x;
var x2Def = model.encoding.x2;
var xScale = model.getScaleComponent(channel_1.X);
var xScaleType = xScale ? xScale.get('type') : undefined;
if (fielddef_1.isFieldDef(xDef) && xDef.bin && !x2Def) {
return mixins.binnedPosition(xDef, 'x', model.scaleName('x'), 0);
}
else if (fielddef_1.isFieldDef(xDef) && xScale && scale_1.hasDiscreteDomain(xScaleType)) {
/* istanbul ignore else */
if (xScaleType === scale_1.ScaleType.BAND) {
return mixins.bandPosition(xDef, 'x', model);
}
else {
// We don't support rect mark with point/ordinal scale
throw new Error(log.message.scaleTypeNotWorkWithMark(mark_1.RECT, xScaleType));
}
}
else {
return tslib_1.__assign({}, mixins.pointPosition('x', model, 'zeroOrMax'), mixins.pointPosition2(model, 'zeroOrMin', 'x2'));
}
}
function y(model) {
var yDef = model.encoding.y;
var y2Def = model.encoding.y2;
var yScale = model.getScaleComponent(channel_1.Y);
var yScaleType = yScale ? yScale.get('type') : undefined;
if (fielddef_1.isFieldDef(yDef) && yDef.bin && !y2Def) {
return mixins.binnedPosition(yDef, 'y', model.scaleName('y'), 0);
}
else if (fielddef_1.isFieldDef(yDef) && yScale && scale_1.hasDiscreteDomain(yScaleType)) {
/* istanbul ignore else */
if (yScaleType === scale_1.ScaleType.BAND) {
return mixins.bandPosition(yDef, 'y', model);
}
else {
// We don't support rect mark with point/ordinal scale
throw new Error(log.message.scaleTypeNotWorkWithMark(mark_1.RECT, yScaleType));
}
}
else {
return tslib_1.__assign({}, mixins.pointPosition('y', model, 'zeroOrMax'), mixins.pointPosition2(model, 'zeroOrMin', 'y2'));
}
}
},{"../../channel":8,"../../fielddef":89,"../../log":94,"../../mark":96,"../../scale":97,"./mixins":49,"tslib":114}],52:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var mixins = require("./mixins");
var ref = require("./valueref");
exports.rule = {
vgMark: 'rule',
encodeEntry: function (model) {
var config = model.config, markDef = model.markDef, width = model.width, height = model.height;
var orient = markDef.orient;
return tslib_1.__assign({}, mixins.pointPosition('x', model, orient === 'horizontal' ? 'zeroOrMin' : ref.mid(width)), mixins.pointPosition('y', model, orient === 'vertical' ? 'zeroOrMin' : ref.mid(height)), mixins.pointPosition2(model, 'zeroOrMax'), mixins.color(model), mixins.text(model, 'tooltip'), mixins.nonPosition('opacity', model), mixins.nonPosition('size', model, {
vgChannel: 'strokeWidth' // VL's rule size is strokeWidth
}));
}
};
},{"./mixins":49,"./valueref":55,"tslib":114}],53:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var encoding_1 = require("../../encoding");
var fielddef_1 = require("../../fielddef");
var type_1 = require("../../type");
var common_1 = require("../common");
var mixins = require("./mixins");
var ref = require("./valueref");
exports.text = {
vgMark: 'text',
encodeEntry: function (model) {
var config = model.config, encoding = model.encoding, height = model.height;
var textDef = encoding.text;
return tslib_1.__assign({}, mixins.pointPosition('x', model, xDefault(config, textDef)), mixins.pointPosition('y', model, ref.mid(height)), mixins.text(model), mixins.color(model), mixins.text(model, 'tooltip'), mixins.nonPosition('opacity', model), mixins.nonPosition('size', model, {
vgChannel: 'fontSize' // VL's text size is fontSize
}), mixins.valueIfDefined('align', align(model.markDef, encoding, config)));
}
};
function xDefault(config, textDef) {
if (fielddef_1.isFieldDef(textDef) && textDef.type === type_1.QUANTITATIVE) {
return { field: { group: 'width' }, offset: -5 };
}
// TODO: allow this to fit (Be consistent with ref.midX())
return { value: config.scale.textXRangeStep / 2 };
}
function align(markDef, encoding, config) {
var alignConfig = common_1.getMarkConfig('align', markDef, config);
if (alignConfig === undefined) {
return encoding_1.channelHasField(encoding, channel_1.X) ? 'center' : 'right';
}
// If there is a config, Vega-parser will process this already.
return undefined;
}
},{"../../channel":8,"../../encoding":87,"../../fielddef":89,"../../type":106,"../common":16,"./mixins":49,"./valueref":55,"tslib":114}],54:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_schema_1 = require("../../vega.schema");
var mixins = require("./mixins");
var ref = require("./valueref");
exports.tick = {
vgMark: 'rect',
encodeEntry: function (model) {
var config = model.config, markDef = model.markDef, width = model.width, height = model.height;
var orient = markDef.orient;
var vgSizeChannel = orient === 'horizontal' ? 'width' : 'height';
var vgThicknessChannel = orient === 'horizontal' ? 'height' : 'width';
return tslib_1.__assign({}, mixins.pointPosition('x', model, ref.mid(width), 'xc'), mixins.pointPosition('y', model, ref.mid(height), 'yc'), mixins.nonPosition('size', model, {
defaultValue: defaultSize(model),
vgChannel: vgSizeChannel
}), (_a = {}, _a[vgThicknessChannel] = { value: config.tick.thickness }, _a), mixins.color(model), mixins.nonPosition('opacity', model));
var _a;
}
};
function defaultSize(model) {
var config = model.config;
var orient = model.markDef.orient;
var scale = model.getScaleComponent(orient === 'horizontal' ? 'x' : 'y');
if (config.tick.bandSize !== undefined) {
return config.tick.bandSize;
}
else {
var scaleRange = scale ? scale.get('range') : undefined;
var rangeStep = scaleRange && vega_schema_1.isVgRangeStep(scaleRange) ?
scaleRange.step :
config.scale.rangeStep;
if (typeof rangeStep !== 'number') {
// FIXME consolidate this log
throw new Error('Function does not handle non-numeric rangeStep');
}
return rangeStep / 1.5;
}
}
},{"../../vega.schema":109,"./mixins":49,"./valueref":55,"tslib":114}],55:[function(require,module,exports){
"use strict";
/**
* Utility files for producing Vega ValueRef for marks
*/
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var fielddef_1 = require("../../fielddef");
var scale_1 = require("../../scale");
var util_1 = require("../../util");
var common_1 = require("../common");
// TODO: we need to find a way to refactor these so that scaleName is a part of scale
// but that's complicated. For now, this is a huge step moving forward.
/**
* @return Vega ValueRef for stackable x or y
*/
function stackable(channel, channelDef, scaleName, scale, stack, defaultRef) {
if (fielddef_1.isFieldDef(channelDef) && stack && channel === stack.fieldChannel) {
// x or y use stack_end so that stacked line's point mark use stack_end too.
return fieldRef(channelDef, scaleName, { suffix: 'end' });
}
return midPoint(channel, channelDef, scaleName, scale, stack, defaultRef);
}
exports.stackable = stackable;
/**
* @return Vega ValueRef for stackable x2 or y2
*/
function stackable2(channel, aFieldDef, a2fieldDef, scaleName, scale, stack, defaultRef) {
if (fielddef_1.isFieldDef(aFieldDef) && stack &&
// If fieldChannel is X and channel is X2 (or Y and Y2)
channel.charAt(0) === stack.fieldChannel.charAt(0)) {
return fieldRef(aFieldDef, scaleName, { suffix: 'start' });
}
return midPoint(channel, a2fieldDef, scaleName, scale, stack, defaultRef);
}
exports.stackable2 = stackable2;
/**
* Value Ref for binned fields
*/
function bin(fieldDef, scaleName, side, offset) {
return fieldRef(fieldDef, scaleName, { binSuffix: side }, offset ? { offset: offset } : {});
}
exports.bin = bin;
function fieldRef(fieldDef, scaleName, opt, mixins) {
var ref = {
scale: scaleName,
field: fielddef_1.field(fieldDef, opt),
};
if (mixins) {
return tslib_1.__assign({}, ref, mixins);
}
return ref;
}
exports.fieldRef = fieldRef;
function band(scaleName, band) {
if (band === void 0) { band = true; }
return {
scale: scaleName,
band: band
};
}
exports.band = band;
/**
* Signal that returns the middle of a bin. Should only be used with x and y.
*/
function binMidSignal(fieldDef, scaleName) {
return {
signal: "(" +
("scale(\"" + scaleName + "\", " + fielddef_1.field(fieldDef, { binSuffix: 'start', expr: 'datum' }) + ")") +
" + " +
("scale(\"" + scaleName + "\", " + fielddef_1.field(fieldDef, { binSuffix: 'end', expr: 'datum' }) + ")") +
")/2"
};
}
/**
* @returns {VgValueRef} Value Ref for xc / yc or mid point for other channels.
*/
function midPoint(channel, channelDef, scaleName, scale, stack, defaultRef) {
// TODO: datum support
if (channelDef) {
/* istanbul ignore else */
if (fielddef_1.isFieldDef(channelDef)) {
if (channelDef.bin) {
// Use middle only for x an y to place marks in the center between start and end of the bin range.
// We do not use the mid point for other channels (e.g. size) so that properties of legends and marks match.
if (util_1.contains(['x', 'y'], channel)) {
if (stack && stack.impute) {
// For stack, we computed bin_mid so we can impute.
return fieldRef(channelDef, scaleName, { binSuffix: 'mid' });
}
// For non-stack, we can just calculate bin mid on the fly using signal.
return binMidSignal(channelDef, scaleName);
}
return fieldRef(channelDef, scaleName, { binSuffix: 'start' });
}
var scaleType = scale.get('type');
if (scale_1.hasDiscreteDomain(scaleType)) {
if (scaleType === 'band') {
// For band, to get mid point, need to offset by half of the band
return fieldRef(channelDef, scaleName, { binSuffix: 'range' }, { band: 0.5 });
}
return fieldRef(channelDef, scaleName, { binSuffix: 'range' });
}
else {
return fieldRef(channelDef, scaleName, {}); // no need for bin suffix
}
}
else if (fielddef_1.isValueDef(channelDef)) {
return { value: channelDef.value };
}
else {
throw new Error('FieldDef without field or value.'); // FIXME add this to log.message
}
}
if (defaultRef === 'zeroOrMin') {
/* istanbul ignore else */
if (channel === channel_1.X || channel === channel_1.X2) {
return zeroOrMinX(scaleName, scale);
}
else if (channel === channel_1.Y || channel === channel_1.Y2) {
return zeroOrMinY(scaleName, scale);
}
else {
throw new Error("Unsupported channel " + channel + " for base function"); // FIXME add this to log.message
}
}
else if (defaultRef === 'zeroOrMax') {
/* istanbul ignore else */
if (channel === channel_1.X || channel === channel_1.X2) {
return zeroOrMaxX(scaleName, scale);
}
else if (channel === channel_1.Y || channel === channel_1.Y2) {
return zeroOrMaxY(scaleName, scale);
}
else {
throw new Error("Unsupported channel " + channel + " for base function"); // FIXME add this to log.message
}
}
return defaultRef;
}
exports.midPoint = midPoint;
function text(textDef, config) {
// text
if (textDef) {
if (fielddef_1.isFieldDef(textDef)) {
return common_1.formatSignalRef(textDef, textDef.format, 'datum', config);
}
else if (fielddef_1.isValueDef(textDef)) {
return { value: textDef.value };
}
}
return undefined;
}
exports.text = text;
function mid(sizeRef) {
return tslib_1.__assign({}, sizeRef, { mult: 0.5 });
}
exports.mid = mid;
function zeroOrMinX(scaleName, scale) {
if (scaleName) {
// Log / Time / UTC scale do not support zero
if (!util_1.contains([scale_1.ScaleType.LOG, scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scale.get('type')) &&
scale.get('zero') !== false) {
return {
scale: scaleName,
value: 0
};
}
}
// Put the mark on the x-axis
return { value: 0 };
}
/**
* @returns {VgValueRef} base value if scale exists and return max value if scale does not exist
*/
function zeroOrMaxX(scaleName, scale) {
if (scaleName) {
// Log / Time / UTC scale do not support zero
if (!util_1.contains([scale_1.ScaleType.LOG, scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scale.get('type')) &&
scale.get('zero') !== false) {
return {
scale: scaleName,
value: 0
};
}
}
return { field: { group: 'width' } };
}
function zeroOrMinY(scaleName, scale) {
if (scaleName) {
// Log / Time / UTC scale do not support zero
if (!util_1.contains([scale_1.ScaleType.LOG, scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scale.get('type')) &&
scale.get('zero') !== false) {
return {
scale: scaleName,
value: 0
};
}
}
// Put the mark on the y-axis
return { field: { group: 'height' } };
}
/**
* @returns {VgValueRef} base value if scale exists and return max value if scale does not exist
*/
function zeroOrMaxY(scaleName, scale) {
if (scaleName) {
// Log / Time / UTC scale do not support zero
if (!util_1.contains([scale_1.ScaleType.LOG, scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scale.get('type')) &&
scale.get('zero') !== false) {
return {
scale: scaleName,
value: 0
};
}
}
// Put the mark on the y-axis
return { value: 0 };
}
},{"../../channel":8,"../../fielddef":89,"../../scale":97,"../../util":107,"../common":16,"tslib":114}],56:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../channel");
var encoding_1 = require("../encoding");
var fielddef_1 = require("../fielddef");
var log = require("../log");
var scale_1 = require("../scale");
var title_1 = require("../title");
var util_1 = require("../util");
var vega_schema_1 = require("../vega.schema");
var assemble_1 = require("./axis/assemble");
var header_1 = require("./layout/header");
var assemble_2 = require("./layoutsize/assemble");
var assemble_3 = require("./legend/assemble");
var parse_1 = require("./legend/parse");
var mark_1 = require("./mark/mark");
var domain_1 = require("./scale/domain");
var parse_2 = require("./scale/parse");
var split_1 = require("./split");
var NameMap = (function () {
function NameMap() {
this.nameMap = {};
}
NameMap.prototype.rename = function (oldName, newName) {
this.nameMap[oldName] = newName;
};
NameMap.prototype.has = function (name) {
return this.nameMap[name] !== undefined;
};
NameMap.prototype.get = function (name) {
// If the name appears in the _nameMap, we need to read its new name.
// We have to loop over the dict just in case the new name also gets renamed.
while (this.nameMap[name] && name !== this.nameMap[name]) {
name = this.nameMap[name];
}
return name;
};
return NameMap;
}());
exports.NameMap = NameMap;
/*
We use type guards instead of `instanceof` as `instanceof` makes
different parts of the compiler depend on the actual implementation of
the model classes, which in turn depend on different parts of the compiler.
Thus, `instanceof` leads to circular dependency problems.
On the other hand, type guards only make different parts of the compiler
depend on the type of the model classes, but not the actual implementation.
*/
function isUnitModel(model) {
return model && model.type === 'unit';
}
exports.isUnitModel = isUnitModel;
function isFacetModel(model) {
return model && model.type === 'facet';
}
exports.isFacetModel = isFacetModel;
function isRepeatModel(model) {
return model && model.type === 'repeat';
}
exports.isRepeatModel = isRepeatModel;
function isConcatModel(model) {
return model && model.type === 'concat';
}
exports.isConcatModel = isConcatModel;
function isLayerModel(model) {
return model && model.type === 'layer';
}
exports.isLayerModel = isLayerModel;
var Model = (function () {
function Model(spec, parent, parentGivenName, config, resolve) {
var _this = this;
this.children = [];
/**
* Corrects the data references in marks after assemble.
*/
this.correctDataNames = function (mark) {
// TODO: make this correct
// for normal data references
if (mark.from && mark.from.data) {
mark.from.data = _this.lookupDataSource(mark.from.data);
}
// for access to facet data
if (mark.from && mark.from.facet && mark.from.facet.data) {
mark.from.facet.data = _this.lookupDataSource(mark.from.facet.data);
}
return mark;
};
this.parent = parent;
this.config = config;
// If name is not provided, always use parent's givenName to avoid name conflicts.
this.name = spec.name || parentGivenName;
this.title = spec.title;
// Shared name maps
this.scaleNameMap = parent ? parent.scaleNameMap : new NameMap();
this.layoutSizeNameMap = parent ? parent.layoutSizeNameMap : new NameMap();
this.data = spec.data;
this.description = spec.description;
this.transforms = spec.transform || [];
this.component = {
data: {
sources: parent ? parent.component.data.sources : {},
outputNodes: parent ? parent.component.data.outputNodes : {},
outputNodeRefCounts: parent ? parent.component.data.outputNodeRefCounts : {},
ancestorParse: parent ? tslib_1.__assign({}, parent.component.data.ancestorParse) : {}
},
layoutSize: new split_1.Split(),
layoutHeaders: { row: {}, column: {} },
mark: null,
resolve: resolve || {},
selection: null,
scales: null,
axes: {},
legends: {},
};
}
Object.defineProperty(Model.prototype, "width", {
get: function () {
return this.getSizeSignalRef('width');
},
enumerable: true,
configurable: true
});
Object.defineProperty(Model.prototype, "height", {
get: function () {
return this.getSizeSignalRef('height');
},
enumerable: true,
configurable: true
});
Model.prototype.initSize = function (size) {
var width = size.width, height = size.height;
if (width) {
this.component.layoutSize.set('width', width, true);
}
if (height) {
this.component.layoutSize.set('height', height, true);
}
};
Model.prototype.parse = function () {
this.parseScale();
this.parseMarkDef();
this.parseLayoutSize(); // depends on scale
this.renameTopLevelLayoutSize();
this.parseSelection();
this.parseData(); // (pathorder) depends on markDef; selection filters depend on parsed selections.
this.parseAxisAndHeader(); // depends on scale and layout size
this.parseLegend(); // depends on scale, markDef
this.parseMarkGroup(); // depends on data name, scale, layout size, axisGroup, and children's scale, axis, legend and mark.
};
Model.prototype.parseScale = function () {
parse_2.parseScale(this);
};
/**
* Rename top-level spec's size to be just width / height, ignoring model name.
* This essentially merges the top-level spec's width/height signals with the width/height signals
* to help us reduce redundant signals declaration.
*/
Model.prototype.renameTopLevelLayoutSize = function () {
if (this.getName('width') !== 'width') {
this.renameLayoutSize(this.getName('width'), 'width');
}
if (this.getName('height') !== 'height') {
this.renameLayoutSize(this.getName('height'), 'height');
}
};
Model.prototype.parseMarkDef = function () {
mark_1.parseMarkDef(this);
};
Model.prototype.parseLegend = function () {
parse_1.parseLegend(this);
};
Model.prototype.assembleGroupStyle = function () {
if (this.type === 'unit' || this.type === 'layer') {
return 'cell';
}
return undefined;
};
Model.prototype.assembleLayoutSize = function () {
if (this.type === 'unit' || this.type === 'layer') {
return {
width: this.getSizeSignalRef('width'),
height: this.getSizeSignalRef('height')
};
}
return undefined;
};
Model.prototype.assembleHeaderMarks = function () {
var layoutHeaders = this.component.layoutHeaders;
var headerMarks = [];
for (var _i = 0, HEADER_CHANNELS_1 = header_1.HEADER_CHANNELS; _i < HEADER_CHANNELS_1.length; _i++) {
var channel = HEADER_CHANNELS_1[_i];
if (layoutHeaders[channel].title) {
headerMarks.push(header_1.getTitleGroup(this, channel));
}
}
for (var _a = 0, HEADER_CHANNELS_2 = header_1.HEADER_CHANNELS; _a < HEADER_CHANNELS_2.length; _a++) {
var channel = HEADER_CHANNELS_2[_a];
var layoutHeader = layoutHeaders[channel];
for (var _b = 0, HEADER_TYPES_1 = header_1.HEADER_TYPES; _b < HEADER_TYPES_1.length; _b++) {
var headerType = HEADER_TYPES_1[_b];
if (layoutHeader[headerType]) {
for (var _c = 0, _d = layoutHeader[headerType]; _c < _d.length; _c++) {
var header = _d[_c];
var headerGroup = header_1.getHeaderGroup(this, channel, headerType, layoutHeader, header);
if (headerGroup) {
headerMarks.push(headerGroup);
}
}
}
}
}
return headerMarks;
};
Model.prototype.assembleAxes = function () {
return assemble_1.assembleAxes(this.component.axes);
};
Model.prototype.assembleLegends = function () {
return assemble_3.assembleLegends(this);
};
Model.prototype.assembleTitle = function () {
var title = tslib_1.__assign({}, title_1.extractTitleConfig(this.config.title).nonMark, this.title);
return util_1.keys(title).length > 0 ? title : undefined;
};
/**
* Assemble the mark group for this model. We accept optional `signals` so that we can include concat top-level signals with the top-level model's local signals.
*/
Model.prototype.assembleGroup = function (signals) {
if (signals === void 0) { signals = []; }
var group = {};
signals = signals.concat(this.assembleSelectionSignals());
if (signals.length > 0) {
group.signals = signals;
}
var layout = this.assembleLayout();
if (layout) {
group.layout = layout;
}
group.marks = [].concat(this.assembleHeaderMarks(), this.assembleMarks());
// Only include scales if this spec is top-level or if parent is facet.
// (Otherwise, it will be merged with upper-level's scope.)
var scales = (!this.parent || isFacetModel(this.parent)) ? this.assembleScales() : [];
if (scales.length > 0) {
group.scales = scales;
}
var axes = this.assembleAxes();
if (axes.length > 0) {
group.axes = axes;
}
var legends = this.assembleLegends();
if (legends.length > 0) {
group.legends = legends;
}
return group;
};
Model.prototype.hasDescendantWithFieldOnChannel = function (channel) {
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
if (isUnitModel(child)) {
if (child.channelHasField(channel)) {
return true;
}
}
else {
if (child.hasDescendantWithFieldOnChannel(channel)) {
return true;
}
}
}
return false;
};
Model.prototype.getName = function (text) {
return util_1.varName((this.name ? this.name + '_' : '') + text);
};
/**
* Request a data source name for the given data source type and mark that data source as required. This method should be called in parse, so that all used data source can be correctly instantiated in assembleData().
*/
Model.prototype.requestDataName = function (name) {
var fullName = this.getName(name);
// Increase ref count. This is critical because otherwise we won't create a data source.
// We also increase the ref counts on OutputNode.getSource() calls.
var refCounts = this.component.data.outputNodeRefCounts;
refCounts[fullName] = (refCounts[fullName] || 0) + 1;
return fullName;
};
Model.prototype.getSizeSignalRef = function (sizeType) {
if (isFacetModel(this.parent)) {
var channel = sizeType === 'width' ? 'x' : 'y';
var scaleComponent = this.component.scales[channel];
if (scaleComponent && !scaleComponent.merged) {
var type = scaleComponent.get('type');
var range = scaleComponent.get('range');
if (scale_1.hasDiscreteDomain(type) && vega_schema_1.isVgRangeStep(range)) {
var scaleName = scaleComponent.get('name');
var fieldName = domain_1.getFieldFromDomains(scaleComponent.domains);
var fieldRef = fielddef_1.field({ aggregate: 'distinct', field: fieldName }, { expr: 'datum' });
return {
signal: assemble_2.sizeExpr(scaleName, scaleComponent, fieldRef)
};
}
}
}
return {
signal: this.layoutSizeNameMap.get(this.getName(sizeType))
};
};
/**
* Lookup the name of the datasource for an output node. You probably want to call this in assemble.
*/
Model.prototype.lookupDataSource = function (name) {
var node = this.component.data.outputNodes[name];
if (!node) {
// Name not found in map so let's just return what we got.
// This can happen if we already have the correct name.
return name;
}
return node.getSource();
};
Model.prototype.getSizeName = function (oldSizeName) {
return this.layoutSizeNameMap.get(oldSizeName);
};
Model.prototype.renameLayoutSize = function (oldName, newName) {
this.layoutSizeNameMap.rename(oldName, newName);
};
Model.prototype.renameScale = function (oldName, newName) {
this.scaleNameMap.rename(oldName, newName);
};
/**
* @return scale name for a given channel after the scale has been parsed and named.
*/
Model.prototype.scaleName = function (originalScaleName, parse) {
if (parse) {
// During the parse phase always return a value
// No need to refer to rename map because a scale can't be renamed
// before it has the original name.
return this.getName(originalScaleName);
}
// If there is a scale for the channel, it should either
// be in the scale component or exist in the name map
if (
// If there is a scale for the channel, there should be a local scale component for it
(channel_1.isChannel(originalScaleName) && channel_1.isScaleChannel(originalScaleName) && this.component.scales[originalScaleName]) ||
// in the scale name map (the the scale get merged by its parent)
this.scaleNameMap.has(this.getName(originalScaleName))) {
return this.scaleNameMap.get(this.getName(originalScaleName));
}
return undefined;
};
/**
* Traverse a model's hierarchy to get the scale component for a particular channel.
*/
Model.prototype.getScaleComponent = function (channel) {
/* istanbul ignore next: This is warning for debugging test */
if (!this.component.scales) {
throw new Error('getScaleComponent cannot be called before parseScale(). Make sure you have called parseScale or use parseUnitModelWithScale().');
}
var localScaleComponent = this.component.scales[channel];
if (localScaleComponent && !localScaleComponent.merged) {
return localScaleComponent;
}
return (this.parent ? this.parent.getScaleComponent(channel) : undefined);
};
/**
* Traverse a model's hierarchy to get a particular selection component.
*/
Model.prototype.getSelectionComponent = function (varName, origName) {
var sel = this.component.selection[varName];
if (!sel && this.parent) {
sel = this.parent.getSelectionComponent(varName, origName);
}
if (!sel) {
throw new Error(log.message.selectionNotFound(origName));
}
return sel;
};
return Model;
}());
exports.Model = Model;
/** Abstract class for UnitModel and FacetModel. Both of which can contain fieldDefs as a part of its own specification. */
var ModelWithField = (function (_super) {
tslib_1.__extends(ModelWithField, _super);
function ModelWithField() {
return _super !== null && _super.apply(this, arguments) || this;
}
/** Get "field" reference for vega */
ModelWithField.prototype.field = function (channel, opt) {
if (opt === void 0) { opt = {}; }
var fieldDef = this.fieldDef(channel);
if (fieldDef.bin) {
opt = util_1.extend({
binSuffix: this.hasDiscreteDomain(channel) ? 'range' : 'start'
}, opt);
}
return fielddef_1.field(fieldDef, opt);
};
ModelWithField.prototype.reduceFieldDef = function (f, init, t) {
return encoding_1.reduce(this.getMapping(), function (acc, cd, c) {
var fieldDef = fielddef_1.getFieldDef(cd);
if (fieldDef) {
return f(acc, fieldDef, c);
}
return acc;
}, init, t);
};
ModelWithField.prototype.forEachFieldDef = function (f, t) {
encoding_1.forEach(this.getMapping(), function (cd, c) {
var fieldDef = fielddef_1.getFieldDef(cd);
if (fieldDef) {
f(fieldDef, c);
}
}, t);
};
return ModelWithField;
}(Model));
exports.ModelWithField = ModelWithField;
},{"../channel":8,"../encoding":87,"../fielddef":89,"../log":94,"../scale":97,"../title":103,"../util":107,"../vega.schema":109,"./axis/assemble":9,"./layout/header":36,"./layoutsize/assemble":37,"./legend/assemble":39,"./legend/parse":42,"./mark/mark":48,"./scale/domain":62,"./scale/parse":63,"./split":79,"tslib":114}],57:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var baseconcat_1 = require("./baseconcat");
var buildmodel_1 = require("./buildmodel");
var parse_1 = require("./layoutsize/parse");
var RepeatModel = (function (_super) {
tslib_1.__extends(RepeatModel, _super);
function RepeatModel(spec, parent, parentGivenName, repeatValues, config) {
var _this = _super.call(this, spec, parent, parentGivenName, config, spec.resolve) || this;
_this.type = 'repeat';
_this.repeat = spec.repeat;
_this.children = _this._initChildren(spec, _this.repeat, repeatValues, config);
return _this;
}
RepeatModel.prototype._initChildren = function (spec, repeat, repeater, config) {
var children = [];
var row = repeat.row || [repeater ? repeater.row : null];
var column = repeat.column || [repeater ? repeater.column : null];
// cross product
for (var _i = 0, row_1 = row; _i < row_1.length; _i++) {
var rowField = row_1[_i];
for (var _a = 0, column_1 = column; _a < column_1.length; _a++) {
var columnField = column_1[_a];
var name_1 = (rowField ? '_' + rowField : '') + (columnField ? '_' + columnField : '');
var childRepeat = {
row: rowField,
column: columnField
};
children.push(buildmodel_1.buildModel(spec.spec, this, this.getName('child' + name_1), undefined, childRepeat, config));
}
}
return children;
};
RepeatModel.prototype.parseLayoutSize = function () {
parse_1.parseRepeatLayoutSize(this);
};
RepeatModel.prototype.assembleLayout = function () {
// TODO: allow customization
return {
padding: { row: 10, column: 10 },
offset: 10,
columns: this.repeat && this.repeat.column ? this.repeat.column.length : 1,
bounds: 'full',
align: 'all'
};
};
return RepeatModel;
}(baseconcat_1.BaseConcatModel));
exports.RepeatModel = RepeatModel;
},{"./baseconcat":14,"./buildmodel":15,"./layoutsize/parse":38,"tslib":114}],58:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_util_1 = require("vega-util");
var fielddef_1 = require("../fielddef");
var log = require("../log");
function replaceRepeaterInFacet(facet, repeater) {
return replaceRepeater(facet, repeater);
}
exports.replaceRepeaterInFacet = replaceRepeaterInFacet;
function replaceRepeaterInEncoding(encoding, repeater) {
return replaceRepeater(encoding, repeater);
}
exports.replaceRepeaterInEncoding = replaceRepeaterInEncoding;
/**
* Replace repeater values in a field def with the concrete field name.
*/
function replaceRepeaterInFieldDef(fieldDef, repeater) {
var field = fieldDef.field;
if (fielddef_1.isRepeatRef(field)) {
if (field.repeat in repeater) {
return tslib_1.__assign({}, fieldDef, { field: repeater[field.repeat] });
}
else {
log.warn(log.message.noSuchRepeatedValue(field.repeat));
return null;
}
}
else {
// field is not a repeat ref so we can just return the field def
return fieldDef;
}
}
function replaceRepeater(mapping, repeater) {
var out = {};
for (var channel in mapping) {
if (mapping.hasOwnProperty(channel)) {
var fieldDef = mapping[channel];
if (vega_util_1.isArray(fieldDef)) {
out[channel] = fieldDef.map(function (fd) { return replaceRepeaterInFieldDef(fd, repeater); })
.filter(function (fd) { return fd !== null; });
}
else {
var fd = replaceRepeaterInFieldDef(fieldDef, repeater);
if (fd !== null) {
out[channel] = fd;
}
}
}
}
return out;
}
},{"../fielddef":89,"../log":94,"tslib":114,"vega-util":116}],59:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("../channel");
var log = require("../log");
var util_1 = require("../util");
var model_1 = require("./model");
function defaultScaleResolve(channel, model) {
if (model_1.isLayerModel(model) || model_1.isFacetModel(model)) {
return 'shared';
}
else if (model_1.isConcatModel(model) || model_1.isRepeatModel(model)) {
return util_1.contains(channel_1.SPATIAL_SCALE_CHANNELS, channel) ? 'independent' : 'shared';
}
/* istanbul ignore next: should never reach here. */
throw new Error('invalid model type for resolve');
}
exports.defaultScaleResolve = defaultScaleResolve;
function parseGuideResolve(resolve, channel) {
var channelResolve = resolve[channel];
var guide = util_1.contains(channel_1.SPATIAL_SCALE_CHANNELS, channel) ? 'axis' : 'legend';
if (channelResolve.scale === 'independent') {
if (channelResolve[guide] === 'shared') {
log.warn(log.message.independentScaleMeansIndependentGuide(channel));
}
return 'independent';
}
return channelResolve[guide] || 'shared';
}
exports.parseGuideResolve = parseGuideResolve;
},{"../channel":8,"../log":94,"../util":107,"./model":56}],60:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_util_1 = require("vega-util");
var util_1 = require("../../util");
var vega_schema_1 = require("../../vega.schema");
var selection_1 = require("../selection/selection");
var domain_1 = require("./domain");
function assembleScaleForModelAndChildren(model) {
return model.children.reduce(function (scales, child) {
return scales.concat(child.assembleScales());
}, assembleScalesForModel(model));
}
exports.assembleScaleForModelAndChildren = assembleScaleForModelAndChildren;
function assembleScalesForModel(model) {
return util_1.keys(model.component.scales).reduce(function (scales, channel) {
var scaleComponent = model.component.scales[channel];
if (scaleComponent.merged) {
// Skipped merged scales
return scales;
}
var scale = scaleComponent.combine();
// need to separate const and non const object destruction
var domainRaw = scale.domainRaw, range = scale.range;
var name = scale.name, type = scale.type, _d = scale.domainRaw, _r = scale.range, otherScaleProps = tslib_1.__rest(scale, ["name", "type", "domainRaw", "range"]);
range = assembleScaleRange(range, name, model, channel);
// As scale parsing occurs before selection parsing, a temporary signal
// is used for domainRaw. Here, we detect if this temporary signal
// is set, and replace it with the correct domainRaw signal.
// For more information, see isRawSelectionDomain in selection.ts.
if (domainRaw && selection_1.isRawSelectionDomain(domainRaw)) {
domainRaw = selection_1.selectionScaleDomain(model, domainRaw);
}
var domains = scaleComponent.domains.map(function (domain) {
// Correct references to data as the original domain's data was determined
// in parseScale, which happens before parseData. Thus the original data
// reference can be incorrect.
if (vega_schema_1.isDataRefDomain(domain)) {
domain.data = model.lookupDataSource(domain.data);
}
return domain;
});
// domains is an array that has to be merged into a single vega domain
var domain = domain_1.mergeDomains(domains);
scales.push(tslib_1.__assign({ name: name,
type: type, domain: domain }, (domainRaw ? { domainRaw: domainRaw } : {}), { range: range }, otherScaleProps));
return scales;
}, []);
}
exports.assembleScalesForModel = assembleScalesForModel;
function assembleScaleRange(scaleRange, scaleName, model, channel) {
// add signals to x/y range
if (channel === 'x' || channel === 'y') {
if (vega_schema_1.isVgRangeStep(scaleRange)) {
// For x/y range step, use a signal created in layout assemble instead of a constant range step.
return {
step: { signal: scaleName + '_step' }
};
}
else if (vega_util_1.isArray(scaleRange) && scaleRange.length === 2) {
var r0 = scaleRange[0];
var r1 = scaleRange[1];
if (r0 === 0 && vega_schema_1.isVgSignalRef(r1)) {
// Replace width signal just in case it is renamed.
return [0, { signal: model.getSizeName(r1.signal) }];
}
else if (vega_schema_1.isVgSignalRef(r0) && r1 === 0) {
// Replace height signal just in case it is renamed.
return [{ signal: model.getSizeName(r0.signal) }, 0];
}
}
}
return scaleRange;
}
exports.assembleScaleRange = assembleScaleRange;
},{"../../util":107,"../../vega.schema":109,"../selection/selection":69,"./domain":62,"tslib":114,"vega-util":116}],61:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var split_1 = require("../split");
var ScaleComponent = (function (_super) {
tslib_1.__extends(ScaleComponent, _super);
function ScaleComponent(name, typeWithExplicit) {
var _this = _super.call(this, {}, // no initial explicit property
{ name: name } // name as initial implicit property
) || this;
_this.merged = false;
_this.domains = [];
_this.setWithExplicit('type', typeWithExplicit);
return _this;
}
return ScaleComponent;
}(split_1.Split));
exports.ScaleComponent = ScaleComponent;
},{"../split":79,"tslib":114}],62:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_util_1 = require("vega-util");
var aggregate_1 = require("../../aggregate");
var bin_1 = require("../../bin");
var channel_1 = require("../../channel");
var data_1 = require("../../data");
var datetime_1 = require("../../datetime");
var log = require("../../log");
var scale_1 = require("../../scale");
var sort_1 = require("../../sort");
var util = require("../../util");
var vega_schema_1 = require("../../vega.schema");
var optimize_1 = require("../data/optimize");
var model_1 = require("../model");
var selection_1 = require("../selection/selection");
function parseScaleDomain(model) {
if (model_1.isUnitModel(model)) {
parseUnitScaleDomain(model);
}
else {
parseNonUnitScaleDomain(model);
}
}
exports.parseScaleDomain = parseScaleDomain;
function parseUnitScaleDomain(model) {
var scales = model.specifiedScales;
var localScaleComponents = model.component.scales;
util.keys(localScaleComponents).forEach(function (channel) {
var specifiedScale = scales[channel];
var specifiedDomain = specifiedScale ? specifiedScale.domain : undefined;
var domains = parseDomainForChannel(model, channel);
var localScaleCmpt = localScaleComponents[channel];
localScaleCmpt.domains = domains;
if (scale_1.isSelectionDomain(specifiedDomain)) {
// As scale parsing occurs before selection parsing, we use a temporary
// signal here and append the scale.domain definition. This is replaced
// with the correct domainRaw signal during scale assembly.
// For more information, see isRawSelectionDomain in selection.ts.
// FIXME: replace this with a special property in the scaleComponent
localScaleCmpt.set('domainRaw', {
signal: selection_1.SELECTION_DOMAIN + JSON.stringify(specifiedDomain)
}, true);
}
});
}
function parseNonUnitScaleDomain(model) {
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
parseScaleDomain(child);
}
var localScaleComponents = model.component.scales;
util.keys(localScaleComponents).forEach(function (channel) {
// FIXME: Arvind -- Please revise logic for merging selectionDomain / domainRaw
var domains;
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
var childComponent = child.component.scales[channel];
if (childComponent) {
if (domains === undefined) {
domains = childComponent.domains;
}
else {
domains = domains.concat(childComponent.domains);
}
}
}
if (model_1.isFacetModel(model)) {
domains.forEach(function (domain) {
// Replace the scale domain with data output from a cloned subtree after the facet.
if (vega_schema_1.isDataRefDomain(domain)) {
// use data from cloned subtree (which is the same as data but with a prefix added once)
domain.data = optimize_1.FACET_SCALE_PREFIX + domain.data.replace(optimize_1.FACET_SCALE_PREFIX, '');
}
});
}
localScaleComponents[channel].domains = domains;
});
}
/**
* Remove unaggregated domain if it is not applicable
* Add unaggregated domain if domain is not specified and config.scale.useUnaggregatedDomain is true.
*/
function normalizeUnaggregatedDomain(domain, fieldDef, scaleType, scaleConfig) {
if (domain === 'unaggregated') {
var _a = canUseUnaggregatedDomain(fieldDef, scaleType), valid = _a.valid, reason = _a.reason;
if (!valid) {
log.warn(reason);
return undefined;
}
}
else if (domain === undefined && scaleConfig.useUnaggregatedDomain) {
// Apply config if domain is not specified.
var valid = canUseUnaggregatedDomain(fieldDef, scaleType).valid;
if (valid) {
return 'unaggregated';
}
}
return domain;
}
function parseDomainForChannel(model, channel) {
var scaleType = model.getScaleComponent(channel).get('type');
var domain = normalizeUnaggregatedDomain(model.scaleDomain(channel), model.fieldDef(channel), scaleType, model.config.scale);
if (domain !== model.scaleDomain(channel)) {
model.specifiedScales[channel] = tslib_1.__assign({}, model.specifiedScales[channel], { domain: domain });
}
// If channel is either X or Y then union them with X2 & Y2 if they exist
if (channel === 'x' && model.channelHasField('x2')) {
if (model.channelHasField('x')) {
return parseSingleChannelDomain(scaleType, domain, model, 'x').concat(parseSingleChannelDomain(scaleType, domain, model, 'x2'));
}
else {
return parseSingleChannelDomain(scaleType, domain, model, 'x2');
}
}
else if (channel === 'y' && model.channelHasField('y2')) {
if (model.channelHasField('y')) {
return parseSingleChannelDomain(scaleType, domain, model, 'y').concat(parseSingleChannelDomain(scaleType, domain, model, 'y2'));
}
else {
return parseSingleChannelDomain(scaleType, domain, model, 'y2');
}
}
return parseSingleChannelDomain(scaleType, domain, model, channel);
}
exports.parseDomainForChannel = parseDomainForChannel;
function parseSingleChannelDomain(scaleType, domain, model, channel) {
var fieldDef = model.fieldDef(channel);
if (domain && domain !== 'unaggregated' && !scale_1.isSelectionDomain(domain)) {
if (fieldDef.bin) {
log.warn(log.message.conflictedDomain(channel));
}
else {
if (datetime_1.isDateTime(domain[0])) {
return domain.map(function (dt) {
return { signal: "{data: " + datetime_1.dateTimeExpr(dt, true) + "}" };
});
}
return [domain];
}
}
var stack = model.stack;
if (stack && channel === stack.fieldChannel) {
if (stack.offset === 'normalize') {
return [[0, 1]];
}
var data = model.requestDataName(data_1.MAIN);
return [{
data: data,
field: model.field(channel, { suffix: 'start' })
}, {
data: data,
field: model.field(channel, { suffix: 'end' })
}];
}
var sort = channel_1.isScaleChannel(channel) ? domainSort(model, channel, scaleType) : undefined;
if (domain === 'unaggregated') {
var data = model.requestDataName(data_1.MAIN);
return [{
data: data,
field: model.field(channel, { aggregate: 'min' })
}, {
data: data,
field: model.field(channel, { aggregate: 'max' })
}];
}
else if (fieldDef.bin) {
if (scale_1.isBinScale(scaleType)) {
var signal = model.getName(bin_1.binToString(fieldDef.bin) + "_" + fieldDef.field + "_bins");
return [{ signal: "sequence(" + signal + ".start, " + signal + ".stop + " + signal + ".step, " + signal + ".step)" }];
}
if (scale_1.hasDiscreteDomain(scaleType)) {
// ordinal bin scale takes domain from bin_range, ordered by bin_start
// This is useful for both axis-based scale (x/y) and legend-based scale (other channels).
return [{
data: model.requestDataName(data_1.MAIN),
field: model.field(channel, { binSuffix: 'range' }),
sort: {
field: model.field(channel, { binSuffix: 'start' }),
op: 'min' // min or max doesn't matter since same _range would have the same _start
}
}];
}
else {
if (channel === 'x' || channel === 'y') {
// X/Y position have to include start and end for non-ordinal scale
var data = model.requestDataName(data_1.MAIN);
return [{
data: data,
field: model.field(channel, { binSuffix: 'start' })
}, {
data: data,
field: model.field(channel, { binSuffix: 'end' })
}];
}
else {
// TODO: use bin_mid
return [{
data: model.requestDataName(data_1.MAIN),
field: model.field(channel, { binSuffix: 'start' })
}];
}
}
}
else if (sort) {
return [{
// If sort by aggregation of a specified sort field, we need to use RAW table,
// so we can aggregate values for the scale independently from the main aggregation.
data: util.isBoolean(sort) ? model.requestDataName(data_1.MAIN) : model.requestDataName(data_1.RAW),
field: model.field(channel),
sort: sort
}];
}
else {
return [{
data: model.requestDataName(data_1.MAIN),
field: model.field(channel)
}];
}
}
function domainSort(model, channel, scaleType) {
if (!scale_1.hasDiscreteDomain(scaleType)) {
return undefined;
}
var sort = model.sort(channel);
// Sorted based on an aggregate calculation over a specified sort field (only for ordinal scale)
if (sort_1.isSortField(sort)) {
return sort;
}
if (sort === 'descending') {
return {
op: 'min',
field: model.field(channel),
order: 'descending'
};
}
if (util.contains(['ascending', undefined /* default =ascending*/], sort)) {
return true;
}
// sort === 'none'
return undefined;
}
exports.domainSort = domainSort;
/**
* Determine if a scale can use unaggregated domain.
* @return {Boolean} Returns true if all of the following conditons applies:
* 1. `scale.domain` is `unaggregated`
* 2. Aggregation function is not `count` or `sum`
* 3. The scale is quantitative or time scale.
*/
function canUseUnaggregatedDomain(fieldDef, scaleType) {
if (!fieldDef.aggregate) {
return {
valid: false,
reason: log.message.unaggregateDomainHasNoEffectForRawField(fieldDef)
};
}
if (!aggregate_1.SHARED_DOMAIN_OP_INDEX[fieldDef.aggregate]) {
return {
valid: false,
reason: log.message.unaggregateDomainWithNonSharedDomainOp(fieldDef.aggregate)
};
}
if (fieldDef.type === 'quantitative') {
if (scaleType === 'log') {
return {
valid: false,
reason: log.message.unaggregatedDomainWithLogScale(fieldDef)
};
}
}
return { valid: true };
}
exports.canUseUnaggregatedDomain = canUseUnaggregatedDomain;
/**
* Converts an array of domains to a single Vega scale domain.
*/
function mergeDomains(domains) {
var uniqueDomains = util.unique(domains.map(function (domain) {
// ignore sort property when computing the unique domains
if (vega_schema_1.isDataRefDomain(domain)) {
var _s = domain.sort, domainWithoutSort = tslib_1.__rest(domain, ["sort"]);
return domainWithoutSort;
}
return domain;
}), util.hash);
var sorts = util.unique(domains.map(function (d) {
if (vega_schema_1.isDataRefDomain(d)) {
var s = d.sort;
if (s !== undefined && !util.isBoolean(s)) {
if (s.op === 'count') {
// let's make sure that if op is count, we don't use a field
delete s.field;
}
if (s.order === 'ascending') {
// drop order: ascending as it is the default
delete s.order;
}
}
return s;
}
return undefined;
}).filter(function (s) { return s !== undefined; }), util.hash);
if (uniqueDomains.length === 1) {
var domain = domains[0];
if (vega_schema_1.isDataRefDomain(domain) && sorts.length > 0) {
var sort_2 = sorts[0];
if (sorts.length > 1) {
log.warn(log.message.MORE_THAN_ONE_SORT);
sort_2 = true;
}
return tslib_1.__assign({}, domain, { sort: sort_2 });
}
return domain;
}
// only keep simple sort properties that work with unioned domains
var onlySimpleSorts = sorts.filter(function (s) {
if (util.isBoolean(s)) {
return true;
}
if (s.op === 'count') {
return true;
}
log.warn(log.message.domainSortDropped(s));
return false;
});
var sort = true;
if (onlySimpleSorts.length === 1) {
sort = onlySimpleSorts[0];
}
else if (onlySimpleSorts.length > 1) {
// ignore sort = false if we have another sort property
var filteredSorts = onlySimpleSorts.filter(function (s) { return s !== false; });
if (filteredSorts.length > 1) {
log.warn(log.message.MORE_THAN_ONE_SORT);
sort = true;
}
else {
sort = filteredSorts[0];
}
}
var allData = util.unique(domains.map(function (d) {
if (vega_schema_1.isDataRefDomain(d)) {
return d.data;
}
return null;
}), function (x) { return x; });
if (allData.length === 1 && allData[0] !== null) {
// create a union domain of different fields with a single data source
var domain = {
data: allData[0],
fields: uniqueDomains.map(function (d) { return d.field; }),
sort: sort
};
return domain;
}
return { fields: uniqueDomains, sort: sort };
}
exports.mergeDomains = mergeDomains;
/**
* Return a field if a scale single field.
* Return `undefined` otherwise.
*
*/
function getFieldFromDomains(domains) {
var domain = mergeDomains(domains);
if (vega_schema_1.isDataRefDomain(domain) && vega_util_1.isString(domain.field)) {
return domain.field;
}
return undefined;
}
exports.getFieldFromDomains = getFieldFromDomains;
},{"../../aggregate":5,"../../bin":7,"../../channel":8,"../../data":85,"../../datetime":86,"../../log":94,"../../scale":97,"../../sort":99,"../../util":107,"../../vega.schema":109,"../data/optimize":27,"../model":56,"../selection/selection":69,"tslib":114,"vega-util":116}],63:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("../../channel");
var fielddef_1 = require("../../fielddef");
var scale_1 = require("../../scale");
var util_1 = require("../../util");
var model_1 = require("../model");
var resolve_1 = require("../resolve");
var split_1 = require("../split");
var component_1 = require("./component");
var domain_1 = require("./domain");
var properties_1 = require("./properties");
var range_1 = require("./range");
var type_1 = require("./type");
function parseScale(model) {
parseScaleCore(model);
domain_1.parseScaleDomain(model);
for (var _i = 0, NON_TYPE_DOMAIN_RANGE_VEGA_SCALE_PROPERTIES_1 = scale_1.NON_TYPE_DOMAIN_RANGE_VEGA_SCALE_PROPERTIES; _i < NON_TYPE_DOMAIN_RANGE_VEGA_SCALE_PROPERTIES_1.length; _i++) {
var prop = NON_TYPE_DOMAIN_RANGE_VEGA_SCALE_PROPERTIES_1[_i];
properties_1.parseScaleProperty(model, prop);
}
// range depends on zero
range_1.parseScaleRange(model);
}
exports.parseScale = parseScale;
function parseScaleCore(model) {
if (model_1.isUnitModel(model)) {
model.component.scales = parseUnitScaleCore(model);
}
else {
model.component.scales = parseNonUnitScaleCore(model);
}
}
exports.parseScaleCore = parseScaleCore;
/**
* Parse scales for all channels of a model.
*/
function parseUnitScaleCore(model) {
var encoding = model.encoding, config = model.config;
var mark = model.mark();
return channel_1.SCALE_CHANNELS.reduce(function (scaleComponents, channel) {
var fieldDef;
var specifiedScale = {};
var channelDef = encoding[channel];
if (fielddef_1.isFieldDef(channelDef)) {
fieldDef = channelDef;
specifiedScale = channelDef.scale || {};
}
else if (fielddef_1.isConditionalDef(channelDef) && fielddef_1.isFieldDef(channelDef.condition)) {
fieldDef = channelDef.condition;
specifiedScale = channelDef.condition.scale || {};
}
else if (channel === 'x') {
fieldDef = fielddef_1.getFieldDef(encoding.x2);
}
else if (channel === 'y') {
fieldDef = fielddef_1.getFieldDef(encoding.y2);
}
if (fieldDef) {
var specifiedScaleType = specifiedScale.type;
var sType = type_1.scaleType(specifiedScale.type, channel, fieldDef, mark, specifiedScale.rangeStep, config.scale);
scaleComponents[channel] = new component_1.ScaleComponent(model.scaleName(channel + '', true), { value: sType, explicit: specifiedScaleType === sType });
}
return scaleComponents;
}, {});
}
var scaleTypeTieBreaker = split_1.tieBreakByComparing(function (st1, st2) { return (scale_1.scaleTypePrecedence(st1) - scale_1.scaleTypePrecedence(st2)); });
function parseNonUnitScaleCore(model) {
var scaleComponents = model.component.scales = {};
var scaleTypeWithExplicitIndex = {};
var resolve = model.component.resolve;
var _loop_1 = function (child) {
parseScaleCore(child);
// Instead of always merging right away -- check if it is compatible to merge first!
util_1.keys(child.component.scales).forEach(function (channel) {
// if resolve is undefined, set default first
resolve[channel] = resolve[channel] || {};
resolve[channel].scale = resolve[channel].scale || resolve_1.defaultScaleResolve(channel, model);
if (model.component.resolve[channel].scale === 'shared') {
var scaleType_1 = scaleTypeWithExplicitIndex[channel];
var childScaleType = child.component.scales[channel].getWithExplicit('type');
if (scaleType_1) {
if (scale_1.scaleCompatible(scaleType_1.value, childScaleType.value)) {
// merge scale component if type are compatible
scaleTypeWithExplicitIndex[channel] = split_1.mergeValuesWithExplicit(scaleType_1, childScaleType, 'type', 'scale', scaleTypeTieBreaker);
}
else {
// Otherwise, update conflicting channel to be independent
model.component.resolve[channel].scale = 'independent';
// Remove from the index so they don't get merged
delete scaleTypeWithExplicitIndex[channel];
}
}
else {
scaleTypeWithExplicitIndex[channel] = childScaleType;
}
}
});
};
// Parse each child scale and determine if a particular channel can be merged.
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
_loop_1(child);
}
// Merge each channel listed in the index
util_1.keys(scaleTypeWithExplicitIndex).forEach(function (channel) {
// Create new merged scale component
var name = model.scaleName(channel, true);
var typeWithExplicit = scaleTypeWithExplicitIndex[channel];
var modelScale = scaleComponents[channel] = new component_1.ScaleComponent(name, typeWithExplicit);
// rename each child and mark them as merged
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
var childScale = child.component.scales[channel];
if (childScale) {
child.renameScale(childScale.get('name'), name);
childScale.merged = true;
}
}
});
return scaleComponents;
}
},{"../../channel":8,"../../fielddef":89,"../../scale":97,"../../util":107,"../model":56,"../resolve":59,"../split":79,"./component":61,"./domain":62,"./properties":64,"./range":65,"./type":66}],64:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("../../channel");
var log = require("../../log");
var scale_1 = require("../../scale");
var timeunit_1 = require("../../timeunit");
var util = require("../../util");
var util_1 = require("../../util");
var model_1 = require("../model");
var split_1 = require("../split");
var range_1 = require("./range");
function parseScaleProperty(model, property) {
if (model_1.isUnitModel(model)) {
parseUnitScaleProperty(model, property);
}
else {
parseNonUnitScaleProperty(model, property);
}
}
exports.parseScaleProperty = parseScaleProperty;
function parseUnitScaleProperty(model, property) {
var localScaleComponents = model.component.scales;
util_1.keys(localScaleComponents).forEach(function (channel) {
var specifiedScale = model.specifiedScales[channel];
var localScaleCmpt = localScaleComponents[channel];
var mergedScaleCmpt = model.getScaleComponent(channel);
var fieldDef = model.fieldDef(channel);
var config = model.config;
var specifiedValue = specifiedScale[property];
var sType = mergedScaleCmpt.get('type');
var supportedByScaleType = scale_1.scaleTypeSupportProperty(sType, property);
var channelIncompatability = scale_1.channelScalePropertyIncompatability(channel, property);
if (specifiedValue !== undefined) {
// If there is a specified value, check if it is compatible with scale type and channel
if (!supportedByScaleType) {
log.warn(log.message.scalePropertyNotWorkWithScaleType(sType, property, channel));
}
else if (channelIncompatability) {
log.warn(channelIncompatability);
}
}
if (supportedByScaleType && channelIncompatability === undefined) {
if (specifiedValue !== undefined) {
// copyKeyFromObject ensure type safety
localScaleCmpt.copyKeyFromObject(property, specifiedScale);
}
else {
var value = getDefaultValue(property, specifiedScale, mergedScaleCmpt, channel, fieldDef, config.scale);
if (value !== undefined) {
localScaleCmpt.set(property, value, false);
}
}
}
});
}
function getDefaultValue(property, scale, scaleCmpt, channel, fieldDef, scaleConfig) {
// If we have default rule-base, determine default value first
switch (property) {
case 'nice':
return nice(scaleCmpt.get('type'), channel, fieldDef);
case 'padding':
return padding(channel, scaleCmpt.get('type'), scaleConfig);
case 'paddingInner':
return paddingInner(scaleCmpt.get('padding'), channel, scaleConfig);
case 'paddingOuter':
return paddingOuter(scaleCmpt.get('padding'), channel, scaleCmpt.get('type'), scaleCmpt.get('paddingInner'), scaleConfig);
case 'round':
return round(channel, scaleConfig);
case 'zero':
return zero(channel, fieldDef, !!scale.domain);
}
// Otherwise, use scale config
return scaleConfig[property];
}
function parseNonUnitScaleProperty(model, property) {
var localScaleComponents = model.component.scales;
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
if (property === 'range') {
range_1.parseScaleRange(child);
}
else {
parseScaleProperty(child, property);
}
}
util_1.keys(localScaleComponents).forEach(function (channel) {
var valueWithExplicit;
for (var _i = 0, _a = model.children; _i < _a.length; _i++) {
var child = _a[_i];
var childComponent = child.component.scales[channel];
if (childComponent) {
var childValueWithExplicit = childComponent.getWithExplicit(property);
valueWithExplicit = split_1.mergeValuesWithExplicit(valueWithExplicit, childValueWithExplicit, property, 'scale', split_1.tieBreakByComparing(function (v1, v2) {
switch (property) {
case 'range':
// For range step, prefer larger step
if (v1.step && v2.step) {
return v1.step - v2.step;
}
return 0;
}
return 0;
}));
}
}
localScaleComponents[channel].setWithExplicit(property, valueWithExplicit);
});
}
exports.parseNonUnitScaleProperty = parseNonUnitScaleProperty;
function nice(scaleType, channel, fieldDef) {
if (util.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scaleType)) {
return timeunit_1.smallestUnit(fieldDef.timeUnit);
}
if (fieldDef.bin) {
return undefined;
}
return util.contains([channel_1.X, channel_1.Y], channel); // return true for quantitative X/Y unless binned
}
exports.nice = nice;
function padding(channel, scaleType, scaleConfig) {
if (util.contains([channel_1.X, channel_1.Y], channel)) {
if (scaleType === scale_1.ScaleType.POINT) {
return scaleConfig.pointPadding;
}
}
return undefined;
}
exports.padding = padding;
function paddingInner(padding, channel, scaleConfig) {
if (padding !== undefined) {
// If user has already manually specified "padding", no need to add default paddingInner.
return undefined;
}
if (util.contains([channel_1.X, channel_1.Y], channel)) {
// Padding is only set for X and Y by default.
// Basically it doesn't make sense to add padding for color and size.
// paddingOuter would only be called if it's a band scale, just return the default for bandScale.
return scaleConfig.bandPaddingInner;
}
return undefined;
}
exports.paddingInner = paddingInner;
function paddingOuter(padding, channel, scaleType, paddingInner, scaleConfig) {
if (padding !== undefined) {
// If user has already manually specified "padding", no need to add default paddingOuter.
return undefined;
}
if (util.contains([channel_1.X, channel_1.Y], channel)) {
// Padding is only set for X and Y by default.
// Basically it doesn't make sense to add padding for color and size.
if (scaleType === scale_1.ScaleType.BAND) {
if (scaleConfig.bandPaddingOuter !== undefined) {
return scaleConfig.bandPaddingOuter;
}
/* By default, paddingOuter is paddingInner / 2. The reason is that
size (width/height) = step * (cardinality - paddingInner + 2 * paddingOuter).
and we want the width/height to be integer by default.
Note that step (by default) and cardinality are integers.) */
return paddingInner / 2;
}
}
return undefined;
}
exports.paddingOuter = paddingOuter;
function round(channel, scaleConfig) {
if (util.contains(['x', 'y'], channel)) {
return scaleConfig.round;
}
return undefined;
}
exports.round = round;
function zero(channel, fieldDef, hasCustomDomain) {
// By default, return true only for the following cases:
// 1) using quantitative field with size
// While this can be either ratio or interval fields, our assumption is that
// ratio are more common.
if (channel === 'size' && fieldDef.type === 'quantitative') {
return true;
}
// 2) non-binned, quantitative x-scale or y-scale if no custom domain is provided.
// (For binning, we should not include zero by default because binning are calculated without zero.
// Similar, if users explicitly provide a domain range, we should not augment zero as that will be unexpected.)
if (!hasCustomDomain && !fieldDef.bin && util.contains([channel_1.X, channel_1.Y], channel)) {
return true;
}
return false;
}
exports.zero = zero;
},{"../../channel":8,"../../log":94,"../../scale":97,"../../timeunit":102,"../../util":107,"../model":56,"../split":79,"./range":65}],65:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var vega_util_1 = require("vega-util");
var channel_1 = require("../../channel");
var log = require("../../log");
var scale_1 = require("../../scale");
var util = require("../../util");
var vega_schema_1 = require("../../vega.schema");
var model_1 = require("../model");
var split_1 = require("../split");
var properties_1 = require("./properties");
exports.RANGE_PROPERTIES = ['range', 'rangeStep', 'scheme'];
function parseScaleRange(model) {
if (model_1.isUnitModel(model)) {
parseUnitScaleRange(model);
}
else {
properties_1.parseNonUnitScaleProperty(model, 'range');
}
}
exports.parseScaleRange = parseScaleRange;
function parseUnitScaleRange(model) {
var localScaleComponents = model.component.scales;
// use SCALE_CHANNELS instead of scales[channel] to ensure that x, y come first!
channel_1.SCALE_CHANNELS.forEach(function (channel) {
var localScaleCmpt = localScaleComponents[channel];
if (!localScaleCmpt) {
return;
}
var specifiedScale = model.specifiedScales[channel];
var fieldDef = model.fieldDef(channel);
// Read if there is a specified width/height
var sizeType = channel === 'x' ? 'width' : channel === 'y' ? 'height' : undefined;
var specifiedSize = sizeType ? model.component.layoutSize.get(sizeType) : undefined;
var xyRangeSteps = getXYRangeStep(model);
var rangeWithExplicit = parseRangeForChannel(channel, localScaleCmpt.get('type'), fieldDef.type, specifiedScale, model.config, localScaleCmpt.get('zero'), model.mark(), specifiedSize, model.getName(sizeType), xyRangeSteps);
localScaleCmpt.setWithExplicit('range', rangeWithExplicit);
});
}
function getXYRangeStep(model) {
var xyRangeSteps = [];
var xScale = model.getScaleComponent('x');
var xRange = xScale && xScale.get('range');
if (xRange && vega_schema_1.isVgRangeStep(xRange) && vega_util_1.isNumber(xRange.step)) {
xyRangeSteps.push(xRange.step);
}
var yScale = model.getScaleComponent('y');
var yRange = yScale && yScale.get('range');
if (yRange && vega_schema_1.isVgRangeStep(yRange) && vega_util_1.isNumber(yRange.step)) {
xyRangeSteps.push(yRange.step);
}
return xyRangeSteps;
}
/**
* Return mixins that includes one of the range properties (range, rangeStep, scheme).
*/
function parseRangeForChannel(channel, scaleType, type, specifiedScale, config, zero, mark, specifiedSize, sizeSignal, xyRangeSteps) {
var noRangeStep = !!specifiedSize || specifiedScale.rangeStep === null;
// Check if any of the range properties is specified.
// If so, check if it is compatible and make sure that we only output one of the properties
for (var _i = 0, RANGE_PROPERTIES_1 = exports.RANGE_PROPERTIES; _i < RANGE_PROPERTIES_1.length; _i++) {
var property = RANGE_PROPERTIES_1[_i];
if (specifiedScale[property] !== undefined) {
var supportedByScaleType = scale_1.scaleTypeSupportProperty(scaleType, property);
var channelIncompatability = scale_1.channelScalePropertyIncompatability(channel, property);
if (!supportedByScaleType) {
log.warn(log.message.scalePropertyNotWorkWithScaleType(scaleType, property, channel));
}
else if (channelIncompatability) {
log.warn(channelIncompatability);
}
else {
switch (property) {
case 'range':
return split_1.makeImplicit(specifiedScale[property]);
case 'scheme':
return split_1.makeImplicit(parseScheme(specifiedScale[property]));
case 'rangeStep':
var rangeStep = specifiedScale[property];
if (rangeStep !== null) {
if (specifiedSize === undefined) {
return split_1.makeImplicit({ step: rangeStep });
}
else {
// If top-level size is specified, we ignore specified rangeStep.
log.warn(log.message.rangeStepDropped(channel));
}
}
}
}
}
}
return {
explicit: false,
value: defaultRange(channel, scaleType, type, config, zero, mark, sizeSignal, xyRangeSteps, noRangeStep)
};
}
exports.parseRangeForChannel = parseRangeForChannel;
function parseScheme(scheme) {
if (scale_1.isExtendedScheme(scheme)) {
var r = { scheme: scheme.name };
if (scheme.count) {
r.count = scheme.count;
}
if (scheme.extent) {
r.extent = scheme.extent;
}
return r;
}
return { scheme: scheme };
}
function defaultRange(channel, scaleType, type, config, zero, mark, sizeSignal, xyRangeSteps, noRangeStep) {
switch (channel) {
case channel_1.X:
case channel_1.Y:
if (util.contains(['point', 'band'], scaleType) && !noRangeStep) {
if (channel === channel_1.X && mark === 'text') {
if (config.scale.textXRangeStep) {
return { step: config.scale.textXRangeStep };
}
}
else {
if (config.scale.rangeStep) {
return { step: config.scale.rangeStep };
}
}
}
// If range step is null, assign temporary range signals,
// which will be later replaced with proper signals in assemble.
// We cannot set the right size signal here since parseLayoutSize() happens after parseScale().
return channel === channel_1.X ? [0, { signal: sizeSignal }] : [{ signal: sizeSignal }, 0];
case channel_1.SIZE:
// TODO: support custom rangeMin, rangeMax
var rangeMin = sizeRangeMin(mark, zero, config);
var rangeMax = sizeRangeMax(mark, xyRangeSteps, config);
return [rangeMin, rangeMax];
case channel_1.SHAPE:
return 'symbol';
case channel_1.COLOR:
if (scaleType === 'ordinal') {
// Only nominal data uses ordinal scale by default
return type === 'nominal' ? 'category' : 'ordinal';
}
return mark === 'rect' ? 'heatmap' : 'ramp';
case channel_1.OPACITY:
// TODO: support custom rangeMin, rangeMax
return [config.scale.minOpacity, config.scale.maxOpacity];
}
/* istanbul ignore next: should never reach here */
throw new Error("Scale range undefined for channel " + channel);
}
exports.defaultRange = defaultRange;
function sizeRangeMin(mark, zero, config) {
if (zero) {
return 0;
}
switch (mark) {
case 'bar':
return config.scale.minBandSize !== undefined ? config.scale.minBandSize : config.bar.continuousBandSize;
case 'tick':
return config.scale.minBandSize;
case 'line':
case 'rule':
return config.scale.minStrokeWidth;
case 'text':
return config.scale.minFontSize;
case 'point':
case 'square':
case 'circle':
if (config.scale.minSize) {
return config.scale.minSize;
}
}
/* istanbul ignore next: should never reach here */
// sizeRangeMin not implemented for the mark
throw new Error(log.message.incompatibleChannel('size', mark));
}
function sizeRangeMax(mark, xyRangeSteps, config) {
var scaleConfig = config.scale;
// TODO(#1168): make max size scale based on rangeStep / overall plot size
switch (mark) {
case 'bar':
case 'tick':
if (config.scale.maxBandSize !== undefined) {
return config.scale.maxBandSize;
}
return minXYRangeStep(xyRangeSteps, config.scale) - 1;
case 'line':
case 'rule':
return config.scale.maxStrokeWidth;
case 'text':
return config.scale.maxFontSize;
case 'point':
case 'square':
case 'circle':
if (config.scale.maxSize) {
return config.scale.maxSize;
}
// FIXME this case totally should be refactored
var pointStep = minXYRangeStep(xyRangeSteps, scaleConfig);
return (pointStep - 2) * (pointStep - 2);
}
/* istanbul ignore next: should never reach here */
// sizeRangeMax not implemented for the mark
throw new Error(log.message.incompatibleChannel('size', mark));
}
/**
* @returns {number} Range step of x or y or minimum between the two if both are ordinal scale.
*/
function minXYRangeStep(xyRangeSteps, scaleConfig) {
if (xyRangeSteps.length > 0) {
return Math.min.apply(null, xyRangeSteps);
}
if (scaleConfig.rangeStep) {
return scaleConfig.rangeStep;
}
return 21; // FIXME: re-evaluate the default value here.
}
},{"../../channel":8,"../../log":94,"../../scale":97,"../../util":107,"../../vega.schema":109,"../model":56,"../split":79,"./properties":64,"vega-util":116}],66:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("../../channel");
var log = require("../../log");
var scale_1 = require("../../scale");
var scale_2 = require("../../scale");
var timeunit_1 = require("../../timeunit");
var type_1 = require("../../type");
var util = require("../../util");
var util_1 = require("../../util");
/**
* Determine if there is a specified scale type and if it is appropriate,
* or determine default type if type is unspecified or inappropriate.
*/
// NOTE: CompassQL uses this method.
function scaleType(specifiedType, channel, fieldDef, mark, specifiedRangeStep, scaleConfig) {
var defaultScaleType = defaultType(channel, fieldDef, mark, specifiedRangeStep, scaleConfig);
if (!channel_1.hasScale(channel)) {
// There is no scale for these channels
return null;
}
if (specifiedType !== undefined) {
// Check if explicitly specified scale type is supported by the channel
if (!channel_1.supportScaleType(channel, specifiedType)) {
log.warn(log.message.scaleTypeNotWorkWithChannel(channel, specifiedType, defaultScaleType));
return defaultScaleType;
}
// Check if explicitly specified scale type is supported by the data type
if (!fieldDefMatchScaleType(specifiedType, fieldDef)) {
log.warn(log.message.scaleTypeNotWorkWithFieldDef(specifiedType, defaultScaleType));
return defaultScaleType;
}
return specifiedType;
}
return defaultScaleType;
}
exports.scaleType = scaleType;
/**
* Determine appropriate default scale type.
*/
function defaultType(channel, fieldDef, mark, specifiedRangeStep, scaleConfig) {
switch (fieldDef.type) {
case 'nominal':
if (channel === 'color' || channel_1.rangeType(channel) === 'discrete') {
return 'ordinal';
}
return discreteToContinuousType(channel, mark, specifiedRangeStep, scaleConfig);
case 'ordinal':
if (channel === 'color') {
return 'ordinal';
}
else if (channel_1.rangeType(channel) === 'discrete') {
if (channel !== 'text' && channel !== 'tooltip') {
log.warn(log.message.discreteChannelCannotEncode(channel, 'ordinal'));
}
return 'ordinal';
}
return discreteToContinuousType(channel, mark, specifiedRangeStep, scaleConfig);
case 'temporal':
if (channel === 'color') {
if (timeunit_1.isDiscreteByDefault(fieldDef.timeUnit)) {
// For discrete timeUnit, use ordinal scale so that legend produces correct value.
// (See https://github.com/vega/vega-lite/issues/2045.)
return 'ordinal';
}
return 'sequential';
}
else if (channel_1.rangeType(channel) === 'discrete') {
log.warn(log.message.discreteChannelCannotEncode(channel, 'temporal'));
// TODO: consider using quantize (equivalent to binning) once we have it
return 'ordinal';
}
if (timeunit_1.isDiscreteByDefault(fieldDef.timeUnit)) {
return discreteToContinuousType(channel, mark, specifiedRangeStep, scaleConfig);
}
return 'time';
case 'quantitative':
if (channel === 'color') {
if (fieldDef.bin) {
return 'bin-ordinal';
}
// Use `sequential` as the default color scale for continuous data
// since it supports both array range and scheme range.
return 'sequential';
}
else if (channel_1.rangeType(channel) === 'discrete') {
log.warn(log.message.discreteChannelCannotEncode(channel, 'quantitative'));
// TODO: consider using quantize (equivalent to binning) once we have it
return 'ordinal';
}
// x and y use a linear scale because selections don't work with bin scales
if (fieldDef.bin && channel !== 'x' && channel !== 'y') {
return 'bin-linear';
}
return 'linear';
}
/* istanbul ignore next: should never reach this */
throw new Error(log.message.invalidFieldType(fieldDef.type));
}
/**
* Determines default scale type for nominal/ordinal field.
* @returns BAND or POINT scale based on channel, mark, and rangeStep
*/
function discreteToContinuousType(channel, mark, specifiedRangeStep, scaleConfig) {
if (util.contains(['x', 'y'], channel)) {
if (mark === 'rect') {
// The rect mark should fit into a band.
return 'band';
}
if (mark === 'bar') {
return 'band';
}
}
// Otherwise, use ordinal point scale so we can easily get center positions of the marks.
return 'point';
}
function fieldDefMatchScaleType(specifiedType, fieldDef) {
var type = fieldDef.type;
if (util_1.contains([type_1.Type.ORDINAL, type_1.Type.NOMINAL], type)) {
return specifiedType === undefined || scale_2.hasDiscreteDomain(specifiedType);
}
else if (type === type_1.Type.TEMPORAL) {
if (!fieldDef.timeUnit) {
return util_1.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC, undefined], specifiedType);
}
else {
return util_1.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC, undefined], specifiedType) || scale_2.hasDiscreteDomain(specifiedType);
}
}
else if (type === type_1.Type.QUANTITATIVE) {
if (fieldDef.bin) {
return specifiedType === scale_1.ScaleType.BIN_LINEAR || specifiedType === scale_1.ScaleType.BIN_ORDINAL;
}
return util_1.contains([scale_1.ScaleType.LOG, scale_1.ScaleType.POW, scale_1.ScaleType.SQRT, scale_1.ScaleType.QUANTILE, scale_1.ScaleType.QUANTIZE, scale_1.ScaleType.LINEAR, undefined], specifiedType);
}
return true;
}
exports.fieldDefMatchScaleType = fieldDefMatchScaleType;
},{"../../channel":8,"../../log":94,"../../scale":97,"../../timeunit":102,"../../type":106,"../../util":107}],67:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../../channel");
var log_1 = require("../../log");
var scale_1 = require("../../scale");
var util_1 = require("../../util");
var selection_1 = require("./selection");
var scales_1 = require("./transforms/scales");
exports.BRUSH = '_brush';
exports.SCALE_TRIGGER = '_scale_trigger';
var interval = {
predicate: 'vlInterval',
scaleDomain: 'vlIntervalDomain',
signals: function (model, selCmpt) {
var name = selCmpt.name;
var hasScales = scales_1.default.has(selCmpt);
var signals = [];
var intervals = [];
var tupleTriggers = [];
var scaleTriggers = [];
if (selCmpt.translate && !hasScales) {
var filterExpr_1 = "!event.item || event.item.mark.name !== " + util_1.stringValue(name + exports.BRUSH);
events(selCmpt, function (_, evt) {
var filters = evt.between[0].filter || (evt.between[0].filter = []);
if (filters.indexOf(filterExpr_1) < 0) {
filters.push(filterExpr_1);
}
});
}
selCmpt.project.forEach(function (p) {
var channel = p.channel;
if (channel !== channel_1.X && channel !== channel_1.Y) {
log_1.warn('Interval selections only support x and y encoding channels.');
return;
}
var cs = channelSignals(model, selCmpt, channel);
var dname = selection_1.channelSignalName(selCmpt, channel, 'data');
var vname = selection_1.channelSignalName(selCmpt, channel, 'visual');
var scaleStr = util_1.stringValue(model.scaleName(channel));
var scaleType = model.getScaleComponent(channel).get('type');
var toNum = scale_1.hasContinuousDomain(scaleType) ? '+' : '';
signals.push.apply(signals, cs);
tupleTriggers.push(dname);
intervals.push("{encoding: " + util_1.stringValue(channel) + ", " +
("field: " + util_1.stringValue(p.field) + ", extent: " + dname + "}"));
scaleTriggers.push({
scaleName: model.scaleName(channel),
expr: "(!isArray(" + dname + ") || " +
("(" + toNum + "invert(" + scaleStr + ", " + vname + ")[0] === " + toNum + dname + "[0] && ") +
(toNum + "invert(" + scaleStr + ", " + vname + ")[1] === " + toNum + dname + "[1]))")
});
});
// Proxy scale reactions to ensure that an infinite loop doesn't occur
// when an interval selection filter touches the scale.
if (!hasScales) {
signals.push({
name: name + exports.SCALE_TRIGGER,
update: scaleTriggers.map(function (t) { return t.expr; }).join(' && ') +
(" ? " + (name + exports.SCALE_TRIGGER) + " : {}")
});
}
// Only add an interval to the store if it has valid data extents. Data extents
// are set to null if pixel extents are equal to account for intervals over
// ordinal/nominal domains which, when inverted, will still produce a valid datum.
return signals.concat({
name: name + selection_1.TUPLE,
on: [{
events: tupleTriggers.map(function (t) { return ({ signal: t }); }),
update: tupleTriggers.join(' && ') +
(" ? {unit: " + selection_1.unitName(model) + ", intervals: [" + intervals.join(', ') + "]} : null")
}]
});
},
modifyExpr: function (model, selCmpt) {
var tpl = selCmpt.name + selection_1.TUPLE;
return tpl + ', ' +
(selCmpt.resolve === 'global' ? 'true' : "{unit: " + selection_1.unitName(model) + "}");
},
marks: function (model, selCmpt, marks) {
var name = selCmpt.name;
var _a = selection_1.spatialProjections(selCmpt), xi = _a.xi, yi = _a.yi;
var tpl = name + selection_1.TUPLE;
var store = "data(" + util_1.stringValue(selCmpt.name + selection_1.STORE) + ")";
// Do not add a brush if we're binding to scales.
if (scales_1.default.has(selCmpt)) {
return marks;
}
var update = {
x: xi !== null ? { signal: name + "_x[0]" } : { value: 0 },
y: yi !== null ? { signal: name + "_y[0]" } : { value: 0 },
x2: xi !== null ? { signal: name + "_x[1]" } : { field: { group: 'width' } },
y2: yi !== null ? { signal: name + "_y[1]" } : { field: { group: 'height' } }
};
// If the selection is resolved to global, only a single interval is in
// the store. Wrap brush mark's encodings with a production rule to test
// this based on the `unit` property. Hide the brush mark if it corresponds
// to a unit different from the one in the store.
if (selCmpt.resolve === 'global') {
util_1.keys(update).forEach(function (key) {
update[key] = [tslib_1.__assign({ test: store + ".length && " + store + "[0].unit === " + selection_1.unitName(model) }, update[key]), { value: 0 }];
});
}
// Two brush marks ensure that fill colors and other aesthetic choices do
// not interefere with the core marks, but that the brushed region can still
// be interacted with (e.g., dragging it around).
var _b = selCmpt.mark, fill = _b.fill, fillOpacity = _b.fillOpacity, stroke = tslib_1.__rest(_b, ["fill", "fillOpacity"]);
var vgStroke = util_1.keys(stroke).reduce(function (def, k) {
def[k] = { value: stroke[k] };
return def;
}, {});
return [{
name: name + exports.BRUSH + '_bg',
type: 'rect',
clip: true,
encode: {
enter: {
fill: { value: fill },
fillOpacity: { value: fillOpacity }
},
update: update
}
}].concat(marks, {
name: name + exports.BRUSH,
type: 'rect',
clip: true,
encode: {
enter: tslib_1.__assign({ fill: { value: 'transparent' } }, vgStroke),
update: update
}
});
}
};
exports.default = interval;
/**
* Returns the visual and data signals for an interval selection.
*/
function channelSignals(model, selCmpt, channel) {
var vname = selection_1.channelSignalName(selCmpt, channel, 'visual');
var dname = selection_1.channelSignalName(selCmpt, channel, 'data');
var hasScales = scales_1.default.has(selCmpt);
var scaleName = model.scaleName(channel);
var scaleStr = util_1.stringValue(scaleName);
var scale = model.getScaleComponent(channel);
var scaleType = scale ? scale.get('type') : undefined;
var size = model.getSizeSignalRef(channel === channel_1.X ? 'width' : 'height').signal;
var coord = channel + "(unit)";
var on = events(selCmpt, function (def, evt) {
return def.concat({ events: evt.between[0], update: "[" + coord + ", " + coord + "]" }, // Brush Start
{ events: evt, update: "[" + vname + "[0], clamp(" + coord + ", 0, " + size + ")]" } // Brush End
);
});
// React to pan/zooms of continuous scales. Non-continuous scales
// (bin-linear, band, point) cannot be pan/zoomed and any other changes
// to their domains (e.g., filtering) should clear the brushes.
on.push({
events: { signal: selCmpt.name + exports.SCALE_TRIGGER },
update: scale_1.hasContinuousDomain(scaleType) && !scale_1.isBinScale(scaleType) ?
"[scale(" + scaleStr + ", " + dname + "[0]), scale(" + scaleStr + ", " + dname + "[1])]" : "[0, 0]"
});
return hasScales ? [{ name: dname, on: [] }] : [{
name: vname, value: [], on: on
}, {
name: dname,
on: [{ events: { signal: vname }, update: vname + "[0] === " + vname + "[1] ? null : invert(" + scaleStr + ", " + vname + ")" }]
}];
}
function events(selCmpt, cb) {
return selCmpt.events.reduce(function (on, evt) {
if (!evt.between) {
log_1.warn(evt + " is not an ordered event stream for interval selections");
return on;
}
return cb(on, evt);
}, []);
}
},{"../../channel":8,"../../log":94,"../../scale":97,"../../util":107,"./selection":69,"./transforms/scales":74,"tslib":114}],68:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = require("../../util");
var selection_1 = require("./selection");
var nearest_1 = require("./transforms/nearest");
var multi = {
predicate: 'vlMulti',
scaleDomain: 'vlMultiDomain',
signals: function (model, selCmpt) {
var proj = selCmpt.project;
var datum = nearest_1.default.has(selCmpt) ?
'(item().isVoronoi ? datum.datum : datum)' : 'datum';
var bins = [];
var encodings = proj.map(function (p) { return util_1.stringValue(p.channel); }).filter(function (e) { return e; }).join(', ');
var fields = proj.map(function (p) { return util_1.stringValue(p.field); }).join(', ');
var values = proj.map(function (p) {
var channel = p.channel;
var fieldDef = model.fieldDef(channel);
// Binned fields should capture extents, for a range test against the raw field.
return (fieldDef && fieldDef.bin) ? (bins.push(p.field),
"[" + datum + "[" + util_1.stringValue(model.field(channel, { binSuffix: 'start' })) + "], " +
(datum + "[" + util_1.stringValue(model.field(channel, { binSuffix: 'end' })) + "]]")) :
datum + "[" + util_1.stringValue(p.field) + "]";
}).join(', ');
// Only add a discrete selection to the store if a datum is present _and_
// the interaction isn't occuring on a group mark. This guards against
// polluting interactive state with invalid values in faceted displays
// as the group marks are also data-driven. We force the update to account
// for constant null states but varying toggles (e.g., shift-click in
// whitespace followed by a click in whitespace; the store should only
// be cleared on the second click).
return [{
name: selCmpt.name + selection_1.TUPLE,
value: {},
on: [{
events: selCmpt.events,
update: "datum && item().mark.marktype !== 'group' ? " +
("{unit: " + selection_1.unitName(model) + ", encodings: [" + encodings + "], ") +
("fields: [" + fields + "], values: [" + values + "]") +
(bins.length ? ', ' + bins.map(function (b) { return util_1.stringValue('bin_' + b) + ": 1"; }).join(', ') : '') +
'} : null',
force: true
}]
}];
},
modifyExpr: function (model, selCmpt) {
var tpl = selCmpt.name + selection_1.TUPLE;
return tpl + ', ' +
(selCmpt.resolve === 'global' ? 'null' : "{unit: " + selection_1.unitName(model) + "}");
}
};
exports.default = multi;
},{"../../util":107,"./selection":69,"./transforms/nearest":72}],69:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_event_selector_1 = require("vega-event-selector");
var channel_1 = require("../../channel");
var log_1 = require("../../log");
var selection_1 = require("../../selection");
var util_1 = require("../../util");
var model_1 = require("../model");
var interval_1 = require("./interval");
var multi_1 = require("./multi");
var single_1 = require("./single");
var transforms_1 = require("./transforms/transforms");
exports.STORE = '_store';
exports.TUPLE = '_tuple';
exports.MODIFY = '_modify';
exports.SELECTION_DOMAIN = '_selection_domain_';
function parseUnitSelection(model, selDefs) {
var selCmpts = {};
var selectionConfig = model.config.selection;
var _loop_1 = function (name_1) {
if (!selDefs.hasOwnProperty(name_1)) {
return "continue";
}
var selDef = selDefs[name_1];
var cfg = selectionConfig[selDef.type];
// Set default values from config if a property hasn't been specified,
// or if it is true. E.g., "translate": true should use the default
// event handlers for translate. However, true may be a valid value for
// a property (e.g., "nearest": true).
for (var key in cfg) {
// A selection should contain either `encodings` or `fields`, only use
// default values for these two values if neither of them is specified.
if ((key === 'encodings' && selDef.fields) || (key === 'fields' && selDef.encodings)) {
continue;
}
if (key === 'mark') {
selDef[key] = tslib_1.__assign({}, cfg[key], selDef[key]);
}
if (selDef[key] === undefined || selDef[key] === true) {
selDef[key] = cfg[key] || selDef[key];
}
}
name_1 = util_1.varName(name_1);
var selCmpt = selCmpts[name_1] = util_1.extend({}, selDef, {
name: name_1,
events: util_1.isString(selDef.on) ? vega_event_selector_1.selector(selDef.on, 'scope') : selDef.on,
});
transforms_1.forEachTransform(selCmpt, function (txCompiler) {
if (txCompiler.parse) {
txCompiler.parse(model, selDef, selCmpt);
}
});
};
for (var name_1 in selDefs) {
_loop_1(name_1);
}
return selCmpts;
}
exports.parseUnitSelection = parseUnitSelection;
function assembleUnitSelectionSignals(model, signals) {
forEachSelection(model, function (selCmpt, selCompiler) {
var name = selCmpt.name;
var modifyExpr = selCompiler.modifyExpr(model, selCmpt);
signals.push.apply(signals, selCompiler.signals(model, selCmpt));
transforms_1.forEachTransform(selCmpt, function (txCompiler) {
if (txCompiler.signals) {
signals = txCompiler.signals(model, selCmpt, signals);
}
if (txCompiler.modifyExpr) {
modifyExpr = txCompiler.modifyExpr(model, selCmpt, modifyExpr);
}
});
signals.push({
name: name + exports.MODIFY,
on: [{
events: { signal: name + exports.TUPLE },
update: "modify(" + util_1.stringValue(selCmpt.name + exports.STORE) + ", " + modifyExpr + ")"
}]
});
});
var facetModel = getFacetModel(model);
if (signals.length && facetModel) {
var name_2 = util_1.stringValue(facetModel.getName('cell'));
signals.unshift({
name: 'facet',
value: {},
on: [{
events: vega_event_selector_1.selector('mousemove', 'scope'),
update: "isTuple(facet) ? facet : group(" + name_2 + ").datum"
}]
});
}
return signals;
}
exports.assembleUnitSelectionSignals = assembleUnitSelectionSignals;
function assembleTopLevelSignals(model, signals) {
var needsUnit = false;
forEachSelection(model, function (selCmpt, selCompiler) {
if (selCompiler.topLevelSignals) {
signals = selCompiler.topLevelSignals(model, selCmpt, signals);
}
transforms_1.forEachTransform(selCmpt, function (txCompiler) {
if (txCompiler.topLevelSignals) {
signals = txCompiler.topLevelSignals(model, selCmpt, signals);
}
});
needsUnit = true;
});
if (needsUnit) {
var hasUnit = signals.filter(function (s) { return s.name === 'unit'; });
if (!(hasUnit.length)) {
signals.unshift({
name: 'unit',
value: {},
on: [{ events: 'mousemove', update: 'isTuple(group()) ? group() : unit' }]
});
}
}
return signals;
}
exports.assembleTopLevelSignals = assembleTopLevelSignals;
function assembleUnitSelectionData(model, data) {
forEachSelection(model, function (selCmpt) {
var contains = data.filter(function (d) { return d.name === selCmpt.name + exports.STORE; });
if (!contains.length) {
data.push({ name: selCmpt.name + exports.STORE });
}
});
return data;
}
exports.assembleUnitSelectionData = assembleUnitSelectionData;
function assembleUnitSelectionMarks(model, marks) {
var selMarks = marks;
forEachSelection(model, function (selCmpt, selCompiler) {
selMarks = selCompiler.marks ? selCompiler.marks(model, selCmpt, selMarks) : selMarks;
transforms_1.forEachTransform(selCmpt, function (txCompiler) {
if (txCompiler.marks) {
selMarks = txCompiler.marks(model, selCmpt, marks, selMarks);
}
});
});
return selMarks;
}
exports.assembleUnitSelectionMarks = assembleUnitSelectionMarks;
function assembleLayerSelectionMarks(model, marks) {
model.children.forEach(function (child) {
if (model_1.isUnitModel(child)) {
marks = assembleUnitSelectionMarks(child, marks);
}
});
return marks;
}
exports.assembleLayerSelectionMarks = assembleLayerSelectionMarks;
function predicate(model, selections, dfnode) {
var stores = [];
function expr(name) {
var vname = util_1.varName(name);
var selCmpt = model.getSelectionComponent(vname, name);
var store = util_1.stringValue(vname + exports.STORE);
if (selCmpt.timeUnit) {
var child = dfnode || model.component.data.raw;
var tunode = selCmpt.timeUnit.clone();
if (child.parent) {
tunode.insertAsParentOf(child);
}
else {
child.parent = tunode;
}
}
stores.push(store);
return compiler(selCmpt.type).predicate + ("(" + store + ", datum") +
(selCmpt.resolve === 'global' ? ')' : ", " + util_1.stringValue(selCmpt.resolve) + ")");
}
var predicateStr = util_1.logicalExpr(selections, expr);
return '!(' + stores.map(function (s) { return "length(data(" + s + "))"; }).join(' || ') +
(") || (" + predicateStr + ")");
}
exports.predicate = predicate;
// Selections are parsed _after_ scales. If a scale domain is set to
// use a selection, the SELECTION_DOMAIN constant is used as the
// domainRaw.signal during scale.parse and then replaced with the necessary
// selection expression function during scale.assemble. To not pollute the
// type signatures to account for this setup, the selection domain definition
// is coerced to a string and appended to SELECTION_DOMAIN.
function isRawSelectionDomain(domainRaw) {
return domainRaw.signal.indexOf(exports.SELECTION_DOMAIN) >= 0;
}
exports.isRawSelectionDomain = isRawSelectionDomain;
function selectionScaleDomain(model, domainRaw) {
var selDomain = JSON.parse(domainRaw.signal.replace(exports.SELECTION_DOMAIN, ''));
var name = util_1.varName(selDomain.selection);
var selCmpt = model.component.selection && model.component.selection[name];
if (selCmpt) {
log_1.warn('Use "bind": "scales" to setup a binding for scales and selections within the same view.');
}
else {
selCmpt = model.getSelectionComponent(name, selDomain.selection);
if (!selDomain.encoding && !selDomain.field) {
selDomain.field = selCmpt.project[0].field;
if (selCmpt.project.length > 1) {
log_1.warn('A "field" or "encoding" must be specified when using a selection as a scale domain. ' +
("Using \"field\": " + util_1.stringValue(selDomain.field) + "."));
}
}
return {
signal: compiler(selCmpt.type).scaleDomain +
("(" + util_1.stringValue(name + exports.STORE) + ", " + util_1.stringValue(selDomain.encoding || null) + ", ") +
util_1.stringValue(selDomain.field || null) +
(selCmpt.resolve === 'global' ? ')' : ", " + util_1.stringValue(selCmpt.resolve) + ")")
};
}
return { signal: 'null' };
}
exports.selectionScaleDomain = selectionScaleDomain;
// Utility functions
function forEachSelection(model, cb) {
var selections = model.component.selection;
for (var name_3 in selections) {
if (selections.hasOwnProperty(name_3)) {
var sel = selections[name_3];
cb(sel, compiler(sel.type));
}
}
}
function compiler(type) {
switch (type) {
case 'single':
return single_1.default;
case 'multi':
return multi_1.default;
case 'interval':
return interval_1.default;
}
return null;
}
function getFacetModel(model) {
var parent = model.parent;
while (parent) {
if (model_1.isFacetModel(parent)) {
break;
}
parent = parent.parent;
}
return parent;
}
function unitName(model) {
var name = util_1.stringValue(model.name);
var facet = getFacetModel(model);
if (facet) {
name += (facet.facet.row ? " + '_' + facet[" + util_1.stringValue(facet.field('row')) + "]" : '')
+ (facet.facet.column ? " + '_' + facet[" + util_1.stringValue(facet.field('column')) + "]" : '');
}
return name;
}
exports.unitName = unitName;
function requiresSelectionId(model) {
var identifier = false;
forEachSelection(model, function (selCmpt) {
identifier = identifier || selCmpt.project.some(function (proj) { return proj.field === selection_1.SELECTION_ID; });
});
return identifier;
}
exports.requiresSelectionId = requiresSelectionId;
function channelSignalName(selCmpt, channel, range) {
return util_1.varName(selCmpt.name + '_' + (range === 'visual' ? channel : selCmpt.fields[channel]));
}
exports.channelSignalName = channelSignalName;
function spatialProjections(selCmpt) {
var x = null;
var xi = null;
var y = null;
var yi = null;
selCmpt.project.forEach(function (p, i) {
if (p.channel === channel_1.X) {
x = p;
xi = i;
}
else if (p.channel === channel_1.Y) {
y = p;
yi = i;
}
});
return { x: x, xi: xi, y: y, yi: yi };
}
exports.spatialProjections = spatialProjections;
},{"../../channel":8,"../../log":94,"../../selection":98,"../../util":107,"../model":56,"./interval":67,"./multi":68,"./single":70,"./transforms/transforms":76,"tslib":114,"vega-event-selector":115}],70:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = require("../../util");
var multi_1 = require("./multi");
var selection_1 = require("./selection");
var single = {
predicate: 'vlSingle',
scaleDomain: 'vlSingleDomain',
signals: multi_1.default.signals,
topLevelSignals: function (model, selCmpt, signals) {
var hasSignal = signals.filter(function (s) { return s.name === selCmpt.name; });
var data = "data(" + util_1.stringValue(selCmpt.name + selection_1.STORE) + ")";
var values = data + "[0].values";
return hasSignal.length ? signals : signals.concat({
name: selCmpt.name,
update: data + ".length && {" +
selCmpt.project.map(function (p, i) { return p.field + ": " + values + "[" + i + "]"; }).join(', ') + '}'
});
},
modifyExpr: function (model, selCmpt) {
var tpl = selCmpt.name + selection_1.TUPLE;
return tpl + ', ' +
(selCmpt.resolve === 'global' ? 'true' : "{unit: " + selection_1.unitName(model) + "}");
}
};
exports.default = single;
},{"../../util":107,"./multi":68,"./selection":69}],71:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = require("../../../util");
var selection_1 = require("../selection");
var nearest_1 = require("./nearest");
var inputBindings = {
has: function (selCmpt) {
return selCmpt.type === 'single' && selCmpt.resolve === 'global' &&
selCmpt.bind && selCmpt.bind !== 'scales';
},
topLevelSignals: function (model, selCmpt, signals) {
var name = selCmpt.name;
var proj = selCmpt.project;
var bind = selCmpt.bind;
var datum = nearest_1.default.has(selCmpt) ?
'(item().isVoronoi ? datum.datum : datum)' : 'datum';
proj.forEach(function (p) {
var sgname = util_1.varName(name + "_" + p.field);
var hasSignal = signals.filter(function (s) { return s.name === sgname; });
if (!hasSignal.length) {
signals.unshift({
name: sgname,
value: '',
on: [{
events: selCmpt.events,
update: "datum && item().mark.marktype !== 'group' ? " + datum + "[" + util_1.stringValue(p.field) + "] : null"
}],
bind: bind[p.field] || bind[p.channel] || bind
});
}
});
return signals;
},
signals: function (model, selCmpt, signals) {
var name = selCmpt.name;
var proj = selCmpt.project;
var signal = signals.filter(function (s) { return s.name === name + selection_1.TUPLE; })[0];
var fields = proj.map(function (p) { return util_1.stringValue(p.field); }).join(', ');
var values = proj.map(function (p) { return util_1.varName(name + "_" + p.field); });
signal.update = values.join(' && ') + " ? {fields: [" + fields + "], values: [" + values.join(', ') + "]} : null";
delete signal.value;
delete signal.on;
return signals;
}
};
exports.default = inputBindings;
},{"../../../util":107,"../selection":69,"./nearest":72}],72:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var selection_1 = require("../selection");
var VORONOI = 'voronoi';
var nearest = {
has: function (selCmpt) {
return selCmpt.type !== 'interval' && selCmpt.nearest;
},
marks: function (model, selCmpt, marks, selMarks) {
var _a = selection_1.spatialProjections(selCmpt), x = _a.x, y = _a.y;
var mark = marks[0];
var index = selMarks.indexOf(mark);
var isPathgroup = mark.name === model.getName('pathgroup');
var exists = (function (m) { return m.name && m.name.indexOf(VORONOI) >= 0; });
var cellDef = {
name: model.getName(VORONOI),
type: 'path',
from: { data: model.getName('marks') },
encode: {
enter: {
fill: { value: 'transparent' },
strokeWidth: { value: 0.35 },
stroke: { value: 'transparent' },
isVoronoi: { value: true }
}
},
transform: [{
type: 'voronoi',
x: (x || (!x && !y)) ? 'datum.x' : { expr: '0' },
y: (y || (!x && !y)) ? 'datum.y' : { expr: '0' },
size: [model.getSizeSignalRef('width'), model.getSizeSignalRef('height')]
}]
};
if (isPathgroup && !mark.marks.filter(exists).length) {
mark.marks.push(cellDef);
selMarks.splice(index, 1, mark);
}
else if (!isPathgroup && !selMarks.filter(exists).length) {
selMarks.splice(index + 1, 0, cellDef);
}
return selMarks;
}
};
exports.default = nearest;
},{"../selection":69}],73:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var log = require("../../../log");
var util_1 = require("../../../util");
var timeunit_1 = require("../../data/timeunit");
var project = {
has: function (selDef) {
return selDef.fields !== undefined || selDef.encodings !== undefined;
},
parse: function (model, selDef, selCmpt) {
var channels = {};
var timeUnits = {};
// TODO: find a possible channel mapping for these fields.
(selDef.fields || []).forEach(function (field) { return channels[field] = null; });
(selDef.encodings || []).forEach(function (channel) {
var fieldDef = model.fieldDef(channel);
if (fieldDef) {
if (fieldDef.timeUnit) {
var tuField = model.field(channel);
channels[tuField] = channel;
// Construct TimeUnitComponents which will be combined into a
// TimeUnitNode. This node may need to be inserted into the
// dataflow if the selection is used across views that do not
// have these time units defined.
timeUnits[tuField] = {
as: tuField,
field: fieldDef.field,
timeUnit: fieldDef.timeUnit
};
}
else {
channels[fieldDef.field] = channel;
}
}
else {
log.warn(log.message.cannotProjectOnChannelWithoutField(channel));
}
});
var projection = selCmpt.project || (selCmpt.project = []);
for (var field in channels) {
if (channels.hasOwnProperty(field)) {
projection.push({ field: field, channel: channels[field] });
}
}
var fields = selCmpt.fields || (selCmpt.fields = {});
projection.filter(function (p) { return p.channel; }).forEach(function (p) { return fields[p.channel] = p.field; });
if (util_1.keys(timeUnits).length) {
selCmpt.timeUnit = new timeunit_1.TimeUnitNode(timeUnits);
}
}
};
exports.default = project;
},{"../../../log":94,"../../../util":107,"../../data/timeunit":32}],74:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var log_1 = require("../../../log");
var scale_1 = require("../../../scale");
var util_1 = require("../../../util");
var selection_1 = require("../selection");
var scaleBindings = {
has: function (selCmpt) {
return selCmpt.type === 'interval' && selCmpt.resolve === 'global' &&
selCmpt.bind && selCmpt.bind === 'scales';
},
parse: function (model, selDef, selCmpt) {
var bound = selCmpt.scales = [];
selCmpt.project.forEach(function (p) {
var channel = p.channel;
var scale = model.getScaleComponent(channel);
var scaleType = scale ? scale.get('type') : undefined;
if (!scale || !scale_1.hasContinuousDomain(scaleType) || scale_1.isBinScale(scaleType)) {
log_1.warn('Scale bindings are currently only supported for scales with unbinned, continuous domains.');
return;
}
scale.set('domainRaw', { signal: selection_1.channelSignalName(selCmpt, channel, 'data') }, true);
bound.push(channel);
});
},
topLevelSignals: function (model, selCmpt, signals) {
// Top-level signals are only needed when coordinating composed views.
if (!model.parent) {
return signals;
}
var channels = selCmpt.scales.filter(function (channel) {
return !(signals.filter(function (s) { return s.name === selection_1.channelSignalName(selCmpt, channel, 'data'); }).length);
});
return signals.concat(channels.map(function (channel) {
return { name: selection_1.channelSignalName(selCmpt, channel, 'data') };
}));
},
signals: function (model, selCmpt, signals) {
// Nested signals need only push to top-level signals when within composed views.
if (model.parent) {
selCmpt.scales.forEach(function (channel) {
var signal = signals.filter(function (s) { return s.name === selection_1.channelSignalName(selCmpt, channel, 'data'); })[0];
signal.push = 'outer';
delete signal.value;
delete signal.update;
});
}
return signals;
}
};
exports.default = scaleBindings;
function domain(model, channel) {
var scale = util_1.stringValue(model.scaleName(channel));
return "domain(" + scale + ")";
}
exports.domain = domain;
},{"../../../log":94,"../../../scale":97,"../../../util":107,"../selection":69}],75:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var selection_1 = require("../selection");
var TOGGLE = '_toggle';
var toggle = {
has: function (selCmpt) {
return selCmpt.type === 'multi' && selCmpt.toggle;
},
signals: function (model, selCmpt, signals) {
return signals.concat({
name: selCmpt.name + TOGGLE,
value: false,
on: [{ events: selCmpt.events, update: selCmpt.toggle }]
});
},
modifyExpr: function (model, selCmpt, expr) {
var tpl = selCmpt.name + selection_1.TUPLE;
var signal = selCmpt.name + TOGGLE;
return signal + " ? null : " + tpl + ", " +
(selCmpt.resolve === 'global' ?
signal + " ? null : true, " :
signal + " ? null : {unit: " + selection_1.unitName(model) + "}, ") +
(signal + " ? " + tpl + " : null");
}
};
exports.default = toggle;
},{"../selection":69}],76:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var inputs_1 = require("./inputs");
var nearest_1 = require("./nearest");
var project_1 = require("./project");
var scales_1 = require("./scales");
var toggle_1 = require("./toggle");
var translate_1 = require("./translate");
var zoom_1 = require("./zoom");
var compilers = { project: project_1.default, toggle: toggle_1.default, scales: scales_1.default,
translate: translate_1.default, zoom: zoom_1.default, inputs: inputs_1.default, nearest: nearest_1.default };
function forEachTransform(selCmpt, cb) {
for (var t in compilers) {
if (compilers[t].has(selCmpt)) {
cb(compilers[t]);
}
}
}
exports.forEachTransform = forEachTransform;
},{"./inputs":71,"./nearest":72,"./project":73,"./scales":74,"./toggle":75,"./translate":77,"./zoom":78}],77:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var vega_event_selector_1 = require("vega-event-selector");
var channel_1 = require("../../../channel");
var interval_1 = require("../interval");
var selection_1 = require("../selection");
var scales_1 = require("./scales");
var ANCHOR = '_translate_anchor';
var DELTA = '_translate_delta';
var translate = {
has: function (selCmpt) {
return selCmpt.type === 'interval' && selCmpt.translate;
},
signals: function (model, selCmpt, signals) {
var name = selCmpt.name;
var hasScales = scales_1.default.has(selCmpt);
var anchor = name + ANCHOR;
var _a = selection_1.spatialProjections(selCmpt), x = _a.x, y = _a.y;
var events = vega_event_selector_1.selector(selCmpt.translate, 'scope');
if (!hasScales) {
events = events.map(function (e) { return (e.between[0].markname = name + interval_1.BRUSH, e); });
}
signals.push({
name: anchor,
value: {},
on: [{
events: events.map(function (e) { return e.between[0]; }),
update: '{x: x(unit), y: y(unit)' +
(x !== null ? ', extent_x: ' + (hasScales ? scales_1.domain(model, channel_1.X) :
"slice(" + selection_1.channelSignalName(selCmpt, 'x', 'visual') + ")") : '') +
(y !== null ? ', extent_y: ' + (hasScales ? scales_1.domain(model, channel_1.Y) :
"slice(" + selection_1.channelSignalName(selCmpt, 'y', 'visual') + ")") : '') + '}'
}]
}, {
name: name + DELTA,
value: {},
on: [{
events: events,
update: "{x: " + anchor + ".x - x(unit), y: " + anchor + ".y - y(unit)}"
}]
});
if (x !== null) {
onDelta(model, selCmpt, channel_1.X, 'width', signals);
}
if (y !== null) {
onDelta(model, selCmpt, channel_1.Y, 'height', signals);
}
return signals;
}
};
exports.default = translate;
function onDelta(model, selCmpt, channel, size, signals) {
var name = selCmpt.name;
var hasScales = scales_1.default.has(selCmpt);
var signal = signals.filter(function (s) {
return s.name === selection_1.channelSignalName(selCmpt, channel, hasScales ? 'data' : 'visual');
})[0];
var anchor = name + ANCHOR;
var delta = name + DELTA;
var sizeSg = model.getSizeSignalRef(size).signal;
var scaleCmpt = model.getScaleComponent(channel);
var scaleType = scaleCmpt.get('type');
var sign = hasScales && channel === channel_1.X ? '-' : ''; // Invert delta when panning x-scales.
var extent = anchor + ".extent_" + channel;
var offset = "" + sign + delta + "." + channel + " / " + (hasScales ? "" + sizeSg : "span(" + extent + ")");
var panFn = !hasScales ? 'panLinear' :
scaleType === 'log' ? 'panLog' :
scaleType === 'pow' ? 'panPow' : 'panLinear';
var update = panFn + "(" + extent + ", " + offset +
(hasScales && scaleType === 'pow' ? ", " + (scaleCmpt.get('exponent') || 1) : '') + ')';
signal.on.push({
events: { signal: delta },
update: hasScales ? update : "clampRange(" + update + ", 0, " + sizeSg + ")"
});
}
},{"../../../channel":8,"../interval":67,"../selection":69,"./scales":74,"vega-event-selector":115}],78:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var vega_event_selector_1 = require("vega-event-selector");
var channel_1 = require("../../../channel");
var util_1 = require("../../../util");
var interval_1 = require("../interval");
var selection_1 = require("../selection");
var scales_1 = require("./scales");
var ANCHOR = '_zoom_anchor';
var DELTA = '_zoom_delta';
var zoom = {
has: function (selCmpt) {
return selCmpt.type === 'interval' && selCmpt.zoom;
},
signals: function (model, selCmpt, signals) {
var name = selCmpt.name;
var hasScales = scales_1.default.has(selCmpt);
var delta = name + DELTA;
var _a = selection_1.spatialProjections(selCmpt), x = _a.x, y = _a.y;
var sx = util_1.stringValue(model.scaleName(channel_1.X));
var sy = util_1.stringValue(model.scaleName(channel_1.Y));
var events = vega_event_selector_1.selector(selCmpt.zoom, 'scope');
if (!hasScales) {
events = events.map(function (e) { return (e.markname = name + interval_1.BRUSH, e); });
}
signals.push({
name: name + ANCHOR,
on: [{
events: events,
update: !hasScales ? "{x: x(unit), y: y(unit)}" :
'{' + [
(sx ? "x: invert(" + sx + ", x(unit))" : ''),
(sy ? "y: invert(" + sy + ", y(unit))" : '')
].filter(function (expr) { return !!expr; }).join(', ') + '}'
}]
}, {
name: delta,
on: [{
events: events,
force: true,
update: 'pow(1.001, event.deltaY * pow(16, event.deltaMode))'
}]
});
if (x !== null) {
onDelta(model, selCmpt, 'x', 'width', signals);
}
if (y !== null) {
onDelta(model, selCmpt, 'y', 'height', signals);
}
return signals;
}
};
exports.default = zoom;
function onDelta(model, selCmpt, channel, size, signals) {
var name = selCmpt.name;
var hasScales = scales_1.default.has(selCmpt);
var signal = signals.filter(function (s) {
return s.name === selection_1.channelSignalName(selCmpt, channel, hasScales ? 'data' : 'visual');
})[0];
var sizeSg = model.getSizeSignalRef(size).signal;
var scaleCmpt = model.getScaleComponent(channel);
var scaleType = scaleCmpt.get('type');
var base = hasScales ? scales_1.domain(model, channel) : signal.name;
var delta = name + DELTA;
var anchor = "" + name + ANCHOR + "." + channel;
var zoomFn = !hasScales ? 'zoomLinear' :
scaleType === 'log' ? 'zoomLog' :
scaleType === 'pow' ? 'zoomPow' : 'zoomLinear';
var update = zoomFn + "(" + base + ", " + anchor + ", " + delta +
(hasScales && scaleType === 'pow' ? ", " + (scaleCmpt.get('exponent') || 1) : '') + ')';
signal.on.push({
events: { signal: delta },
update: hasScales ? update : "clampRange(" + update + ", 0, " + sizeSg + ")"
});
}
},{"../../../channel":8,"../../../util":107,"../interval":67,"../selection":69,"./scales":74,"vega-event-selector":115}],79:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var log = require("../log");
var util_1 = require("../util");
/**
* Generic class for storing properties that are explicitly specified
* and implicitly determined by the compiler.
* This is important for scale/axis/legend merging as
* we want to prioritize properties that users explicitly specified.
*/
var Split = (function () {
function Split(explicit, implicit) {
if (explicit === void 0) { explicit = {}; }
if (implicit === void 0) { implicit = {}; }
this.explicit = explicit;
this.implicit = implicit;
}
Split.prototype.clone = function () {
return new Split(util_1.duplicate(this.explicit), util_1.duplicate(this.implicit));
};
Split.prototype.combine = function () {
// FIXME remove "as any".
// Add "as any" to avoid an error "Spread types may only be created from object types".
return tslib_1.__assign({}, this.explicit, this.implicit);
};
Split.prototype.get = function (key) {
// Explicit has higher precedence
return this.explicit[key] !== undefined ? this.explicit[key] : this.implicit[key];
};
Split.prototype.getWithExplicit = function (key) {
// Explicit has higher precedence
if (this.explicit[key] !== undefined) {
return { explicit: true, value: this.explicit[key] };
}
else if (this.implicit[key] !== undefined) {
return { explicit: false, value: this.implicit[key] };
}
return { explicit: false, value: undefined };
};
Split.prototype.setWithExplicit = function (key, value) {
if (value.value !== undefined) {
this.set(key, value.value, value.explicit);
}
};
Split.prototype.set = function (key, value, explicit) {
delete this[explicit ? 'implicit' : 'explicit'][key];
this[explicit ? 'explicit' : 'implicit'][key] = value;
return this;
};
Split.prototype.copyKeyFromSplit = function (key, s) {
// Explicit has higher precedence
if (s.explicit[key] !== undefined) {
this.set(key, s.explicit[key], true);
}
else if (s.implicit[key] !== undefined) {
this.set(key, s.implicit[key], false);
}
};
Split.prototype.copyKeyFromObject = function (key, s) {
// Explicit has higher precedence
if (s[key] !== undefined) {
this.set(key, s[key], true);
}
};
Split.prototype.extend = function (mixins, explicit) {
return new Split(explicit ? tslib_1.__assign({}, this.explicit, mixins) : this.explicit, explicit ? this.implicit : tslib_1.__assign({}, this.implicit, mixins));
};
return Split;
}());
exports.Split = Split;
function makeExplicit(value) {
return {
explicit: true,
value: value
};
}
exports.makeExplicit = makeExplicit;
function makeImplicit(value) {
return {
explicit: false,
value: value
};
}
exports.makeImplicit = makeImplicit;
function tieBreakByComparing(compare) {
return function (v1, v2, property, propertyOf) {
var diff = compare(v1.value, v2.value);
if (diff > 0) {
return v1;
}
else if (diff < 0) {
return v2;
}
return defaultTieBreaker(v1, v2, property, propertyOf);
};
}
exports.tieBreakByComparing = tieBreakByComparing;
function defaultTieBreaker(v1, v2, property, propertyOf) {
if (v1.explicit && v2.explicit) {
log.warn(log.message.mergeConflictingProperty(property, propertyOf, v1.value, v2.value));
}
// If equal score, prefer v1.
return v1;
}
exports.defaultTieBreaker = defaultTieBreaker;
function mergeValuesWithExplicit(v1, v2, property, propertyOf, tieBreaker) {
if (tieBreaker === void 0) { tieBreaker = defaultTieBreaker; }
if (v1 === undefined || v1.value === undefined) {
// For first run
return v2;
}
if (v1.explicit && !v2.explicit) {
return v1;
}
else if (v2.explicit && !v1.explicit) {
return v2;
}
else if (v1.value === v2.value) {
return v1;
}
else {
return tieBreaker(v1, v2, property, propertyOf);
}
}
exports.mergeValuesWithExplicit = mergeValuesWithExplicit;
},{"../log":94,"../util":107,"tslib":114}],80:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("../channel");
var vlEncoding = require("../encoding");
var encoding_1 = require("../encoding");
var fielddef_1 = require("../fielddef");
var mark_1 = require("../mark");
var scale_1 = require("../scale");
var stack_1 = require("../stack");
var util_1 = require("../util");
var parse_1 = require("./axis/parse");
var parse_2 = require("./data/parse");
var assemble_1 = require("./layoutsize/assemble");
var parse_3 = require("./layoutsize/parse");
var init_1 = require("./mark/init");
var mark_2 = require("./mark/mark");
var model_1 = require("./model");
var repeater_1 = require("./repeater");
var assemble_2 = require("./scale/assemble");
var selection_1 = require("./selection/selection");
/**
* Internal model of Vega-Lite specification for the compiler.
*/
var UnitModel = (function (_super) {
tslib_1.__extends(UnitModel, _super);
function UnitModel(spec, parent, parentGivenName, parentGivenSize, repeater, config) {
if (parentGivenSize === void 0) { parentGivenSize = {}; }
var _this = _super.call(this, spec, parent, parentGivenName, config, {}) || this;
_this.type = 'unit';
_this.specifiedScales = {};
_this.specifiedAxes = {};
_this.specifiedLegends = {};
_this.selection = {};
_this.children = [];
_this.initSize(tslib_1.__assign({}, parentGivenSize, (spec.width ? { width: spec.width } : {}), (spec.height ? { height: spec.height } : {})));
_this.markDef = mark_1.isMarkDef(spec.mark) ? tslib_1.__assign({}, spec.mark) : { type: spec.mark };
var mark = _this.markDef.type;
var encoding = _this.encoding = encoding_1.normalizeEncoding(repeater_1.replaceRepeaterInEncoding(spec.encoding || {}, repeater), mark);
// calculate stack properties
_this.stack = stack_1.stack(mark, encoding, _this.config.stack);
_this.specifiedScales = _this.initScales(mark, encoding);
// FIXME: this one seems out of place!
_this.encoding = init_1.initEncoding(_this.markDef, encoding, _this.stack, _this.config);
_this.specifiedAxes = _this.initAxes(encoding);
_this.specifiedLegends = _this.initLegend(encoding);
// Selections will be initialized upon parse.
_this.selection = spec.selection;
return _this;
}
/**
* Return specified Vega-lite scale domain for a particular channel
* @param channel
*/
UnitModel.prototype.scaleDomain = function (channel) {
var scale = this.specifiedScales[channel];
return scale ? scale.domain : undefined;
};
UnitModel.prototype.hasDiscreteDomain = function (channel) {
if (channel_1.isScaleChannel(channel)) {
var scaleCmpt = this.getScaleComponent(channel);
return scaleCmpt && scale_1.hasDiscreteDomain(scaleCmpt.get('type'));
}
return false;
};
UnitModel.prototype.sort = function (channel) {
return (this.getMapping()[channel] || {}).sort;
};
UnitModel.prototype.axis = function (channel) {
return this.specifiedAxes[channel];
};
UnitModel.prototype.legend = function (channel) {
return this.specifiedLegends[channel];
};
UnitModel.prototype.initScales = function (mark, encoding) {
return channel_1.SCALE_CHANNELS.reduce(function (scales, channel) {
var fieldDef;
var specifiedScale;
var channelDef = encoding[channel];
if (fielddef_1.isFieldDef(channelDef)) {
fieldDef = channelDef;
specifiedScale = channelDef.scale;
}
else if (fielddef_1.isConditionalDef(channelDef) && fielddef_1.isFieldDef(channelDef.condition)) {
fieldDef = channelDef.condition;
specifiedScale = channelDef.condition.scale;
}
else if (channel === 'x') {
fieldDef = fielddef_1.getFieldDef(encoding.x2);
}
else if (channel === 'y') {
fieldDef = fielddef_1.getFieldDef(encoding.y2);
}
if (fieldDef) {
scales[channel] = specifiedScale || {};
}
return scales;
}, {});
};
UnitModel.prototype.initAxes = function (encoding) {
return [channel_1.X, channel_1.Y].reduce(function (_axis, channel) {
// Position Axis
// TODO: handle ConditionFieldDef
var channelDef = encoding[channel];
if (fielddef_1.isFieldDef(channelDef) ||
(channel === channel_1.X && fielddef_1.isFieldDef(encoding.x2)) ||
(channel === channel_1.Y && fielddef_1.isFieldDef(encoding.y2))) {
var axisSpec = fielddef_1.isFieldDef(channelDef) ? channelDef.axis : null;
// We no longer support false in the schema, but we keep false here for backward compatability.
if (axisSpec !== null && axisSpec !== false) {
_axis[channel] = tslib_1.__assign({}, axisSpec);
}
}
return _axis;
}, {});
};
UnitModel.prototype.initLegend = function (encoding) {
return channel_1.NONSPATIAL_SCALE_CHANNELS.reduce(function (_legend, channel) {
var channelDef = encoding[channel];
if (channelDef) {
var legend = fielddef_1.isFieldDef(channelDef) ? channelDef.legend :
(channelDef.condition && fielddef_1.isFieldDef(channelDef.condition)) ? channelDef.condition.legend : null;
if (legend !== null && legend !== false) {
_legend[channel] = tslib_1.__assign({}, legend);
}
}
return _legend;
}, {});
};
UnitModel.prototype.parseData = function () {
this.component.data = parse_2.parseData(this);
};
UnitModel.prototype.parseLayoutSize = function () {
parse_3.parseUnitLayoutSize(this);
};
UnitModel.prototype.parseSelection = function () {
this.component.selection = selection_1.parseUnitSelection(this, this.selection);
};
UnitModel.prototype.parseMarkGroup = function () {
this.component.mark = mark_2.parseMarkGroup(this);
};
UnitModel.prototype.parseAxisAndHeader = function () {
this.component.axes = parse_1.parseUnitAxis(this);
};
UnitModel.prototype.assembleScales = function () {
return assemble_2.assembleScalesForModel(this);
};
UnitModel.prototype.assembleSelectionTopLevelSignals = function (signals) {
return selection_1.assembleTopLevelSignals(this, signals);
};
UnitModel.prototype.assembleSelectionSignals = function () {
return selection_1.assembleUnitSelectionSignals(this, []);
};
UnitModel.prototype.assembleSelectionData = function (data) {
return selection_1.assembleUnitSelectionData(this, data);
};
UnitModel.prototype.assembleLayout = function () {
return null;
};
UnitModel.prototype.assembleLayoutSignals = function () {
return assemble_1.assembleLayoutSignals(this);
};
UnitModel.prototype.assembleMarks = function () {
var marks = this.component.mark || [];
// If this unit is part of a layer, selections should augment
// all in concert rather than each unit individually. This
// ensures correct interleaving of clipping and brushed marks.
if (!this.parent || !model_1.isLayerModel(this.parent)) {
marks = selection_1.assembleUnitSelectionMarks(this, marks);
}
return marks.map(this.correctDataNames);
};
UnitModel.prototype.assembleLayoutSize = function () {
return {
width: this.getSizeSignalRef('width'),
height: this.getSizeSignalRef('height')
};
};
UnitModel.prototype.getMapping = function () {
return this.encoding;
};
UnitModel.prototype.toSpec = function (excludeConfig, excludeData) {
var encoding = util_1.duplicate(this.encoding);
var spec;
spec = {
mark: this.markDef,
encoding: encoding
};
if (!excludeConfig) {
spec.config = util_1.duplicate(this.config);
}
if (!excludeData) {
spec.data = util_1.duplicate(this.data);
}
// remove defaults
return spec;
};
UnitModel.prototype.mark = function () {
return this.markDef.type;
};
UnitModel.prototype.channelHasField = function (channel) {
return vlEncoding.channelHasField(this.encoding, channel);
};
UnitModel.prototype.fieldDef = function (channel) {
var channelDef = this.encoding[channel];
return fielddef_1.getFieldDef(channelDef);
};
/** Get "field" reference for vega */
UnitModel.prototype.field = function (channel, opt) {
if (opt === void 0) { opt = {}; }
var fieldDef = this.fieldDef(channel);
if (!fieldDef) {
return undefined;
}
if (fieldDef.bin) {
opt = util_1.extend({
binSuffix: this.hasDiscreteDomain(channel) ? 'range' : 'start'
}, opt);
}
return fielddef_1.field(fieldDef, opt);
};
return UnitModel;
}(model_1.ModelWithField));
exports.UnitModel = UnitModel;
},{"../channel":8,"../encoding":87,"../fielddef":89,"../mark":96,"../scale":97,"../stack":101,"../util":107,"./axis/parse":12,"./data/parse":29,"./layoutsize/assemble":37,"./layoutsize/parse":38,"./mark/init":46,"./mark/mark":48,"./model":56,"./repeater":58,"./scale/assemble":60,"./selection/selection":69,"tslib":114}],81:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var vega_util_1 = require("vega-util");
var encoding_1 = require("../encoding");
var encoding_2 = require("./../encoding");
var fielddef_1 = require("./../fielddef");
var log = require("./../log");
exports.BOXPLOT = 'box-plot';
function isBoxPlotDef(mark) {
return !!mark['type'];
}
exports.isBoxPlotDef = isBoxPlotDef;
exports.BOXPLOT_STYLES = ['boxWhisker', 'box', 'boxMid'];
exports.VL_ONLY_BOXPLOT_CONFIG_PROPERTY_INDEX = {
box: ['size']
};
var supportedChannels = ['x', 'y', 'color', 'detail', 'opacity', 'size'];
function filterUnsupportedChannels(spec) {
return tslib_1.__assign({}, spec, { encoding: encoding_1.reduce(spec.encoding, function (newEncoding, fieldDef, channel) {
if (supportedChannels.indexOf(channel) > -1) {
newEncoding[channel] = fieldDef;
}
else {
log.warn(log.message.incompatibleChannel(channel, exports.BOXPLOT));
}
return newEncoding;
}, {}) });
}
exports.filterUnsupportedChannels = filterUnsupportedChannels;
function normalizeBoxPlot(spec, config) {
spec = filterUnsupportedChannels(spec);
// TODO: use selection
var mark = spec.mark, encoding = spec.encoding, selection = spec.selection, outerSpec = tslib_1.__rest(spec, ["mark", "encoding", "selection"]);
var kIQRScalar = undefined;
if (isBoxPlotDef(mark)) {
if (mark.extent) {
if (vega_util_1.isNumber(mark.extent)) {
kIQRScalar = mark.extent;
}
}
}
var isMinMax = kIQRScalar === undefined;
var orient = boxOrient(spec);
var _a = boxParams(spec, orient, kIQRScalar), transform = _a.transform, continuousAxisChannelDef = _a.continuousAxisChannelDef, continuousAxis = _a.continuousAxis, encodingWithoutContinuousAxis = _a.encodingWithoutContinuousAxis;
var size = encodingWithoutContinuousAxis.size, color = encodingWithoutContinuousAxis.color, nonPositionEncodingWithoutColorSize = tslib_1.__rest(encodingWithoutContinuousAxis, ["size", "color"]);
var sizeMixins = size ? { size: size } : { size: { value: config.box.size } };
var continuousAxisScaleAndAxis = {};
if (continuousAxisChannelDef.scale) {
continuousAxisScaleAndAxis['scale'] = continuousAxisChannelDef.scale;
}
if (continuousAxisChannelDef.axis) {
continuousAxisScaleAndAxis['axis'] = continuousAxisChannelDef.axis;
}
return tslib_1.__assign({}, outerSpec, { transform: transform, layer: [
{
mark: {
type: 'rule',
style: 'boxWhisker'
},
encoding: tslib_1.__assign((_b = {}, _b[continuousAxis] = tslib_1.__assign({ field: 'lowerWhisker', type: continuousAxisChannelDef.type }, continuousAxisScaleAndAxis), _b[continuousAxis + '2'] = {
field: 'lowerBox',
type: continuousAxisChannelDef.type
}, _b), nonPositionEncodingWithoutColorSize)
}, {
mark: {
type: 'rule',
style: 'boxWhisker'
},
encoding: tslib_1.__assign((_c = {}, _c[continuousAxis] = {
field: 'upperBox',
type: continuousAxisChannelDef.type
}, _c[continuousAxis + '2'] = {
field: 'upperWhisker',
type: continuousAxisChannelDef.type
}, _c), nonPositionEncodingWithoutColorSize)
},
tslib_1.__assign({}, (selection ? { selection: selection } : {}), { mark: {
type: 'bar',
style: 'box'
}, encoding: tslib_1.__assign((_d = {}, _d[continuousAxis] = {
field: 'lowerBox',
type: continuousAxisChannelDef.type
}, _d[continuousAxis + '2'] = {
field: 'upperBox',
type: continuousAxisChannelDef.type
}, _d), encodingWithoutContinuousAxis, sizeMixins) }),
{
mark: {
type: 'tick',
style: 'boxMid'
},
encoding: tslib_1.__assign((_e = {}, _e[continuousAxis] = {
field: 'midBox',
type: continuousAxisChannelDef.type
}, _e), nonPositionEncodingWithoutColorSize, sizeMixins)
}
] });
var _b, _c, _d, _e;
}
exports.normalizeBoxPlot = normalizeBoxPlot;
function boxOrient(spec) {
var mark = spec.mark, encoding = spec.encoding, outerSpec = tslib_1.__rest(spec, ["mark", "encoding"]);
if (fielddef_1.isFieldDef(encoding.x) && fielddef_1.isContinuous(encoding.x)) {
// x is continuous
if (fielddef_1.isFieldDef(encoding.y) && fielddef_1.isContinuous(encoding.y)) {
// both x and y are continuous
if (encoding.x.aggregate === undefined && encoding.y.aggregate === exports.BOXPLOT) {
return 'vertical';
}
else if (encoding.y.aggregate === undefined && encoding.x.aggregate === exports.BOXPLOT) {
return 'horizontal';
}
else if (encoding.x.aggregate === exports.BOXPLOT && encoding.y.aggregate === exports.BOXPLOT) {
throw new Error('Both x and y cannot have aggregate');
}
else {
if (isBoxPlotDef(mark) && mark.orient) {
return mark.orient;
}
// default orientation = vertical
return 'vertical';
}
}
// x is continuous but y is not
return 'horizontal';
}
else if (fielddef_1.isFieldDef(encoding.y) && fielddef_1.isContinuous(encoding.y)) {
// y is continuous but x is not
return 'vertical';
}
else {
// Neither x nor y is continuous.
throw new Error('Need a valid continuous axis for boxplots');
}
}
function boxContinousAxis(spec, orient) {
var mark = spec.mark, encoding = spec.encoding, outerSpec = tslib_1.__rest(spec, ["mark", "encoding"]);
var continuousAxisChannelDef;
var continuousAxis;
if (orient === 'vertical') {
continuousAxis = 'y';
continuousAxisChannelDef = encoding.y; // Safe to cast because if y is not continous fielddef, the orient would not be vertical.
}
else {
continuousAxis = 'x';
continuousAxisChannelDef = encoding.x; // Safe to cast because if x is not continous fielddef, the orient would not be horizontal.
}
if (continuousAxisChannelDef && continuousAxisChannelDef.aggregate) {
var aggregate = continuousAxisChannelDef.aggregate, continuousAxisWithoutAggregate = tslib_1.__rest(continuousAxisChannelDef, ["aggregate"]);
if (aggregate !== exports.BOXPLOT) {
log.warn("Continuous axis should not have customized aggregation function " + aggregate);
}
continuousAxisChannelDef = continuousAxisWithoutAggregate;
}
return {
continuousAxisChannelDef: continuousAxisChannelDef,
continuousAxis: continuousAxis
};
}
function boxParams(spec, orient, kIQRScalar) {
var _a = boxContinousAxis(spec, orient), continuousAxisChannelDef = _a.continuousAxisChannelDef, continuousAxis = _a.continuousAxis;
var encoding = spec.encoding;
var isMinMax = kIQRScalar === undefined;
var summarize = [
{
aggregate: 'q1',
field: continuousAxisChannelDef.field,
as: 'lowerBox'
},
{
aggregate: 'q3',
field: continuousAxisChannelDef.field,
as: 'upperBox'
},
{
aggregate: 'median',
field: continuousAxisChannelDef.field,
as: 'midBox'
}
];
var postAggregateCalculates = [];
if (isMinMax) {
summarize.push({
aggregate: 'min',
field: continuousAxisChannelDef.field,
as: 'lowerWhisker'
});
summarize.push({
aggregate: 'max',
field: continuousAxisChannelDef.field,
as: 'upperWhisker'
});
}
else {
postAggregateCalculates = [
{
calculate: 'datum.upperBox - datum.lowerBox',
as: 'IQR'
},
{
calculate: 'datum.lowerBox - datum.IQR * ' + kIQRScalar,
as: 'lowerWhisker'
},
{
calculate: 'datum.upperBox + datum.IQR * ' + kIQRScalar,
as: 'lowerWhisker'
}
];
}
var groupby = [];
var bins = [];
var timeUnits = [];
var encodingWithoutContinuousAxis = {};
encoding_2.forEach(encoding, function (channelDef, channel) {
if (channel === continuousAxis) {
// Skip continuous axis as we already handle it separately
return;
}
if (fielddef_1.isFieldDef(channelDef)) {
if (channelDef.aggregate && channelDef.aggregate !== exports.BOXPLOT) {
summarize.push({
aggregate: channelDef.aggregate,
field: channelDef.field,
as: fielddef_1.field(channelDef)
});
}
else if (channelDef.aggregate === undefined) {
var transformedField = fielddef_1.field(channelDef);
// Add bin or timeUnit transform if applicable
var bin = channelDef.bin;
if (bin) {
var field_1 = channelDef.field;
bins.push({ bin: bin, field: field_1, as: transformedField });
}
else if (channelDef.timeUnit) {
var timeUnit = channelDef.timeUnit, field_2 = channelDef.field;
timeUnits.push({ timeUnit: timeUnit, field: field_2, as: transformedField });
}
groupby.push(transformedField);
}
// now the field should refer to post-transformed field instead
encodingWithoutContinuousAxis[channel] = {
field: fielddef_1.field(channelDef),
type: channelDef.type
};
}
else {
// For value def, just copy
encodingWithoutContinuousAxis[channel] = encoding[channel];
}
});
return {
transform: [].concat(bins, timeUnits, [{ summarize: summarize, groupby: groupby }], postAggregateCalculates),
continuousAxisChannelDef: continuousAxisChannelDef,
continuousAxis: continuousAxis,
encodingWithoutContinuousAxis: encodingWithoutContinuousAxis
};
}
},{"../encoding":87,"./../encoding":87,"./../fielddef":89,"./../log":94,"tslib":114,"vega-util":116}],82:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
exports.ERRORBAR = 'error-bar';
function normalizeErrorBar(spec) {
// TODO: use selection
var _m = spec.mark, _sel = spec.selection, encoding = spec.encoding, outerSpec = tslib_1.__rest(spec, ["mark", "selection", "encoding"]);
var _s = encoding.size, encodingWithoutSize = tslib_1.__rest(encoding, ["size"]);
var _x2 = encoding.x2, _y2 = encoding.y2, encodingWithoutX2Y2 = tslib_1.__rest(encoding, ["x2", "y2"]);
var _x = encodingWithoutX2Y2.x, _y = encodingWithoutX2Y2.y, encodingWithoutX_X2_Y_Y2 = tslib_1.__rest(encodingWithoutX2Y2, ["x", "y"]);
if (!encoding.x2 && !encoding.y2) {
throw new Error('Neither x2 or y2 provided');
}
return tslib_1.__assign({}, outerSpec, { layer: [
{
mark: 'rule',
encoding: encodingWithoutSize
}, {
mark: 'tick',
encoding: encodingWithoutX2Y2
}, {
mark: 'tick',
encoding: encoding.x2 ? tslib_1.__assign({ x: encoding.x2, y: encoding.y }, encodingWithoutX_X2_Y_Y2) : tslib_1.__assign({ x: encoding.x, y: encoding.y2 }, encodingWithoutX_X2_Y_Y2)
}
] });
}
exports.normalizeErrorBar = normalizeErrorBar;
},{"tslib":114}],83:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var mark_1 = require("./../mark");
var boxplot_1 = require("./boxplot");
var errorbar_1 = require("./errorbar");
/**
* Registry index for all composite mark's normalizer
*/
var normalizerRegistry = {};
function add(mark, normalizer) {
normalizerRegistry[mark] = normalizer;
}
exports.add = add;
function remove(mark) {
delete normalizerRegistry[mark];
}
exports.remove = remove;
exports.COMPOSITE_MARK_STYLES = boxplot_1.BOXPLOT_STYLES;
exports.VL_ONLY_COMPOSITE_MARK_SPECIFIC_CONFIG_PROPERTY_INDEX = tslib_1.__assign({}, boxplot_1.VL_ONLY_BOXPLOT_CONFIG_PROPERTY_INDEX);
add(boxplot_1.BOXPLOT, boxplot_1.normalizeBoxPlot);
add(errorbar_1.ERRORBAR, errorbar_1.normalizeErrorBar);
/**
* Transform a unit spec with composite mark into a normal layer spec.
*/
function normalize(
// This GenericUnitSpec has any as Encoding because unit specs with composite mark can have additional encoding channels.
spec, config) {
var mark = mark_1.isMarkDef(spec.mark) ? spec.mark.type : spec.mark;
var normalizer = normalizerRegistry[mark];
if (normalizer) {
return normalizer(spec, config);
}
throw new Error("Unregistered composite mark " + mark);
}
exports.normalize = normalize;
},{"./../mark":96,"./boxplot":81,"./errorbar":82,"tslib":114}],84:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var compositemark_1 = require("./compositemark");
var index_1 = require("./compositemark/index");
var guide_1 = require("./guide");
var legend_1 = require("./legend");
var mark_1 = require("./mark");
var mark = require("./mark");
var scale_1 = require("./scale");
var selection_1 = require("./selection");
var title_1 = require("./title");
var util_1 = require("./util");
exports.defaultCellConfig = {
width: 200,
height: 200
};
exports.defaultConfig = {
padding: 5,
timeFormat: '%b %d, %Y',
countTitle: 'Number of Records',
invalidValues: 'filter',
cell: exports.defaultCellConfig,
mark: mark.defaultMarkConfig,
area: {},
bar: mark.defaultBarConfig,
circle: {},
line: {},
point: {},
rect: {},
rule: { color: 'black' },
square: {},
text: { color: 'black' },
tick: mark.defaultTickConfig,
box: { size: 14 },
boxWhisker: {},
boxMid: { color: 'white' },
scale: scale_1.defaultScaleConfig,
axis: {
domainColor: '#888',
tickColor: '#888'
},
axisX: {},
axisY: { minExtent: 30 },
axisLeft: {},
axisRight: {},
axisTop: {},
axisBottom: {},
axisBand: {},
legend: legend_1.defaultLegendConfig,
selection: selection_1.defaultConfig,
style: {},
title: {},
};
function initConfig(config) {
return util_1.mergeDeep(util_1.duplicate(exports.defaultConfig), config);
}
exports.initConfig = initConfig;
var MARK_STYLES = ['cell'].concat(mark_1.PRIMITIVE_MARKS, compositemark_1.COMPOSITE_MARK_STYLES);
var VL_ONLY_CONFIG_PROPERTIES = [
'padding', 'numberFormat', 'timeFormat', 'countTitle',
'stack', 'scale', 'selection', 'invalidValues',
'overlay' // FIXME: Redesign and unhide this
];
var VL_ONLY_ALL_MARK_SPECIFIC_CONFIG_PROPERTY_INDEX = tslib_1.__assign({ cell: ['width', 'height'] }, mark_1.VL_ONLY_MARK_SPECIFIC_CONFIG_PROPERTY_INDEX, index_1.VL_ONLY_COMPOSITE_MARK_SPECIFIC_CONFIG_PROPERTY_INDEX);
function stripAndRedirectConfig(config) {
config = util_1.duplicate(config);
for (var _i = 0, VL_ONLY_CONFIG_PROPERTIES_1 = VL_ONLY_CONFIG_PROPERTIES; _i < VL_ONLY_CONFIG_PROPERTIES_1.length; _i++) {
var prop = VL_ONLY_CONFIG_PROPERTIES_1[_i];
delete config[prop];
}
// Remove Vega-Lite only axis/legend config
if (config.axis) {
for (var _a = 0, VL_ONLY_GUIDE_CONFIG_1 = guide_1.VL_ONLY_GUIDE_CONFIG; _a < VL_ONLY_GUIDE_CONFIG_1.length; _a++) {
var prop = VL_ONLY_GUIDE_CONFIG_1[_a];
delete config.axis[prop];
}
}
if (config.legend) {
for (var _b = 0, VL_ONLY_GUIDE_CONFIG_2 = guide_1.VL_ONLY_GUIDE_CONFIG; _b < VL_ONLY_GUIDE_CONFIG_2.length; _b++) {
var prop = VL_ONLY_GUIDE_CONFIG_2[_b];
delete config.legend[prop];
}
}
// Remove Vega-Lite only generic mark config
if (config.mark) {
for (var _c = 0, VL_ONLY_MARK_CONFIG_PROPERTIES_1 = mark_1.VL_ONLY_MARK_CONFIG_PROPERTIES; _c < VL_ONLY_MARK_CONFIG_PROPERTIES_1.length; _c++) {
var prop = VL_ONLY_MARK_CONFIG_PROPERTIES_1[_c];
delete config.mark[prop];
}
}
for (var _d = 0, MARK_STYLES_1 = MARK_STYLES; _d < MARK_STYLES_1.length; _d++) {
var mark_2 = MARK_STYLES_1[_d];
// Remove Vega-Lite-only mark config
for (var _e = 0, VL_ONLY_MARK_CONFIG_PROPERTIES_2 = mark_1.VL_ONLY_MARK_CONFIG_PROPERTIES; _e < VL_ONLY_MARK_CONFIG_PROPERTIES_2.length; _e++) {
var prop = VL_ONLY_MARK_CONFIG_PROPERTIES_2[_e];
delete config[mark_2][prop];
}
// Remove Vega-Lite only mark-specific config
var vlOnlyMarkSpecificConfigs = VL_ONLY_ALL_MARK_SPECIFIC_CONFIG_PROPERTY_INDEX[mark_2];
if (vlOnlyMarkSpecificConfigs) {
for (var _f = 0, vlOnlyMarkSpecificConfigs_1 = vlOnlyMarkSpecificConfigs; _f < vlOnlyMarkSpecificConfigs_1.length; _f++) {
var prop = vlOnlyMarkSpecificConfigs_1[_f];
delete config[mark_2][prop];
}
}
// Redirect mark config to config.style so that mark config only affect its own mark type
// without affecting other marks that share the same underlying Vega marks.
// For example, config.rect should not affect bar marks.
redirectConfig(config, mark_2);
}
// Redirect config.title -- so that title config do not
// affect header labels, which also uses `title` directive to implement.
redirectConfig(config, 'title', 'group-title');
// Remove empty config objects
for (var prop in config) {
if (util_1.isObject(config[prop]) && util_1.keys(config[prop]).length === 0) {
delete config[prop];
}
}
return util_1.keys(config).length > 0 ? config : undefined;
}
exports.stripAndRedirectConfig = stripAndRedirectConfig;
function redirectConfig(config, prop, toProp) {
var propConfig = prop === 'title' ? title_1.extractTitleConfig(config.title).mark : config[prop];
var style = tslib_1.__assign({}, propConfig, config.style[prop]);
// set config.style if it is not an empty object
if (util_1.keys(style).length > 0) {
config.style[toProp || prop] = style;
}
delete config[prop];
}
},{"./compositemark":83,"./compositemark/index":83,"./guide":91,"./legend":93,"./mark":96,"./scale":97,"./selection":98,"./title":103,"./util":107,"tslib":114}],85:[function(require,module,exports){
"use strict";
/*
* Constants and utilities for data.
*/
Object.defineProperty(exports, "__esModule", { value: true });
function isUrlData(data) {
return !!data['url'];
}
exports.isUrlData = isUrlData;
function isInlineData(data) {
return !!data['values'];
}
exports.isInlineData = isInlineData;
function isNamedData(data) {
return !!data['name'];
}
exports.isNamedData = isNamedData;
exports.MAIN = 'main';
exports.RAW = 'raw';
},{}],86:[function(require,module,exports){
"use strict";
// DateTime definition object
Object.defineProperty(exports, "__esModule", { value: true });
var log = require("./log");
var util_1 = require("./util");
/*
* A designated year that starts on Sunday.
*/
var SUNDAY_YEAR = 2006;
function isDateTime(o) {
return !!o && (!!o.year || !!o.quarter || !!o.month || !!o.date || !!o.day ||
!!o.hours || !!o.minutes || !!o.seconds || !!o.milliseconds);
}
exports.isDateTime = isDateTime;
exports.MONTHS = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];
exports.SHORT_MONTHS = exports.MONTHS.map(function (m) { return m.substr(0, 3); });
exports.DAYS = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
exports.SHORT_DAYS = exports.DAYS.map(function (d) { return d.substr(0, 3); });
function normalizeQuarter(q) {
if (util_1.isNumber(q)) {
if (q > 4) {
log.warn(log.message.invalidTimeUnit('quarter', q));
}
// We accept 1-based quarter, so need to readjust to 0-based quarter
return (q - 1) + '';
}
else {
// Invalid quarter
throw new Error(log.message.invalidTimeUnit('quarter', q));
}
}
function normalizeMonth(m) {
if (util_1.isNumber(m)) {
// We accept 1-based month, so need to readjust to 0-based month
return (m - 1) + '';
}
else {
var lowerM = m.toLowerCase();
var monthIndex = exports.MONTHS.indexOf(lowerM);
if (monthIndex !== -1) {
return monthIndex + ''; // 0 for january, ...
}
var shortM = lowerM.substr(0, 3);
var shortMonthIndex = exports.SHORT_MONTHS.indexOf(shortM);
if (shortMonthIndex !== -1) {
return shortMonthIndex + '';
}
// Invalid month
throw new Error(log.message.invalidTimeUnit('month', m));
}
}
function normalizeDay(d) {
if (util_1.isNumber(d)) {
// mod so that this can be both 0-based where 0 = sunday
// and 1-based where 7=sunday
return (d % 7) + '';
}
else {
var lowerD = d.toLowerCase();
var dayIndex = exports.DAYS.indexOf(lowerD);
if (dayIndex !== -1) {
return dayIndex + ''; // 0 for january, ...
}
var shortD = lowerD.substr(0, 3);
var shortDayIndex = exports.SHORT_DAYS.indexOf(shortD);
if (shortDayIndex !== -1) {
return shortDayIndex + '';
}
// Invalid day
throw new Error(log.message.invalidTimeUnit('day', d));
}
}
/**
* Return Vega Expression for a particular date time.
* @param d
* @param normalize whether to normalize quarter, month, day.
*/
function dateTimeExpr(d, normalize) {
if (normalize === void 0) { normalize = false; }
var units = [];
if (normalize && d.day !== undefined) {
if (util_1.keys(d).length > 1) {
log.warn(log.message.droppedDay(d));
d = util_1.duplicate(d);
delete d.day;
}
}
if (d.year !== undefined) {
units.push(d.year);
}
else if (d.day !== undefined) {
// Set year to 2006 for working with day since January 1 2006 is a Sunday
units.push(SUNDAY_YEAR);
}
else {
units.push(0);
}
if (d.month !== undefined) {
var month = normalize ? normalizeMonth(d.month) : d.month;
units.push(month);
}
else if (d.quarter !== undefined) {
var quarter = normalize ? normalizeQuarter(d.quarter) : d.quarter;
units.push(quarter + '*3');
}
else {
units.push(0); // months start at zero in JS
}
if (d.date !== undefined) {
units.push(d.date);
}
else if (d.day !== undefined) {
// HACK: Day only works as a standalone unit
// This is only correct because we always set year to 2006 for day
var day = normalize ? normalizeDay(d.day) : d.day;
units.push(day + '+1');
}
else {
units.push(1); // Date starts at 1 in JS
}
// Note: can't use TimeUnit enum here as importing it will create
// circular dependency problem!
for (var _i = 0, _a = ['hours', 'minutes', 'seconds', 'milliseconds']; _i < _a.length; _i++) {
var timeUnit = _a[_i];
if (d[timeUnit] !== undefined) {
units.push(d[timeUnit]);
}
else {
units.push(0);
}
}
if (d.utc) {
return "utc(" + units.join(', ') + ")";
}
else {
return "datetime(" + units.join(', ') + ")";
}
}
exports.dateTimeExpr = dateTimeExpr;
},{"./log":94,"./util":107}],87:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var channel_1 = require("./channel");
var fielddef_1 = require("./fielddef");
var log = require("./log");
var util_1 = require("./util");
function channelHasField(encoding, channel) {
var channelDef = encoding && encoding[channel];
if (channelDef) {
if (util_1.isArray(channelDef)) {
return util_1.some(channelDef, function (fieldDef) { return !!fieldDef.field; });
}
else {
return fielddef_1.isFieldDef(channelDef) || fielddef_1.hasConditionFieldDef(channelDef);
}
}
return false;
}
exports.channelHasField = channelHasField;
function isAggregate(encoding) {
return util_1.some(channel_1.CHANNELS, function (channel) {
if (channelHasField(encoding, channel)) {
var channelDef = encoding[channel];
if (util_1.isArray(channelDef)) {
return util_1.some(channelDef, function (fieldDef) { return !!fieldDef.aggregate; });
}
else {
var fieldDef = fielddef_1.getFieldDef(channelDef);
return fieldDef && !!fieldDef.aggregate;
}
}
return false;
});
}
exports.isAggregate = isAggregate;
function normalizeEncoding(encoding, mark) {
return util_1.keys(encoding).reduce(function (normalizedEncoding, channel) {
if (!channel_1.supportMark(channel, mark)) {
// Drop unsupported channel
log.warn(log.message.incompatibleChannel(channel, mark));
return normalizedEncoding;
}
// Drop line's size if the field is aggregated.
if (channel === 'size' && mark === 'line') {
var fieldDef = fielddef_1.getFieldDef(encoding[channel]);
if (fieldDef && fieldDef.aggregate) {
log.warn(log.message.incompatibleChannel(channel, mark, 'when the field is aggregated.'));
return normalizedEncoding;
}
}
if (channel === 'detail' || channel === 'order') {
var channelDef = encoding[channel];
if (channelDef) {
// Array of fieldDefs for detail channel (or production rule)
normalizedEncoding[channel] = (util_1.isArray(channelDef) ? channelDef : [channelDef])
.reduce(function (fieldDefs, fieldDef) {
if (!fielddef_1.isFieldDef(fieldDef)) {
log.warn(log.message.emptyFieldDef(fieldDef, channel));
}
else {
fieldDefs.push(fielddef_1.normalizeFieldDef(fieldDef, channel));
}
return fieldDefs;
}, []);
}
}
else {
// FIXME: remove this casting. (I don't know why Typescript doesn't infer this correctly here.)
var channelDef = encoding[channel];
if (!fielddef_1.isFieldDef(channelDef) && !fielddef_1.isValueDef(channelDef) && !fielddef_1.isConditionalDef(channelDef)) {
log.warn(log.message.emptyFieldDef(channelDef, channel));
return normalizedEncoding;
}
normalizedEncoding[channel] = fielddef_1.normalize(channelDef, channel);
}
return normalizedEncoding;
}, {});
}
exports.normalizeEncoding = normalizeEncoding;
function isRanged(encoding) {
return encoding && ((!!encoding.x && !!encoding.x2) || (!!encoding.y && !!encoding.y2));
}
exports.isRanged = isRanged;
function fieldDefs(encoding) {
var arr = [];
channel_1.CHANNELS.forEach(function (channel) {
if (channelHasField(encoding, channel)) {
var channelDef = encoding[channel];
(util_1.isArray(channelDef) ? channelDef : [channelDef]).forEach(function (def) {
if (fielddef_1.isFieldDef(def)) {
arr.push(def);
}
else if (fielddef_1.hasConditionFieldDef(def)) {
arr.push(def.condition);
}
});
}
});
return arr;
}
exports.fieldDefs = fieldDefs;
function forEach(mapping, f, thisArg) {
if (!mapping) {
return;
}
util_1.keys(mapping).forEach(function (c) {
var channel = c;
if (util_1.isArray(mapping[channel])) {
mapping[channel].forEach(function (channelDef) {
f.call(thisArg, channelDef, channel);
});
}
else {
f.call(thisArg, mapping[channel], channel);
}
});
}
exports.forEach = forEach;
function reduce(mapping, f, init, thisArg) {
if (!mapping) {
return init;
}
return util_1.keys(mapping).reduce(function (r, c) {
var channel = c;
if (util_1.isArray(mapping[channel])) {
return mapping[channel].reduce(function (r1, channelDef) {
return f.call(thisArg, r1, channelDef, channel);
}, r);
}
else {
return f.call(thisArg, r, mapping[channel], channel);
}
}, init);
}
exports.reduce = reduce;
},{"./channel":8,"./fielddef":89,"./log":94,"./util":107}],88:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
},{}],89:[function(require,module,exports){
"use strict";
// utility for a field definition object
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var aggregate_1 = require("./aggregate");
var bin_1 = require("./bin");
var channel_1 = require("./channel");
var log = require("./log");
var timeunit_1 = require("./timeunit");
var type_1 = require("./type");
var util_1 = require("./util");
function isRepeatRef(field) {
return field && !util_1.isString(field) && 'repeat' in field;
}
exports.isRepeatRef = isRepeatRef;
function isConditionalDef(channelDef) {
return !!channelDef && !!channelDef.condition;
}
exports.isConditionalDef = isConditionalDef;
/**
* Return if a channelDef is a ConditionalValueDef with ConditionFieldDef
*/
function hasConditionFieldDef(channelDef) {
return !!channelDef && !!channelDef.condition && isFieldDef(channelDef.condition);
}
exports.hasConditionFieldDef = hasConditionFieldDef;
function isFieldDef(channelDef) {
return !!channelDef && (!!channelDef['field'] || channelDef['aggregate'] === 'count');
}
exports.isFieldDef = isFieldDef;
function isValueDef(channelDef) {
return channelDef && 'value' in channelDef && channelDef['value'] !== undefined;
}
exports.isValueDef = isValueDef;
function isScaleFieldDef(channelDef) {
return !!channelDef && (!!channelDef['scale'] || !!channelDef['sort']);
}
exports.isScaleFieldDef = isScaleFieldDef;
function field(fieldDef, opt) {
if (opt === void 0) { opt = {}; }
var field = fieldDef.field;
var prefix = opt.prefix;
var suffix = opt.suffix;
if (isCount(fieldDef)) {
field = 'count_*';
}
else {
var fn = undefined;
if (!opt.nofn) {
if (fieldDef.bin) {
fn = bin_1.binToString(fieldDef.bin);
suffix = opt.binSuffix;
}
else if (fieldDef.aggregate) {
fn = String(opt.aggregate || fieldDef.aggregate);
}
else if (fieldDef.timeUnit) {
fn = String(fieldDef.timeUnit);
}
}
if (fn) {
field = fn + "_" + field;
}
}
if (suffix) {
field = field + "_" + suffix;
}
if (prefix) {
field = prefix + "_" + field;
}
if (opt.expr) {
field = opt.expr + "[" + util_1.stringValue(field) + "]";
}
return field;
}
exports.field = field;
function isDiscrete(fieldDef) {
switch (fieldDef.type) {
case 'nominal':
case 'ordinal':
return true;
case 'quantitative':
return !!fieldDef.bin;
case 'temporal':
// TODO: deal with custom scale type case.
return timeunit_1.isDiscreteByDefault(fieldDef.timeUnit);
}
throw new Error(log.message.invalidFieldType(fieldDef.type));
}
exports.isDiscrete = isDiscrete;
function isContinuous(fieldDef) {
return !isDiscrete(fieldDef);
}
exports.isContinuous = isContinuous;
function isCount(fieldDef) {
return fieldDef.aggregate === 'count';
}
exports.isCount = isCount;
function title(fieldDef, config) {
if (isCount(fieldDef)) {
return config.countTitle;
}
var fn = fieldDef.aggregate || fieldDef.timeUnit || (fieldDef.bin && 'bin');
if (fn) {
return fn.toUpperCase() + '(' + fieldDef.field + ')';
}
else {
return fieldDef.field;
}
}
exports.title = title;
function defaultType(fieldDef, channel) {
if (fieldDef.timeUnit) {
return 'temporal';
}
if (fieldDef.bin) {
return 'quantitative';
}
switch (channel_1.rangeType(channel)) {
case 'continuous':
return 'quantitative';
case 'discrete':
return 'nominal';
case 'flexible':// color
return 'nominal';
default:
return 'quantitative';
}
}
exports.defaultType = defaultType;
/**
* Returns the fieldDef -- either from the outer channelDef or from the condition of channelDef.
* @param channelDef
*/
function getFieldDef(channelDef) {
if (isFieldDef(channelDef)) {
return channelDef;
}
else if (hasConditionFieldDef(channelDef)) {
return channelDef.condition;
}
return undefined;
}
exports.getFieldDef = getFieldDef;
/**
* Convert type to full, lowercase type, or augment the fieldDef with a default type if missing.
*/
function normalize(channelDef, channel) {
// If a fieldDef contains a field, we need type.
if (isFieldDef(channelDef)) {
return normalizeFieldDef(channelDef, channel);
}
else if (hasConditionFieldDef(channelDef)) {
return tslib_1.__assign({}, channelDef, {
// Need to cast as normalizeFieldDef normally return FieldDef, but here we know that it is definitely Condition<FieldDef>
condition: normalizeFieldDef(channelDef.condition, channel) });
}
return channelDef;
}
exports.normalize = normalize;
function normalizeFieldDef(fieldDef, channel) {
// Drop invalid aggregate
if (fieldDef.aggregate && !aggregate_1.AGGREGATE_OP_INDEX[fieldDef.aggregate]) {
var aggregate = fieldDef.aggregate, fieldDefWithoutAggregate = tslib_1.__rest(fieldDef, ["aggregate"]);
log.warn(log.message.invalidAggregate(fieldDef.aggregate));
fieldDef = fieldDefWithoutAggregate;
}
// Normalize bin
if (fieldDef.bin) {
fieldDef = tslib_1.__assign({}, fieldDef, { bin: normalizeBin(fieldDef.bin, channel) });
}
// Normalize Type
if (fieldDef.type) {
var fullType = type_1.getFullName(fieldDef.type);
if (fieldDef.type !== fullType) {
// convert short type to full type
fieldDef = tslib_1.__assign({}, fieldDef, { type: fullType });
}
if (fieldDef.type !== 'quantitative') {
if (aggregate_1.isCountingAggregateOp(fieldDef.aggregate)) {
log.warn(log.message.invalidFieldTypeForCountAggregate(fieldDef.type, fieldDef.aggregate));
fieldDef = tslib_1.__assign({}, fieldDef, { type: 'quantitative' });
}
else if (fieldDef.bin) {
log.warn(log.message.invalidFieldTypeForBin(fieldDef.type));
fieldDef = tslib_1.__assign({}, fieldDef, { type: 'quantitative' });
}
}
}
else {
// If type is empty / invalid, then augment with default type
var newType = defaultType(fieldDef, channel);
log.warn(log.message.emptyOrInvalidFieldType(fieldDef.type, channel, newType));
fieldDef = tslib_1.__assign({}, fieldDef, { type: newType });
}
var _a = channelCompatibility(fieldDef, channel), compatible = _a.compatible, warning = _a.warning;
if (!compatible) {
log.warn(warning);
}
return fieldDef;
}
exports.normalizeFieldDef = normalizeFieldDef;
function normalizeBin(bin, channel) {
if (util_1.isBoolean(bin)) {
return { maxbins: bin_1.autoMaxBins(channel) };
}
else if (!bin.maxbins && !bin.step) {
return tslib_1.__assign({}, bin, { maxbins: bin_1.autoMaxBins(channel) });
}
else {
return bin;
}
}
exports.normalizeBin = normalizeBin;
var COMPATIBLE = { compatible: true };
function channelCompatibility(fieldDef, channel) {
switch (channel) {
case 'row':
case 'column':
if (isContinuous(fieldDef) && !fieldDef.timeUnit) {
// TODO:(https://github.com/vega/vega-lite/issues/2011):
// with timeUnit it's not always strictly continuous
return {
compatible: false,
warning: log.message.facetChannelShouldBeDiscrete(channel)
};
}
return COMPATIBLE;
case 'x':
case 'y':
case 'color':
case 'text':
case 'detail':
case 'tooltip':
return COMPATIBLE;
case 'opacity':
case 'size':
case 'x2':
case 'y2':
if (isDiscrete(fieldDef) && !fieldDef.bin) {
return {
compatible: false,
warning: "Channel " + channel + " should not be used with discrete field."
};
}
return COMPATIBLE;
case 'shape':
if (fieldDef.type !== 'nominal') {
return {
compatible: false,
warning: 'Shape channel should be used with nominal data only'
};
}
return COMPATIBLE;
case 'order':
if (fieldDef.type === 'nominal') {
return {
compatible: false,
warning: "Channel order is inappropriate for nominal field, which has no inherent order."
};
}
return COMPATIBLE;
}
throw new Error('channelCompatability not implemented for channel ' + channel);
}
exports.channelCompatibility = channelCompatibility;
},{"./aggregate":5,"./bin":7,"./channel":8,"./log":94,"./timeunit":102,"./type":106,"./util":107,"tslib":114}],90:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var selection_1 = require("./compile/selection/selection");
var datetime_1 = require("./datetime");
var fielddef_1 = require("./fielddef");
var timeunit_1 = require("./timeunit");
var util_1 = require("./util");
function isSelectionFilter(filter) {
return filter && filter['selection'];
}
exports.isSelectionFilter = isSelectionFilter;
function isEqualFilter(filter) {
return filter && !!filter.field && filter.equal !== undefined;
}
exports.isEqualFilter = isEqualFilter;
function isRangeFilter(filter) {
if (filter && filter.field) {
if (util_1.isArray(filter.range) && filter.range.length === 2) {
return true;
}
}
return false;
}
exports.isRangeFilter = isRangeFilter;
function isOneOfFilter(filter) {
return filter && !!filter.field && (util_1.isArray(filter.oneOf) ||
util_1.isArray(filter.in) // backward compatibility
);
}
exports.isOneOfFilter = isOneOfFilter;
/**
* Converts a filter into an expression.
*/
// model is only used for selection filters.
function expression(model, filterOp, node) {
return util_1.logicalExpr(filterOp, function (filter) {
if (util_1.isString(filter)) {
return filter;
}
else if (isSelectionFilter(filter)) {
return selection_1.predicate(model, filter.selection, node);
}
else {
var fieldExpr = filter.timeUnit ?
// For timeUnit, cast into integer with time() so we can use ===, inrange, indexOf to compare values directly.
// TODO: We calculate timeUnit on the fly here. Consider if we would like to consolidate this with timeUnit pipeline
// TODO: support utc
('time(' + timeunit_1.fieldExpr(filter.timeUnit, filter.field) + ')') :
fielddef_1.field(filter, { expr: 'datum' });
if (isEqualFilter(filter)) {
return fieldExpr + '===' + valueExpr(filter.equal, filter.timeUnit);
}
else if (isOneOfFilter(filter)) {
// "oneOf" was formerly "in" -- so we need to add backward compatibility
var oneOf = filter.oneOf || filter['in'];
return 'indexof([' +
oneOf.map(function (v) { return valueExpr(v, filter.timeUnit); }).join(',') +
'], ' + fieldExpr + ') !== -1';
}
else if (isRangeFilter(filter)) {
var lower = filter.range[0];
var upper = filter.range[1];
if (lower !== null && upper !== null) {
return 'inrange(' + fieldExpr + ', [' +
valueExpr(lower, filter.timeUnit) + ', ' +
valueExpr(upper, filter.timeUnit) + '])';
}
else if (lower !== null) {
return fieldExpr + ' >= ' + lower;
}
else if (upper !== null) {
return fieldExpr + ' <= ' + upper;
}
}
}
return undefined;
});
}
exports.expression = expression;
function valueExpr(v, timeUnit) {
if (datetime_1.isDateTime(v)) {
var expr = datetime_1.dateTimeExpr(v, true);
return 'time(' + expr + ')';
}
if (timeunit_1.isSingleTimeUnit(timeUnit)) {
var datetime = {};
datetime[timeUnit] = v;
var expr = datetime_1.dateTimeExpr(datetime, true);
return 'time(' + expr + ')';
}
return JSON.stringify(v);
}
},{"./compile/selection/selection":69,"./datetime":86,"./fielddef":89,"./timeunit":102,"./util":107}],91:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VL_ONLY_GUIDE_CONFIG = ['shortTimeLabels'];
},{}],92:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.axis = require("./axis");
exports.aggregate = require("./aggregate");
exports.bin = require("./bin");
exports.channel = require("./channel");
exports.compositeMark = require("./compositemark");
var compile_1 = require("./compile/compile");
exports.compile = compile_1.compile;
exports.config = require("./config");
exports.data = require("./data");
exports.datetime = require("./datetime");
exports.encoding = require("./encoding");
exports.facet = require("./facet");
exports.fieldDef = require("./fielddef");
exports.legend = require("./legend");
exports.mark = require("./mark");
exports.scale = require("./scale");
exports.sort = require("./sort");
exports.spec = require("./spec");
exports.stack = require("./stack");
exports.timeUnit = require("./timeunit");
exports.transform = require("./transform");
exports.type = require("./type");
exports.util = require("./util");
exports.validate = require("./validate");
exports.version = require('../package.json').version;
},{"../package.json":4,"./aggregate":5,"./axis":6,"./bin":7,"./channel":8,"./compile/compile":17,"./compositemark":83,"./config":84,"./data":85,"./datetime":86,"./encoding":87,"./facet":88,"./fielddef":89,"./legend":93,"./mark":96,"./scale":97,"./sort":99,"./spec":100,"./stack":101,"./timeunit":102,"./transform":105,"./type":106,"./util":107,"./validate":108}],93:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultLegendConfig = {};
exports.LEGEND_PROPERTIES = ['entryPadding', 'format', 'offset', 'orient', 'tickCount', 'title', 'type', 'values', 'zindex'];
exports.VG_LEGEND_PROPERTIES = [].concat(['fill', 'stroke', 'shape', 'size', 'opacity', 'encode'], exports.LEGEND_PROPERTIES);
},{}],94:[function(require,module,exports){
"use strict";
/**
* Vega-Lite's singleton logger utility.
*/
Object.defineProperty(exports, "__esModule", { value: true });
var vega_util_1 = require("vega-util");
/**
* Main (default) Vega Logger instance for Vega-Lite
*/
var main = vega_util_1.logger(vega_util_1.Warn);
var current = main;
/**
* Logger tool for checking if the code throws correct warning
*/
var LocalLogger = (function () {
function LocalLogger() {
this.warns = [];
this.infos = [];
this.debugs = [];
}
LocalLogger.prototype.level = function () {
return this;
};
LocalLogger.prototype.warn = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
(_a = this.warns).push.apply(_a, args);
return this;
var _a;
};
LocalLogger.prototype.info = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
(_a = this.infos).push.apply(_a, args);
return this;
var _a;
};
LocalLogger.prototype.debug = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
(_a = this.debugs).push.apply(_a, args);
return this;
var _a;
};
return LocalLogger;
}());
exports.LocalLogger = LocalLogger;
function runLocalLogger(f) {
var localLogger = current = new LocalLogger();
f(localLogger);
reset();
}
exports.runLocalLogger = runLocalLogger;
function wrap(f) {
return function () {
var logger = current = new LocalLogger();
f(logger);
reset();
};
}
exports.wrap = wrap;
/**
* Set the singleton logger to be a custom logger
*/
function set(logger) {
current = logger;
return current;
}
exports.set = set;
/**
* Reset the main logger to use the default Vega Logger
*/
function reset() {
current = main;
return current;
}
exports.reset = reset;
function warn() {
var _ = [];
for (var _i = 0; _i < arguments.length; _i++) {
_[_i] = arguments[_i];
}
current.warn.apply(current, arguments);
}
exports.warn = warn;
function info() {
var _ = [];
for (var _i = 0; _i < arguments.length; _i++) {
_[_i] = arguments[_i];
}
current.info.apply(current, arguments);
}
exports.info = info;
function debug() {
var _ = [];
for (var _i = 0; _i < arguments.length; _i++) {
_[_i] = arguments[_i];
}
current.debug.apply(current, arguments);
}
exports.debug = debug;
/**
* Collection of all Vega-Lite Error Messages
*/
var message;
(function (message) {
message.INVALID_SPEC = 'Invalid spec';
// SELECTION
function cannotProjectOnChannelWithoutField(channel) {
return "Cannot project a selection on encoding channel " + channel + ", which has no field.";
}
message.cannotProjectOnChannelWithoutField = cannotProjectOnChannelWithoutField;
function selectionNotFound(name) {
return "Cannot find a selection named \"" + name + "\"";
}
message.selectionNotFound = selectionNotFound;
// REPEAT
function noSuchRepeatedValue(field) {
return "Unknown repeated value \"" + field + "\".";
}
message.noSuchRepeatedValue = noSuchRepeatedValue;
// DATA
function unrecognizedParse(p) {
return "Unrecognized parse " + p + ".";
}
message.unrecognizedParse = unrecognizedParse;
function differentParse(field, local, ancestor) {
return "An ancestor parsed field " + field + " as " + ancestor + " but a child wants to parse the field as " + local + ".";
}
message.differentParse = differentParse;
// TRANSFORMS
function invalidTransformIgnored(transform) {
return "Ignoring an invalid transform: " + JSON.stringify(transform) + ".";
}
message.invalidTransformIgnored = invalidTransformIgnored;
message.NO_FIELDS_NEEDS_AS = 'If `from.fields` is not specified, `as` has to be a string that specifies the key to be used for the the data from the secondary source.';
// ENCODING & FACET
function invalidFieldType(type) {
return "Invalid field type \"" + type + "\"";
}
message.invalidFieldType = invalidFieldType;
function invalidFieldTypeForCountAggregate(type, aggregate) {
return "Invalid field type \"" + type + "\" for aggregate: \"" + aggregate + "\", using \"quantitative\" instead.";
}
message.invalidFieldTypeForCountAggregate = invalidFieldTypeForCountAggregate;
function invalidFieldTypeForBin(type) {
return "Invalid field type \"" + type + "\" for bin, using \"quantitative\" instead.";
}
message.invalidFieldTypeForBin = invalidFieldTypeForBin;
function invalidAggregate(aggregate) {
return "Invalid aggregation operator \"" + aggregate + "\"";
}
message.invalidAggregate = invalidAggregate;
function emptyOrInvalidFieldType(type, channel, newType) {
return "Invalid field type (" + type + ") for channel " + channel + ", using " + newType + " instead.";
}
message.emptyOrInvalidFieldType = emptyOrInvalidFieldType;
function emptyFieldDef(fieldDef, channel) {
return "Dropping " + JSON.stringify(fieldDef) + " from channel " + channel + " since it does not contain data field or value.";
}
message.emptyFieldDef = emptyFieldDef;
function incompatibleChannel(channel, markOrFacet, when) {
return channel + " dropped as it is incompatible with " + markOrFacet + (when ? " when " + when : '') + ".";
}
message.incompatibleChannel = incompatibleChannel;
function facetChannelShouldBeDiscrete(channel) {
return channel + " encoding should be discrete (ordinal / nominal / binned).";
}
message.facetChannelShouldBeDiscrete = facetChannelShouldBeDiscrete;
function discreteChannelCannotEncode(channel, type) {
return "Using discrete channel " + channel + " to encode " + type + " field can be misleading as it does not encode " + (type === 'ordinal' ? 'order' : 'magnitude') + ".";
}
message.discreteChannelCannotEncode = discreteChannelCannotEncode;
// Mark
message.BAR_WITH_POINT_SCALE_AND_RANGESTEP_NULL = 'Bar mark should not be used with point scale when rangeStep is null. Please use band scale instead.';
function unclearOrientContinuous(mark) {
return "Cannot clearly determine orientation for " + mark + " since both x and y channel encode continous fields. In this case, we use vertical by default";
}
message.unclearOrientContinuous = unclearOrientContinuous;
function unclearOrientDiscreteOrEmpty(mark) {
return "Cannot clearly determine orientation for " + mark + " since both x and y channel encode discrete or empty fields.";
}
message.unclearOrientDiscreteOrEmpty = unclearOrientDiscreteOrEmpty;
function orientOverridden(original, actual) {
return "Specified orient " + original + " overridden with " + actual;
}
message.orientOverridden = orientOverridden;
// SCALE
message.CANNOT_UNION_CUSTOM_DOMAIN_WITH_FIELD_DOMAIN = 'custom domain scale cannot be unioned with default field-based domain';
function cannotUseScalePropertyWithNonColor(prop) {
return "Cannot use " + prop + " with non-color channel.";
}
message.cannotUseScalePropertyWithNonColor = cannotUseScalePropertyWithNonColor;
function unaggregateDomainHasNoEffectForRawField(fieldDef) {
return "Using unaggregated domain with raw field has no effect (" + JSON.stringify(fieldDef) + ").";
}
message.unaggregateDomainHasNoEffectForRawField = unaggregateDomainHasNoEffectForRawField;
function unaggregateDomainWithNonSharedDomainOp(aggregate) {
return "Unaggregated domain not applicable for " + aggregate + " since it produces values outside the origin domain of the source data.";
}
message.unaggregateDomainWithNonSharedDomainOp = unaggregateDomainWithNonSharedDomainOp;
function unaggregatedDomainWithLogScale(fieldDef) {
return "Unaggregated domain is currently unsupported for log scale (" + JSON.stringify(fieldDef) + ").";
}
message.unaggregatedDomainWithLogScale = unaggregatedDomainWithLogScale;
message.CANNOT_USE_RANGE_WITH_POSITION = 'Cannot use custom range with x or y channel. Please customize width, height, padding, or rangeStep instead.';
function cannotUseSizeFieldWithBandSize(positionChannel) {
return "Using size field when " + positionChannel + "-channel has a band scale is not supported.";
}
message.cannotUseSizeFieldWithBandSize = cannotUseSizeFieldWithBandSize;
function cannotApplySizeToNonOrientedMark(mark) {
return "Cannot apply size to non-oriented mark " + mark + ".";
}
message.cannotApplySizeToNonOrientedMark = cannotApplySizeToNonOrientedMark;
function rangeStepDropped(channel) {
return "rangeStep for " + channel + " is dropped as top-level " + (channel === 'x' ? 'width' : 'height') + " is provided.";
}
message.rangeStepDropped = rangeStepDropped;
function scaleTypeNotWorkWithChannel(channel, scaleType, defaultScaleType) {
return "Channel " + channel + " does not work with " + scaleType + " scale. We are using " + defaultScaleType + " scale instead.";
}
message.scaleTypeNotWorkWithChannel = scaleTypeNotWorkWithChannel;
function scaleTypeNotWorkWithFieldDef(scaleType, defaultScaleType) {
return "FieldDef does not work with " + scaleType + " scale. We are using " + defaultScaleType + " scale instead.";
}
message.scaleTypeNotWorkWithFieldDef = scaleTypeNotWorkWithFieldDef;
function scalePropertyNotWorkWithScaleType(scaleType, propName, channel) {
return channel + "-scale's \"" + propName + "\" is dropped as it does not work with " + scaleType + " scale.";
}
message.scalePropertyNotWorkWithScaleType = scalePropertyNotWorkWithScaleType;
function scaleTypeNotWorkWithMark(mark, scaleType) {
return "Scale type \"" + scaleType + "\" does not work with mark " + mark + ".";
}
message.scaleTypeNotWorkWithMark = scaleTypeNotWorkWithMark;
function mergeConflictingProperty(property, propertyOf, v1, v2) {
return "Conflicting " + propertyOf + " property " + property + " (" + v1 + " and " + v2 + "). Using " + v1 + ".";
}
message.mergeConflictingProperty = mergeConflictingProperty;
function independentScaleMeansIndependentGuide(channel) {
return "Setting the scale to be independent for " + channel + " means we also have to set the guide (axis or legend) to be independent.";
}
message.independentScaleMeansIndependentGuide = independentScaleMeansIndependentGuide;
function conflictedDomain(channel) {
return "Cannot set " + channel + "-scale's \"domain\" as it is binned. Please use \"bin\"'s \"extent\" instead.";
}
message.conflictedDomain = conflictedDomain;
function domainSortDropped(sort) {
return "Dropping sort property " + JSON.stringify(sort) + " as unioned domains only support boolean or op 'count'.";
}
message.domainSortDropped = domainSortDropped;
message.UNABLE_TO_MERGE_DOMAINS = 'Unable to merge domains';
message.MORE_THAN_ONE_SORT = 'Domains that should be unioned has conflicting sort properties. Sort will be set to true.';
// AXIS
message.INVALID_CHANNEL_FOR_AXIS = 'Invalid channel for axis.';
// STACK
function cannotStackRangedMark(channel) {
return "Cannot stack " + channel + " if there is already " + channel + "2";
}
message.cannotStackRangedMark = cannotStackRangedMark;
function cannotStackNonLinearScale(scaleType) {
return "Cannot stack non-linear scale (" + scaleType + ")";
}
message.cannotStackNonLinearScale = cannotStackNonLinearScale;
function cannotStackNonSummativeAggregate(aggregate) {
return "Cannot stack when the aggregate function is non-summative (" + aggregate + ")";
}
message.cannotStackNonSummativeAggregate = cannotStackNonSummativeAggregate;
// TIMEUNIT
function invalidTimeUnit(unitName, value) {
return "Invalid " + unitName + ": " + value;
}
message.invalidTimeUnit = invalidTimeUnit;
function dayReplacedWithDate(fullTimeUnit) {
return "Time unit \"" + fullTimeUnit + "\" is not supported. We are replacing it with " + fullTimeUnit.replace('day', 'date') + ".";
}
message.dayReplacedWithDate = dayReplacedWithDate;
function droppedDay(d) {
return "Dropping day from datetime " + JSON.stringify(d) + " as day cannot be combined with other units.";
}
message.droppedDay = droppedDay;
})(message = exports.message || (exports.message = {}));
},{"vega-util":116}],95:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function isLogicalOr(op) {
return !!op.or;
}
exports.isLogicalOr = isLogicalOr;
function isLogicalAnd(op) {
return !!op.and;
}
exports.isLogicalAnd = isLogicalAnd;
function isLogicalNot(op) {
return !!op.not;
}
exports.isLogicalNot = isLogicalNot;
function forEachLeave(op, fn) {
if (isLogicalNot(op)) {
forEachLeave(op.not, fn);
}
else if (isLogicalAnd(op)) {
for (var _i = 0, _a = op.and; _i < _a.length; _i++) {
var subop = _a[_i];
forEachLeave(subop, fn);
}
}
else if (isLogicalOr(op)) {
for (var _b = 0, _c = op.or; _b < _c.length; _b++) {
var subop = _c[_b];
forEachLeave(subop, fn);
}
}
else {
fn(op);
}
}
exports.forEachLeave = forEachLeave;
},{}],96:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = require("./util");
var Mark;
(function (Mark) {
Mark.AREA = 'area';
Mark.BAR = 'bar';
Mark.LINE = 'line';
Mark.POINT = 'point';
Mark.RECT = 'rect';
Mark.RULE = 'rule';
Mark.TEXT = 'text';
Mark.TICK = 'tick';
Mark.CIRCLE = 'circle';
Mark.SQUARE = 'square';
})(Mark = exports.Mark || (exports.Mark = {}));
exports.AREA = Mark.AREA;
exports.BAR = Mark.BAR;
exports.LINE = Mark.LINE;
exports.POINT = Mark.POINT;
exports.TEXT = Mark.TEXT;
exports.TICK = Mark.TICK;
exports.RECT = Mark.RECT;
exports.RULE = Mark.RULE;
exports.CIRCLE = Mark.CIRCLE;
exports.SQUARE = Mark.SQUARE;
exports.PRIMITIVE_MARKS = [exports.AREA, exports.BAR, exports.LINE, exports.POINT, exports.TEXT, exports.TICK, exports.RECT, exports.RULE, exports.CIRCLE, exports.SQUARE];
function isMarkDef(mark) {
return mark['type'];
}
exports.isMarkDef = isMarkDef;
var PRIMITIVE_MARK_INDEX = util_1.toSet(exports.PRIMITIVE_MARKS);
function isPrimitiveMark(mark) {
var markType = isMarkDef(mark) ? mark.type : mark;
return markType in PRIMITIVE_MARK_INDEX;
}
exports.isPrimitiveMark = isPrimitiveMark;
exports.STROKE_CONFIG = ['stroke', 'strokeWidth',
'strokeDash', 'strokeDashOffset', 'strokeOpacity'];
exports.FILL_CONFIG = ['fill', 'fillOpacity'];
exports.FILL_STROKE_CONFIG = [].concat(exports.STROKE_CONFIG, exports.FILL_CONFIG);
exports.VL_ONLY_MARK_CONFIG_PROPERTIES = ['filled', 'color'];
exports.VL_ONLY_MARK_SPECIFIC_CONFIG_PROPERTY_INDEX = {
bar: ['binSpacing', 'continuousBandSize', 'discreteBandSize'],
text: ['shortTimeLabels'],
tick: ['bandSize', 'thickness']
};
exports.defaultMarkConfig = {
color: '#4c78a8',
};
exports.defaultBarConfig = {
binSpacing: 1,
continuousBandSize: 2
};
exports.defaultTickConfig = {
thickness: 1
};
},{"./util":107}],97:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var log = require("./log");
var util_1 = require("./util");
var ScaleType;
(function (ScaleType) {
// Continuous - Quantitative
ScaleType.LINEAR = 'linear';
ScaleType.BIN_LINEAR = 'bin-linear';
ScaleType.LOG = 'log';
ScaleType.POW = 'pow';
ScaleType.SQRT = 'sqrt';
// Continuous - Time
ScaleType.TIME = 'time';
ScaleType.UTC = 'utc';
// sequential
ScaleType.SEQUENTIAL = 'sequential';
// Quantile, Quantize, threshold
ScaleType.QUANTILE = 'quantile';
ScaleType.QUANTIZE = 'quantize';
ScaleType.THRESHOLD = 'threshold';
ScaleType.ORDINAL = 'ordinal';
ScaleType.BIN_ORDINAL = 'bin-ordinal';
ScaleType.POINT = 'point';
ScaleType.BAND = 'band';
})(ScaleType = exports.ScaleType || (exports.ScaleType = {}));
exports.SCALE_TYPES = [
// Continuous - Quantitative
'linear', 'bin-linear', 'log', 'pow', 'sqrt',
// Continuous - Time
'time', 'utc',
// Sequential
'sequential',
// Discrete
'ordinal', 'bin-ordinal', 'point', 'band',
];
/**
* Index for scale categories -- only scale of the same categories can be merged together.
* Current implementation is trying to be conservative and avoid merging scale type that might not work together
*/
var SCALE_CATEGORY_INDEX = {
linear: 'numeric',
log: 'numeric',
pow: 'numeric',
sqrt: 'numeric',
'bin-linear': 'bin-linear',
time: 'time',
utc: 'time',
sequential: 'sequential',
ordinal: 'ordinal',
'bin-ordinal': 'bin-ordinal',
point: 'ordinal-position',
band: 'ordinal-position'
};
function getScaleCategory(scaleType) {
return SCALE_CATEGORY_INDEX[scaleType];
}
exports.getScaleCategory = getScaleCategory;
/**
* Whether the two given scale types can be merged together.
*/
function scaleCompatible(scaleType1, scaleType2) {
return SCALE_CATEGORY_INDEX[scaleType1] === SCALE_CATEGORY_INDEX[scaleType2];
}
exports.scaleCompatible = scaleCompatible;
/**
* Index for scale predecence -- high score = higher priority for merging.
*/
var SCALE_PRECEDENCE_INDEX = {
// numeric
linear: 0,
log: 1,
pow: 1,
sqrt: 1,
// time
time: 0,
utc: 0,
// ordinal-position
point: 0,
band: 1,
// non grouped types
'bin-linear': 0,
sequential: 0,
ordinal: 0,
'bin-ordinal': 0,
};
/**
* Return scale categories -- only scale of the same categories can be merged together.
*/
function scaleTypePrecedence(scaleType) {
return SCALE_PRECEDENCE_INDEX[scaleType];
}
exports.scaleTypePrecedence = scaleTypePrecedence;
exports.CONTINUOUS_TO_CONTINUOUS_SCALES = ['linear', 'bin-linear', 'log', 'pow', 'sqrt', 'time', 'utc'];
var CONTINUOUS_TO_CONTINUOUS_INDEX = util_1.toSet(exports.CONTINUOUS_TO_CONTINUOUS_SCALES);
exports.CONTINUOUS_DOMAIN_SCALES = exports.CONTINUOUS_TO_CONTINUOUS_SCALES.concat(['sequential' /* TODO add 'quantile', 'quantize', 'threshold'*/]);
var CONTINUOUS_DOMAIN_INDEX = util_1.toSet(exports.CONTINUOUS_DOMAIN_SCALES);
exports.DISCRETE_DOMAIN_SCALES = ['ordinal', 'bin-ordinal', 'point', 'band'];
var DISCRETE_DOMAIN_INDEX = util_1.toSet(exports.DISCRETE_DOMAIN_SCALES);
var BIN_SCALES_INDEX = util_1.toSet(['bin-linear', 'bin-ordinal']);
exports.TIME_SCALE_TYPES = ['time', 'utc'];
function hasDiscreteDomain(type) {
return type in DISCRETE_DOMAIN_INDEX;
}
exports.hasDiscreteDomain = hasDiscreteDomain;
function isBinScale(type) {
return type in BIN_SCALES_INDEX;
}
exports.isBinScale = isBinScale;
function hasContinuousDomain(type) {
return type in CONTINUOUS_DOMAIN_INDEX;
}
exports.hasContinuousDomain = hasContinuousDomain;
function isContinuousToContinuous(type) {
return type in CONTINUOUS_TO_CONTINUOUS_INDEX;
}
exports.isContinuousToContinuous = isContinuousToContinuous;
exports.defaultScaleConfig = {
round: true,
textXRangeStep: 90,
rangeStep: 21,
pointPadding: 0.5,
bandPaddingInner: 0.1,
facetSpacing: 16,
minFontSize: 8,
maxFontSize: 40,
minOpacity: 0.3,
maxOpacity: 0.8,
// FIXME: revise if these *can* become ratios of rangeStep
minSize: 9,
minStrokeWidth: 1,
maxStrokeWidth: 4,
shapes: ['circle', 'square', 'cross', 'diamond', 'triangle-up', 'triangle-down']
};
function isExtendedScheme(scheme) {
return scheme && !!scheme['name'];
}
exports.isExtendedScheme = isExtendedScheme;
function isSelectionDomain(domain) {
return domain && domain['selection'];
}
exports.isSelectionDomain = isSelectionDomain;
exports.NON_TYPE_DOMAIN_RANGE_VEGA_SCALE_PROPERTIES = [
'reverse', 'round',
// quantitative / time
'clamp', 'nice',
// quantitative
'exponent', 'interpolate', 'zero',
// ordinal
'padding', 'paddingInner', 'paddingOuter',
];
exports.SCALE_PROPERTIES = [].concat([
'type', 'domain',
'range', 'rangeStep', 'scheme'
], exports.NON_TYPE_DOMAIN_RANGE_VEGA_SCALE_PROPERTIES);
function scaleTypeSupportProperty(scaleType, propName) {
switch (propName) {
case 'type':
case 'domain':
case 'reverse':
case 'range':
case 'scheme':
return true;
case 'interpolate':
return util_1.contains(['linear', 'bin-linear', 'pow', 'log', 'sqrt', 'utc', 'time'], scaleType);
case 'round':
return isContinuousToContinuous(scaleType) || scaleType === 'band' || scaleType === 'point';
case 'rangeStep':
case 'padding':
case 'paddingOuter':
return util_1.contains(['point', 'band'], scaleType);
case 'paddingInner':
return scaleType === 'band';
case 'clamp':
return isContinuousToContinuous(scaleType) || scaleType === 'sequential';
case 'nice':
return isContinuousToContinuous(scaleType) || scaleType === 'sequential' || scaleType === 'quantize';
case 'exponent':
return scaleType === 'pow' || scaleType === 'log';
case 'zero':
// TODO: what about quantize, threshold?
return scaleType === 'bin-ordinal' || (!hasDiscreteDomain(scaleType) && !util_1.contains(['log', 'time', 'utc', 'bin-linear'], scaleType));
}
/* istanbul ignore next: should never reach here*/
throw new Error("Invalid scale property " + propName + ".");
}
exports.scaleTypeSupportProperty = scaleTypeSupportProperty;
/**
* Returns undefined if the input channel supports the input scale property name
*/
function channelScalePropertyIncompatability(channel, propName) {
switch (propName) {
case 'range':
// User should not customize range for position and facet channel directly.
if (channel === 'x' || channel === 'y') {
return log.message.CANNOT_USE_RANGE_WITH_POSITION;
}
return undefined; // GOOD!
case 'interpolate':
case 'scheme':
if (channel !== 'color') {
return log.message.cannotUseScalePropertyWithNonColor(channel);
}
return undefined;
case 'type':
case 'domain':
case 'exponent':
case 'nice':
case 'padding':
case 'paddingInner':
case 'paddingOuter':
case 'rangeStep':
case 'reverse':
case 'round':
case 'clamp':
case 'zero':
return undefined; // GOOD!
}
/* istanbul ignore next: it should never reach here */
throw new Error('Invalid scale property "${propName}".');
}
exports.channelScalePropertyIncompatability = channelScalePropertyIncompatability;
},{"./log":94,"./util":107}],98:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SELECTION_ID = '_vgsid_';
exports.defaultConfig = {
single: { on: 'click', fields: [exports.SELECTION_ID], resolve: 'global' },
multi: { on: 'click', fields: [exports.SELECTION_ID], toggle: 'event.shiftKey', resolve: 'global' },
interval: {
on: '[mousedown, window:mouseup] > window:mousemove!',
encodings: ['x', 'y'],
translate: '[mousedown, window:mouseup] > window:mousemove!',
zoom: 'wheel!',
mark: { fill: '#333', fillOpacity: 0.125, stroke: 'white' },
resolve: 'global'
}
};
},{}],99:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function isSortField(sort) {
return !!sort && (sort['op'] === 'count' || !!sort['field']) && !!sort['op'];
}
exports.isSortField = isSortField;
},{}],100:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var channel_1 = require("./channel");
var compositeMark = require("./compositemark");
var encoding_1 = require("./encoding");
var vlEncoding = require("./encoding");
var log = require("./log");
var mark_1 = require("./mark");
var stack_1 = require("./stack");
var util_1 = require("./util");
/* Custom type guards */
function isFacetSpec(spec) {
return spec['facet'] !== undefined;
}
exports.isFacetSpec = isFacetSpec;
function isUnitSpec(spec) {
return !!spec['mark'];
}
exports.isUnitSpec = isUnitSpec;
function isLayerSpec(spec) {
return spec['layer'] !== undefined;
}
exports.isLayerSpec = isLayerSpec;
function isRepeatSpec(spec) {
return spec['repeat'] !== undefined;
}
exports.isRepeatSpec = isRepeatSpec;
function isConcatSpec(spec) {
return isVConcatSpec(spec) || isHConcatSpec(spec);
}
exports.isConcatSpec = isConcatSpec;
function isVConcatSpec(spec) {
return spec['vconcat'] !== undefined;
}
exports.isVConcatSpec = isVConcatSpec;
function isHConcatSpec(spec) {
return spec['hconcat'] !== undefined;
}
exports.isHConcatSpec = isHConcatSpec;
/**
* Decompose extended unit specs into composition of pure unit specs.
*/
// TODO: consider moving this to another file. Maybe vl.spec.normalize or vl.normalize
function normalize(spec, config) {
if (isFacetSpec(spec)) {
return normalizeFacet(spec, config);
}
if (isLayerSpec(spec)) {
return normalizeLayer(spec, config);
}
if (isRepeatSpec(spec)) {
return normalizeRepeat(spec, config);
}
if (isVConcatSpec(spec)) {
return normalizeVConcat(spec, config);
}
if (isHConcatSpec(spec)) {
return normalizeHConcat(spec, config);
}
if (isUnitSpec(spec)) {
var hasRow = encoding_1.channelHasField(spec.encoding, channel_1.ROW);
var hasColumn = encoding_1.channelHasField(spec.encoding, channel_1.COLUMN);
if (hasRow || hasColumn) {
return normalizeFacetedUnit(spec, config);
}
return normalizeNonFacetUnit(spec, config);
}
throw new Error(log.message.INVALID_SPEC);
}
exports.normalize = normalize;
function normalizeFacet(spec, config) {
var subspec = spec.spec, rest = tslib_1.__rest(spec, ["spec"]);
return tslib_1.__assign({}, rest, { spec: normalize(subspec, config) });
}
function normalizeLayer(spec, config) {
var layer = spec.layer, rest = tslib_1.__rest(spec, ["layer"]);
return tslib_1.__assign({}, rest, { layer: layer.map(function (subspec) { return isLayerSpec(subspec) ? normalizeLayer(subspec, config) : normalizeNonFacetUnit(subspec, config); }) });
}
function normalizeRepeat(spec, config) {
var subspec = spec.spec, rest = tslib_1.__rest(spec, ["spec"]);
return tslib_1.__assign({}, rest, { spec: normalize(subspec, config) });
}
function normalizeVConcat(spec, config) {
var vconcat = spec.vconcat, rest = tslib_1.__rest(spec, ["vconcat"]);
return tslib_1.__assign({}, rest, { vconcat: vconcat.map(function (subspec) { return normalize(subspec, config); }) });
}
function normalizeHConcat(spec, config) {
var hconcat = spec.hconcat, rest = tslib_1.__rest(spec, ["hconcat"]);
return tslib_1.__assign({}, rest, { hconcat: hconcat.map(function (subspec) { return normalize(subspec, config); }) });
}
function normalizeFacetedUnit(spec, config) {
// New encoding in the inside spec should not contain row / column
// as row/column should be moved to facet
var _a = spec.encoding, row = _a.row, column = _a.column, encoding = tslib_1.__rest(_a, ["row", "column"]);
// Mark and encoding should be moved into the inner spec
var mark = spec.mark, width = spec.width, height = spec.height, selection = spec.selection, _ = spec.encoding, outerSpec = tslib_1.__rest(spec, ["mark", "width", "height", "selection", "encoding"]);
return tslib_1.__assign({}, outerSpec, { facet: tslib_1.__assign({}, (row ? { row: row } : {}), (column ? { column: column } : {})), spec: normalizeNonFacetUnit(tslib_1.__assign({ mark: mark }, (width ? { width: width } : {}), (height ? { height: height } : {}), { encoding: encoding }, (selection ? { selection: selection } : {})), config) });
}
function isNonFacetUnitSpecWithPrimitiveMark(spec) {
return mark_1.isPrimitiveMark(spec.mark);
}
function normalizeNonFacetUnit(spec, config) {
if (isNonFacetUnitSpecWithPrimitiveMark(spec)) {
// TODO: thoroughly test
if (encoding_1.isRanged(spec.encoding)) {
return normalizeRangedUnit(spec);
}
var overlayConfig = config && config.overlay;
var overlayWithLine = overlayConfig && spec.mark === mark_1.AREA &&
util_1.contains(['linepoint', 'line'], overlayConfig.area);
var overlayWithPoint = overlayConfig && ((overlayConfig.line && spec.mark === mark_1.LINE) ||
(overlayConfig.area === 'linepoint' && spec.mark === mark_1.AREA));
// TODO: consider moving this to become another case of compositeMark
if (overlayWithPoint || overlayWithLine) {
return normalizeOverlay(spec, overlayWithPoint, overlayWithLine, config);
}
return spec; // Nothing to normalize
}
else {
return compositeMark.normalize(spec, config);
}
}
function normalizeRangedUnit(spec) {
var hasX = encoding_1.channelHasField(spec.encoding, channel_1.X);
var hasY = encoding_1.channelHasField(spec.encoding, channel_1.Y);
var hasX2 = encoding_1.channelHasField(spec.encoding, channel_1.X2);
var hasY2 = encoding_1.channelHasField(spec.encoding, channel_1.Y2);
if ((hasX2 && !hasX) || (hasY2 && !hasY)) {
var normalizedSpec = util_1.duplicate(spec);
if (hasX2 && !hasX) {
normalizedSpec.encoding.x = normalizedSpec.encoding.x2;
delete normalizedSpec.encoding.x2;
}
if (hasY2 && !hasY) {
normalizedSpec.encoding.y = normalizedSpec.encoding.y2;
delete normalizedSpec.encoding.y2;
}
return normalizedSpec;
}
return spec;
}
// FIXME(#1804): re-design this
function normalizeOverlay(spec, overlayWithPoint, overlayWithLine, config) {
var mark = spec.mark, selection = spec.selection, encoding = spec.encoding, outerSpec = tslib_1.__rest(spec, ["mark", "selection", "encoding"]);
var layer = [{ mark: mark, encoding: encoding }];
// Need to copy stack config to overlayed layer
var stackProps = stack_1.stack(mark, encoding, config ? config.stack : undefined);
var overlayEncoding = encoding;
if (stackProps) {
var stackFieldChannel = stackProps.fieldChannel, offset = stackProps.offset;
overlayEncoding = tslib_1.__assign({}, encoding, (_a = {}, _a[stackFieldChannel] = tslib_1.__assign({}, encoding[stackFieldChannel], (offset ? { stack: offset } : {})), _a));
}
if (overlayWithLine) {
layer.push(tslib_1.__assign({ mark: {
type: 'line',
style: 'lineOverlay'
} }, (selection ? { selection: selection } : {}), { encoding: overlayEncoding }));
}
if (overlayWithPoint) {
layer.push(tslib_1.__assign({ mark: {
type: 'point',
filled: true,
style: 'pointOverlay'
} }, (selection ? { selection: selection } : {}), { encoding: overlayEncoding }));
}
return tslib_1.__assign({}, outerSpec, { layer: layer });
var _a;
}
// TODO: add vl.spec.validate & move stuff from vl.validate to here
/* Accumulate non-duplicate fieldDefs in a dictionary */
function accumulate(dict, fieldDefs) {
fieldDefs.forEach(function (fieldDef) {
// Consider only pure fieldDef properties (ignoring scale, axis, legend)
var pureFieldDef = ['field', 'type', 'value', 'timeUnit', 'bin', 'aggregate'].reduce(function (f, key) {
if (fieldDef[key] !== undefined) {
f[key] = fieldDef[key];
}
return f;
}, {});
var key = util_1.hash(pureFieldDef);
dict[key] = dict[key] || fieldDef;
});
return dict;
}
/* Recursively get fieldDefs from a spec, returns a dictionary of fieldDefs */
function fieldDefIndex(spec, dict) {
if (dict === void 0) { dict = {}; }
// FIXME(https://github.com/vega/vega-lite/issues/2207): Support fieldDefIndex for repeat
if (isLayerSpec(spec)) {
spec.layer.forEach(function (layer) {
if (isUnitSpec(layer)) {
accumulate(dict, vlEncoding.fieldDefs(layer.encoding));
}
else {
fieldDefIndex(layer, dict);
}
});
}
else if (isFacetSpec(spec)) {
accumulate(dict, vlEncoding.fieldDefs(spec.facet));
fieldDefIndex(spec.spec, dict);
}
else if (isRepeatSpec(spec)) {
fieldDefIndex(spec.spec, dict);
}
else if (isConcatSpec(spec)) {
var childSpec = isVConcatSpec(spec) ? spec.vconcat : spec.hconcat;
childSpec.forEach(function (child) { return fieldDefIndex(child, dict); });
}
else {
accumulate(dict, vlEncoding.fieldDefs(spec.encoding));
}
return dict;
}
/* Returns all non-duplicate fieldDefs in a spec in a flat array */
function fieldDefs(spec) {
return util_1.vals(fieldDefIndex(spec));
}
exports.fieldDefs = fieldDefs;
function isStacked(spec, config) {
config = config || spec.config;
if (mark_1.isPrimitiveMark(spec.mark)) {
return stack_1.stack(spec.mark, spec.encoding, config ? config.stack : undefined) !== null;
}
return false;
}
exports.isStacked = isStacked;
},{"./channel":8,"./compositemark":83,"./encoding":87,"./log":94,"./mark":96,"./stack":101,"./util":107,"tslib":114}],101:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var aggregate_1 = require("./aggregate");
var channel_1 = require("./channel");
var encoding_1 = require("./encoding");
var fielddef_1 = require("./fielddef");
var log = require("./log");
var mark_1 = require("./mark");
var scale_1 = require("./scale");
var util_1 = require("./util");
exports.STACKABLE_MARKS = [mark_1.BAR, mark_1.AREA, mark_1.RULE, mark_1.POINT, mark_1.CIRCLE, mark_1.SQUARE, mark_1.LINE, mark_1.TEXT, mark_1.TICK];
exports.STACK_BY_DEFAULT_MARKS = [mark_1.BAR, mark_1.AREA];
// Note: CompassQL uses this method and only pass in required properties of each argument object.
// If required properties change, make sure to update CompassQL.
function stack(m, encoding, stackConfig) {
var mark = mark_1.isMarkDef(m) ? m.type : m;
// Should have stackable mark
if (!util_1.contains(exports.STACKABLE_MARKS, mark)) {
return null;
}
// Should be aggregate plot
if (!encoding_1.isAggregate(encoding)) {
return null;
}
// Should have grouping level of detail
var stackBy = channel_1.STACK_BY_CHANNELS.reduce(function (sc, channel) {
if (encoding_1.channelHasField(encoding, channel)) {
var channelDef = encoding[channel];
(util_1.isArray(channelDef) ? channelDef : [channelDef]).forEach(function (cDef) {
var fieldDef = fielddef_1.getFieldDef(cDef);
if (!fieldDef.aggregate) {
sc.push({
channel: channel,
fieldDef: fieldDef
});
}
});
}
return sc;
}, []);
if (stackBy.length === 0) {
return null;
}
// Has only one aggregate axis
var hasXField = fielddef_1.isFieldDef(encoding.x);
var hasYField = fielddef_1.isFieldDef(encoding.y);
var xIsAggregate = fielddef_1.isFieldDef(encoding.x) && !!encoding.x.aggregate;
var yIsAggregate = fielddef_1.isFieldDef(encoding.y) && !!encoding.y.aggregate;
if (xIsAggregate !== yIsAggregate) {
var fieldChannel = xIsAggregate ? channel_1.X : channel_1.Y;
var fieldDef = encoding[fieldChannel];
var fieldChannelAggregate = fieldDef.aggregate;
var fieldChannelScale = fieldDef.scale;
var stackOffset = null;
if (fieldDef.stack !== undefined) {
stackOffset = fieldDef.stack;
}
else if (util_1.contains(exports.STACK_BY_DEFAULT_MARKS, mark)) {
// Bar and Area with sum ops are automatically stacked by default
stackOffset = stackConfig === undefined ? 'zero' : stackConfig;
}
else {
stackOffset = stackConfig;
}
if (!stackOffset || stackOffset === 'none') {
return null;
}
// If stacked, check if it qualifies for stacking (and log warning if not qualified.)
if (fieldChannelScale && fieldChannelScale.type && fieldChannelScale.type !== scale_1.ScaleType.LINEAR) {
log.warn(log.message.cannotStackNonLinearScale(fieldChannelScale.type));
return null;
}
if (encoding_1.channelHasField(encoding, fieldChannel === channel_1.X ? channel_1.X2 : channel_1.Y2)) {
log.warn(log.message.cannotStackRangedMark(fieldChannel));
return null;
}
if (!util_1.contains(aggregate_1.SUM_OPS, fieldChannelAggregate)) {
log.warn(log.message.cannotStackNonSummativeAggregate(fieldChannelAggregate));
return null;
}
return {
groupbyChannel: xIsAggregate ? (hasYField ? channel_1.Y : null) : (hasXField ? channel_1.X : null),
fieldChannel: fieldChannel,
impute: util_1.contains(['area', 'line'], mark),
stackBy: stackBy,
offset: stackOffset
};
}
return null;
}
exports.stack = stack;
},{"./aggregate":5,"./channel":8,"./encoding":87,"./fielddef":89,"./log":94,"./mark":96,"./scale":97,"./util":107}],102:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var datetime_1 = require("./datetime");
var log = require("./log");
var util_1 = require("./util");
var TimeUnit;
(function (TimeUnit) {
TimeUnit.YEAR = 'year';
TimeUnit.MONTH = 'month';
TimeUnit.DAY = 'day';
TimeUnit.DATE = 'date';
TimeUnit.HOURS = 'hours';
TimeUnit.MINUTES = 'minutes';
TimeUnit.SECONDS = 'seconds';
TimeUnit.MILLISECONDS = 'milliseconds';
TimeUnit.YEARMONTH = 'yearmonth';
TimeUnit.YEARMONTHDATE = 'yearmonthdate';
TimeUnit.YEARMONTHDATEHOURS = 'yearmonthdatehours';
TimeUnit.YEARMONTHDATEHOURSMINUTES = 'yearmonthdatehoursminutes';
TimeUnit.YEARMONTHDATEHOURSMINUTESSECONDS = 'yearmonthdatehoursminutesseconds';
// MONTHDATE always include 29 February since we use year 0th (which is a leap year);
TimeUnit.MONTHDATE = 'monthdate';
TimeUnit.HOURSMINUTES = 'hoursminutes';
TimeUnit.HOURSMINUTESSECONDS = 'hoursminutesseconds';
TimeUnit.MINUTESSECONDS = 'minutesseconds';
TimeUnit.SECONDSMILLISECONDS = 'secondsmilliseconds';
TimeUnit.QUARTER = 'quarter';
TimeUnit.YEARQUARTER = 'yearquarter';
TimeUnit.QUARTERMONTH = 'quartermonth';
TimeUnit.YEARQUARTERMONTH = 'yearquartermonth';
TimeUnit.UTCYEAR = 'utcyear';
TimeUnit.UTCMONTH = 'utcmonth';
TimeUnit.UTCDAY = 'utcday';
TimeUnit.UTCDATE = 'utcdate';
TimeUnit.UTCHOURS = 'utchours';
TimeUnit.UTCMINUTES = 'utcminutes';
TimeUnit.UTCSECONDS = 'utcseconds';
TimeUnit.UTCMILLISECONDS = 'utcmilliseconds';
TimeUnit.UTCYEARMONTH = 'utcyearmonth';
TimeUnit.UTCYEARMONTHDATE = 'utcyearmonthdate';
TimeUnit.UTCYEARMONTHDATEHOURS = 'utcyearmonthdatehours';
TimeUnit.UTCYEARMONTHDATEHOURSMINUTES = 'utcyearmonthdatehoursminutes';
TimeUnit.UTCYEARMONTHDATEHOURSMINUTESSECONDS = 'utcyearmonthdatehoursminutesseconds';
// MONTHDATE always include 29 February since we use year 0th (which is a leap year);
TimeUnit.UTCMONTHDATE = 'utcmonthdate';
TimeUnit.UTCHOURSMINUTES = 'utchoursminutes';
TimeUnit.UTCHOURSMINUTESSECONDS = 'utchoursminutesseconds';
TimeUnit.UTCMINUTESSECONDS = 'utcminutesseconds';
TimeUnit.UTCSECONDSMILLISECONDS = 'utcsecondsmilliseconds';
TimeUnit.UTCQUARTER = 'utcquarter';
TimeUnit.UTCYEARQUARTER = 'utcyearquarter';
TimeUnit.UTCQUARTERMONTH = 'utcquartermonth';
TimeUnit.UTCYEARQUARTERMONTH = 'utcyearquartermonth';
})(TimeUnit = exports.TimeUnit || (exports.TimeUnit = {}));
/** Time Unit that only corresponds to only one part of Date objects. */
exports.SINGLE_TIMEUNITS = [
TimeUnit.YEAR,
TimeUnit.QUARTER,
TimeUnit.MONTH,
TimeUnit.DAY,
TimeUnit.DATE,
TimeUnit.HOURS,
TimeUnit.MINUTES,
TimeUnit.SECONDS,
TimeUnit.MILLISECONDS
];
var SINGLE_TIMEUNIT_INDEX = exports.SINGLE_TIMEUNITS.reduce(function (d, timeUnit) {
d[timeUnit] = true;
return d;
}, {});
function isSingleTimeUnit(timeUnit) {
return !!SINGLE_TIMEUNIT_INDEX[timeUnit];
}
exports.isSingleTimeUnit = isSingleTimeUnit;
/**
* Converts a date to only have the measurements relevant to the specified unit
* i.e. ('yearmonth', '2000-12-04 07:58:14') -> '2000-12-01 00:00:00'
* Note: the base date is Jan 01 1900 00:00:00
*/
function convert(unit, date) {
var result = new Date(0, 0, 1, 0, 0, 0, 0); // start with uniform date
exports.SINGLE_TIMEUNITS.forEach(function (singleUnit) {
if (containsTimeUnit(unit, singleUnit)) {
switch (singleUnit) {
case TimeUnit.DAY:
throw new Error('Cannot convert to TimeUnits containing \'day\'');
case TimeUnit.YEAR:
result.setFullYear(date.getFullYear());
break;
case TimeUnit.QUARTER:
// indicate quarter by setting month to be the first of the quarter i.e. may (4) -> april (3)
result.setMonth((Math.floor(date.getMonth() / 3)) * 3);
break;
case TimeUnit.MONTH:
result.setMonth(date.getMonth());
break;
case TimeUnit.DATE:
result.setDate(date.getDate());
break;
case TimeUnit.HOURS:
result.setHours(date.getHours());
break;
case TimeUnit.MINUTES:
result.setMinutes(date.getMinutes());
break;
case TimeUnit.SECONDS:
result.setSeconds(date.getSeconds());
break;
case TimeUnit.MILLISECONDS:
result.setMilliseconds(date.getMilliseconds());
break;
}
}
});
return result;
}
exports.convert = convert;
exports.MULTI_TIMEUNITS = [
TimeUnit.YEARQUARTER,
TimeUnit.YEARQUARTERMONTH,
TimeUnit.YEARMONTH,
TimeUnit.YEARMONTHDATE,
TimeUnit.YEARMONTHDATEHOURS,
TimeUnit.YEARMONTHDATEHOURSMINUTES,
TimeUnit.YEARMONTHDATEHOURSMINUTESSECONDS,
TimeUnit.QUARTERMONTH,
TimeUnit.HOURSMINUTES,
TimeUnit.HOURSMINUTESSECONDS,
TimeUnit.MINUTESSECONDS,
TimeUnit.SECONDSMILLISECONDS,
];
var MULTI_TIMEUNIT_INDEX = exports.MULTI_TIMEUNITS.reduce(function (d, timeUnit) {
d[timeUnit] = true;
return d;
}, {});
function isMultiTimeUnit(timeUnit) {
return !!MULTI_TIMEUNIT_INDEX[timeUnit];
}
exports.isMultiTimeUnit = isMultiTimeUnit;
exports.TIMEUNITS = [
TimeUnit.YEAR,
TimeUnit.QUARTER,
TimeUnit.MONTH,
TimeUnit.DAY,
TimeUnit.DATE,
TimeUnit.HOURS,
TimeUnit.MINUTES,
TimeUnit.SECONDS,
TimeUnit.MILLISECONDS,
TimeUnit.YEARQUARTER,
TimeUnit.YEARQUARTERMONTH,
TimeUnit.YEARMONTH,
TimeUnit.YEARMONTHDATE,
TimeUnit.YEARMONTHDATEHOURS,
TimeUnit.YEARMONTHDATEHOURSMINUTES,
TimeUnit.YEARMONTHDATEHOURSMINUTESSECONDS,
TimeUnit.QUARTERMONTH,
TimeUnit.HOURSMINUTES,
TimeUnit.HOURSMINUTESSECONDS,
TimeUnit.MINUTESSECONDS,
TimeUnit.SECONDSMILLISECONDS,
];
/** Returns true if fullTimeUnit contains the timeUnit, false otherwise. */
function containsTimeUnit(fullTimeUnit, timeUnit) {
var index = fullTimeUnit.indexOf(timeUnit);
return index > -1 &&
(timeUnit !== TimeUnit.SECONDS ||
index === 0 ||
fullTimeUnit.charAt(index - 1) !== 'i' // exclude milliseconds
);
}
exports.containsTimeUnit = containsTimeUnit;
/**
* Returns Vega expresssion for a given timeUnit and fieldRef
*/
function fieldExpr(fullTimeUnit, field) {
var fieldRef = "datum[" + util_1.stringValue(field) + "]";
var utc = isUTCTimeUnit(fullTimeUnit) ? 'utc' : '';
function func(timeUnit) {
if (timeUnit === TimeUnit.QUARTER) {
// quarter starting at 0 (0,3,6,9).
return "(" + utc + "quarter(" + fieldRef + ")-1)";
}
else {
return "" + utc + timeUnit + "(" + fieldRef + ")";
}
}
var d = exports.SINGLE_TIMEUNITS.reduce(function (dateExpr, tu) {
if (containsTimeUnit(fullTimeUnit, tu)) {
dateExpr[tu] = func(tu);
}
return dateExpr;
}, {});
if (d.day && util_1.keys(d).length > 1) {
log.warn(log.message.dayReplacedWithDate(fullTimeUnit));
delete d.day;
d.date = func(TimeUnit.DATE);
}
return datetime_1.dateTimeExpr(d);
}
exports.fieldExpr = fieldExpr;
/** returns the smallest nice unit for scale.nice */
function smallestUnit(timeUnit) {
if (!timeUnit) {
return undefined;
}
if (containsTimeUnit(timeUnit, TimeUnit.SECONDS)) {
return 'second';
}
if (containsTimeUnit(timeUnit, TimeUnit.MINUTES)) {
return 'minute';
}
if (containsTimeUnit(timeUnit, TimeUnit.HOURS)) {
return 'hour';
}
if (containsTimeUnit(timeUnit, TimeUnit.DAY) ||
containsTimeUnit(timeUnit, TimeUnit.DATE)) {
return 'day';
}
if (containsTimeUnit(timeUnit, TimeUnit.MONTH)) {
return 'month';
}
if (containsTimeUnit(timeUnit, TimeUnit.YEAR)) {
return 'year';
}
return undefined;
}
exports.smallestUnit = smallestUnit;
/**
* returns the signal expression used for axis labels for a time unit
*/
function formatExpression(timeUnit, field, shortTimeLabels, isUTCScale) {
if (!timeUnit) {
return undefined;
}
var dateComponents = [];
var expression = '';
var hasYear = containsTimeUnit(timeUnit, TimeUnit.YEAR);
if (containsTimeUnit(timeUnit, TimeUnit.QUARTER)) {
// special expression for quarter as prefix
expression = "'Q' + quarter(" + field + ")";
}
if (containsTimeUnit(timeUnit, TimeUnit.MONTH)) {
// By default use short month name
dateComponents.push(shortTimeLabels !== false ? '%b' : '%B');
}
if (containsTimeUnit(timeUnit, TimeUnit.DAY)) {
dateComponents.push(shortTimeLabels ? '%a' : '%A');
}
else if (containsTimeUnit(timeUnit, TimeUnit.DATE)) {
dateComponents.push('%d' + (hasYear ? ',' : '')); // add comma if there is year
}
if (hasYear) {
dateComponents.push(shortTimeLabels ? '%y' : '%Y');
}
var timeComponents = [];
if (containsTimeUnit(timeUnit, TimeUnit.HOURS)) {
timeComponents.push('%H');
}
if (containsTimeUnit(timeUnit, TimeUnit.MINUTES)) {
timeComponents.push('%M');
}
if (containsTimeUnit(timeUnit, TimeUnit.SECONDS)) {
timeComponents.push('%S');
}
if (containsTimeUnit(timeUnit, TimeUnit.MILLISECONDS)) {
timeComponents.push('%L');
}
var dateTimeComponents = [];
if (dateComponents.length > 0) {
dateTimeComponents.push(dateComponents.join(' '));
}
if (timeComponents.length > 0) {
dateTimeComponents.push(timeComponents.join(':'));
}
if (dateTimeComponents.length > 0) {
if (expression) {
// Add space between quarter and main time format
expression += " + ' ' + ";
}
if (isUTCScale) {
expression += "utcFormat(" + field + ", '" + dateTimeComponents.join(' ') + "')";
}
else {
expression += "timeFormat(" + field + ", '" + dateTimeComponents.join(' ') + "')";
}
}
// If expression is still an empty string, return undefined instead.
return expression || undefined;
}
exports.formatExpression = formatExpression;
function isDiscreteByDefault(timeUnit) {
switch (timeUnit) {
// These time unit use discrete scale by default
case 'hours':
case 'day':
case 'month':
case 'quarter':
return true;
}
return false;
}
exports.isDiscreteByDefault = isDiscreteByDefault;
function isUTCTimeUnit(timeUnit) {
return timeUnit.substr(0, 3) === 'utc';
}
},{"./datetime":86,"./log":94,"./util":107}],103:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
function extractTitleConfig(titleConfig) {
var
// These are non-mark title config that need to be hardcoded
anchor = titleConfig.anchor, offset = titleConfig.offset, orient = titleConfig.orient,
// color needs to be redirect to fill
color = titleConfig.color,
// The rest are mark config.
titleMarkConfig = tslib_1.__rest(titleConfig, ["anchor", "offset", "orient", "color"]);
var mark = tslib_1.__assign({}, titleMarkConfig, color ? { fill: color } : {});
var nonMark = tslib_1.__assign({}, anchor ? { anchor: anchor } : {}, offset ? { offset: offset } : {}, orient ? { orient: orient } : {});
return { mark: mark, nonMark: nonMark };
}
exports.extractTitleConfig = extractTitleConfig;
},{"tslib":114}],104:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var TOP_LEVEL_PROPERTIES = [
'background', 'padding', 'autoResize'
];
function extractTopLevelProperties(t) {
return TOP_LEVEL_PROPERTIES.reduce(function (o, p) {
if (t && t[p] !== undefined) {
o[p] = t[p];
}
return o;
}, {});
}
exports.extractTopLevelProperties = extractTopLevelProperties;
},{}],105:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function isFilter(t) {
return t['filter'] !== undefined;
}
exports.isFilter = isFilter;
function isLookup(t) {
return t['lookup'] !== undefined;
}
exports.isLookup = isLookup;
function isCalculate(t) {
return t['calculate'] !== undefined;
}
exports.isCalculate = isCalculate;
function isBin(t) {
return t['bin'] !== undefined;
}
exports.isBin = isBin;
function isTimeUnit(t) {
return t['timeUnit'] !== undefined;
}
exports.isTimeUnit = isTimeUnit;
function isSummarize(t) {
return t['summarize'] !== undefined;
}
exports.isSummarize = isSummarize;
},{}],106:[function(require,module,exports){
"use strict";
/** Constants and utilities for data type */
/** Data type based on level of measurement */
Object.defineProperty(exports, "__esModule", { value: true });
var Type;
(function (Type) {
Type.QUANTITATIVE = 'quantitative';
Type.ORDINAL = 'ordinal';
Type.TEMPORAL = 'temporal';
Type.NOMINAL = 'nominal';
})(Type = exports.Type || (exports.Type = {}));
exports.QUANTITATIVE = Type.QUANTITATIVE;
exports.ORDINAL = Type.ORDINAL;
exports.TEMPORAL = Type.TEMPORAL;
exports.NOMINAL = Type.NOMINAL;
/**
* Get full, lowercase type name for a given type.
* @param type
* @return Full type name.
*/
function getFullName(type) {
if (type) {
type = type.toLowerCase();
switch (type) {
case 'q':
case exports.QUANTITATIVE:
return 'quantitative';
case 't':
case exports.TEMPORAL:
return 'temporal';
case 'o':
case exports.ORDINAL:
return 'ordinal';
case 'n':
case exports.NOMINAL:
return 'nominal';
}
}
// If we get invalid input, return undefined type.
return undefined;
}
exports.getFullName = getFullName;
},{}],107:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var stringify = require("json-stable-stringify");
var vega_util_1 = require("vega-util");
var logical_1 = require("./logical");
var vega_util_2 = require("vega-util");
exports.extend = vega_util_2.extend;
exports.isArray = vega_util_2.isArray;
exports.isObject = vega_util_2.isObject;
exports.isNumber = vega_util_2.isNumber;
exports.isString = vega_util_2.isString;
exports.truncate = vega_util_2.truncate;
exports.toSet = vega_util_2.toSet;
exports.stringValue = vega_util_2.stringValue;
/**
* Creates an object composed of the picked object properties.
*
* Example: (from lodash)
*
* var object = {'a': 1, 'b': '2', 'c': 3};
* pick(object, ['a', 'c']);
* // → {'a': 1, 'c': 3}
*
*/
function pick(obj, props) {
var copy = {};
props.forEach(function (prop) {
if (obj.hasOwnProperty(prop)) {
copy[prop] = obj[prop];
}
});
return copy;
}
exports.pick = pick;
/**
* The opposite of _.pick; this method creates an object composed of the own
* and inherited enumerable string keyed properties of object that are not omitted.
*/
function omit(obj, props) {
var copy = duplicate(obj);
props.forEach(function (prop) {
delete copy[prop];
});
return copy;
}
exports.omit = omit;
function hash(a) {
if (vega_util_1.isString(a) || vega_util_1.isNumber(a) || isBoolean(a)) {
return String(a);
}
return stringify(a);
}
exports.hash = hash;
function contains(array, item) {
return array.indexOf(item) > -1;
}
exports.contains = contains;
/** Returns the array without the elements in item */
function without(array, excludedItems) {
return array.filter(function (item) { return !contains(excludedItems, item); });
}
exports.without = without;
function union(array, other) {
return array.concat(without(other, array));
}
exports.union = union;
/**
* Returns true if any item returns true.
*/
function some(arr, f) {
var i = 0;
for (var k = 0; k < arr.length; k++) {
if (f(arr[k], k, i++)) {
return true;
}
}
return false;
}
exports.some = some;
/**
* Returns true if all items return true.
*/
function every(arr, f) {
var i = 0;
for (var k = 0; k < arr.length; k++) {
if (!f(arr[k], k, i++)) {
return false;
}
}
return true;
}
exports.every = every;
function flatten(arrays) {
return [].concat.apply([], arrays);
}
exports.flatten = flatten;
/**
* recursively merges src into dest
*/
function mergeDeep(dest) {
var src = [];
for (var _i = 1; _i < arguments.length; _i++) {
src[_i - 1] = arguments[_i];
}
for (var _a = 0, src_1 = src; _a < src_1.length; _a++) {
var s = src_1[_a];
dest = deepMerge_(dest, s);
}
return dest;
}
exports.mergeDeep = mergeDeep;
// recursively merges src into dest
function deepMerge_(dest, src) {
if (typeof src !== 'object' || src === null) {
return dest;
}
for (var p in src) {
if (!src.hasOwnProperty(p)) {
continue;
}
if (src[p] === undefined) {
continue;
}
if (typeof src[p] !== 'object' || vega_util_1.isArray(src[p]) || src[p] === null) {
dest[p] = src[p];
}
else if (typeof dest[p] !== 'object' || dest[p] === null) {
dest[p] = mergeDeep(vega_util_1.isArray(src[p].constructor) ? [] : {}, src[p]);
}
else {
mergeDeep(dest[p], src[p]);
}
}
return dest;
}
function unique(values, f) {
var results = [];
var u = {};
var v;
for (var _i = 0, values_1 = values; _i < values_1.length; _i++) {
var val = values_1[_i];
v = f(val);
if (v in u) {
continue;
}
u[v] = 1;
results.push(val);
}
return results;
}
exports.unique = unique;
/**
* Returns true if the two dictionaries disagree. Applies only to defined values.
*/
function differ(dict, other) {
for (var key in dict) {
if (dict.hasOwnProperty(key)) {
if (other[key] && dict[key] && other[key] !== dict[key]) {
return true;
}
}
}
return false;
}
exports.differ = differ;
function hasIntersection(a, b) {
for (var key in a) {
if (key in b) {
return true;
}
}
return false;
}
exports.hasIntersection = hasIntersection;
function differArray(array, other) {
if (array.length !== other.length) {
return true;
}
array.sort();
other.sort();
for (var i = 0; i < array.length; i++) {
if (other[i] !== array[i]) {
return true;
}
}
return false;
}
exports.differArray = differArray;
exports.keys = Object.keys;
function vals(x) {
var _vals = [];
for (var k in x) {
if (x.hasOwnProperty(k)) {
_vals.push(x[k]);
}
}
return _vals;
}
exports.vals = vals;
function duplicate(obj) {
return JSON.parse(JSON.stringify(obj));
}
exports.duplicate = duplicate;
function isBoolean(b) {
return b === true || b === false;
}
exports.isBoolean = isBoolean;
/**
* Convert a string into a valid variable name
*/
function varName(s) {
// Replace non-alphanumeric characters (anything besides a-zA-Z0-9_) with _
var alphanumericS = s.replace(/\W/g, '_');
// Add _ if the string has leading numbers.
return (s.match(/^\d+/) ? '_' : '') + alphanumericS;
}
exports.varName = varName;
function logicalExpr(op, cb) {
if (logical_1.isLogicalNot(op)) {
return '!(' + logicalExpr(op.not, cb) + ')';
}
else if (logical_1.isLogicalAnd(op)) {
return '(' + op.and.map(function (and) { return logicalExpr(and, cb); }).join(') && (') + ')';
}
else if (logical_1.isLogicalOr(op)) {
return '(' + op.or.map(function (or) { return logicalExpr(or, cb); }).join(') || (') + ')';
}
else {
return cb(op);
}
}
exports.logicalExpr = logicalExpr;
},{"./logical":95,"json-stable-stringify":110,"vega-util":116}],108:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var mark_1 = require("./mark");
var mark_2 = require("./mark");
var util_1 = require("./util");
/**
* Required Encoding Channels for each mark type
*/
exports.DEFAULT_REQUIRED_CHANNEL_MAP = {
text: ['text'],
line: ['x', 'y'],
area: ['x', 'y']
};
/**
* Supported Encoding Channel for each mark type
*/
exports.DEFAULT_SUPPORTED_CHANNEL_TYPE = {
bar: util_1.toSet(['row', 'column', 'x', 'y', 'size', 'color', 'detail']),
line: util_1.toSet(['row', 'column', 'x', 'y', 'color', 'detail']),
area: util_1.toSet(['row', 'column', 'x', 'y', 'color', 'detail']),
tick: util_1.toSet(['row', 'column', 'x', 'y', 'color', 'detail']),
circle: util_1.toSet(['row', 'column', 'x', 'y', 'color', 'size', 'detail']),
square: util_1.toSet(['row', 'column', 'x', 'y', 'color', 'size', 'detail']),
point: util_1.toSet(['row', 'column', 'x', 'y', 'color', 'size', 'detail', 'shape']),
text: util_1.toSet(['row', 'column', 'size', 'color', 'text']) // TODO(#724) revise
};
// TODO: consider if we should add validate method and
// requires ZSchema in the main vega-lite repo
/**
* Further check if encoding mapping of a spec is invalid and
* return error if it is invalid.
*
* This checks if
* (1) all the required encoding channels for the mark type are specified
* (2) all the specified encoding channels are supported by the mark type
* @param {[type]} spec [description]
* @param {RequiredChannelMap = DefaultRequiredChannelMap} requiredChannelMap
* @param {SupportedChannelMap = DefaultSupportedChannelMap} supportedChannelMap
* @return {String} Return one reason why the encoding is invalid,
* or null if the encoding is valid.
*/
function getEncodingMappingError(spec, requiredChannelMap, supportedChannelMap) {
if (requiredChannelMap === void 0) { requiredChannelMap = exports.DEFAULT_REQUIRED_CHANNEL_MAP; }
if (supportedChannelMap === void 0) { supportedChannelMap = exports.DEFAULT_SUPPORTED_CHANNEL_TYPE; }
var mark = mark_1.isMarkDef(spec.mark) ? spec.mark.type : spec.mark;
var encoding = spec.encoding;
var requiredChannels = requiredChannelMap[mark];
var supportedChannels = supportedChannelMap[mark];
for (var i in requiredChannels) {
if (!(requiredChannels[i] in encoding)) {
return 'Missing encoding channel \"' + requiredChannels[i] +
'\" for mark \"' + mark + '\"';
}
}
for (var channel in encoding) {
if (!supportedChannels[channel]) {
return 'Encoding channel \"' + channel +
'\" is not supported by mark type \"' + mark + '\"';
}
}
if (mark === mark_2.BAR && !encoding.x && !encoding.y) {
return 'Missing both x and y for bar';
}
return null;
}
exports.getEncodingMappingError = getEncodingMappingError;
},{"./mark":96,"./util":107}],109:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = require("./util");
function isVgSignalRef(o) {
return !!o['signal'];
}
exports.isVgSignalRef = isVgSignalRef;
function isVgRangeStep(range) {
return !!range['step'];
}
exports.isVgRangeStep = isVgRangeStep;
function isDataRefUnionedDomain(domain) {
if (!util_1.isArray(domain)) {
return 'fields' in domain && !('data' in domain);
}
return false;
}
exports.isDataRefUnionedDomain = isDataRefUnionedDomain;
function isFieldRefUnionDomain(domain) {
if (!util_1.isArray(domain)) {
return 'fields' in domain && 'data' in domain;
}
return false;
}
exports.isFieldRefUnionDomain = isFieldRefUnionDomain;
function isDataRefDomain(domain) {
if (!util_1.isArray(domain)) {
return 'field' in domain && 'data' in domain;
}
return false;
}
exports.isDataRefDomain = isDataRefDomain;
function isSignalRefDomain(domain) {
if (!util_1.isArray(domain)) {
return 'signal' in domain;
}
return false;
}
exports.isSignalRefDomain = isSignalRefDomain;
},{"./util":107}],110:[function(require,module,exports){
var json = typeof JSON !== 'undefined' ? JSON : require('jsonify');
module.exports = function (obj, opts) {
if (!opts) opts = {};
if (typeof opts === 'function') opts = { cmp: opts };
var space = opts.space || '';
if (typeof space === 'number') space = Array(space+1).join(' ');
var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
var replacer = opts.replacer || function(key, value) { return value; };
var cmp = opts.cmp && (function (f) {
return function (node) {
return function (a, b) {
var aobj = { key: a, value: node[a] };
var bobj = { key: b, value: node[b] };
return f(aobj, bobj);
};
};
})(opts.cmp);
var seen = [];
return (function stringify (parent, key, node, level) {
var indent = space ? ('\n' + new Array(level + 1).join(space)) : '';
var colonSeparator = space ? ': ' : ':';
if (node && node.toJSON && typeof node.toJSON === 'function') {
node = node.toJSON();
}
node = replacer.call(parent, key, node);
if (node === undefined) {
return;
}
if (typeof node !== 'object' || node === null) {
return json.stringify(node);
}
if (isArray(node)) {
var out = [];
for (var i = 0; i < node.length; i++) {
var item = stringify(node, i, node[i], level+1) || json.stringify(null);
out.push(indent + space + item);
}
return '[' + out.join(',') + indent + ']';
}
else {
if (seen.indexOf(node) !== -1) {
if (cycles) return json.stringify('__cycle__');
throw new TypeError('Converting circular structure to JSON');
}
else seen.push(node);
var keys = objectKeys(node).sort(cmp && cmp(node));
var out = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = stringify(node, key, node[key], level+1);
if(!value) continue;
var keyValue = json.stringify(key)
+ colonSeparator
+ value;
;
out.push(indent + space + keyValue);
}
seen.splice(seen.indexOf(node), 1);
return '{' + out.join(',') + indent + '}';
}
})({ '': obj }, '', obj, 0);
};
var isArray = Array.isArray || function (x) {
return {}.toString.call(x) === '[object Array]';
};
var objectKeys = Object.keys || function (obj) {
var has = Object.prototype.hasOwnProperty || function () { return true };
var keys = [];
for (var key in obj) {
if (has.call(obj, key)) keys.push(key);
}
return keys;
};
},{"jsonify":111}],111:[function(require,module,exports){
exports.parse = require('./lib/parse');
exports.stringify = require('./lib/stringify');
},{"./lib/parse":112,"./lib/stringify":113}],112:[function(require,module,exports){
var at, // The index of the current character
ch, // The current character
escapee = {
'"': '"',
'\\': '\\',
'/': '/',
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t'
},
text,
error = function (m) {
// Call error when something is wrong.
throw {
name: 'SyntaxError',
message: m,
at: at,
text: text
};
},
next = function (c) {
// If a c parameter is provided, verify that it matches the current character.
if (c && c !== ch) {
error("Expected '" + c + "' instead of '" + ch + "'");
}
// Get the next character. When there are no more characters,
// return the empty string.
ch = text.charAt(at);
at += 1;
return ch;
},
number = function () {
// Parse a number value.
var number,
string = '';
if (ch === '-') {
string = '-';
next('-');
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
if (ch === '.') {
string += '.';
while (next() && ch >= '0' && ch <= '9') {
string += ch;
}
}
if (ch === 'e' || ch === 'E') {
string += ch;
next();
if (ch === '-' || ch === '+') {
string += ch;
next();
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
}
number = +string;
if (!isFinite(number)) {
error("Bad number");
} else {
return number;
}
},
string = function () {
// Parse a string value.
var hex,
i,
string = '',
uffff;
// When parsing for string values, we must look for " and \ characters.
if (ch === '"') {
while (next()) {
if (ch === '"') {
next();
return string;
} else if (ch === '\\') {
next();
if (ch === 'u') {
uffff = 0;
for (i = 0; i < 4; i += 1) {
hex = parseInt(next(), 16);
if (!isFinite(hex)) {
break;
}
uffff = uffff * 16 + hex;
}
string += String.fromCharCode(uffff);
} else if (typeof escapee[ch] === 'string') {
string += escapee[ch];
} else {
break;
}
} else {
string += ch;
}
}
}
error("Bad string");
},
white = function () {
// Skip whitespace.
while (ch && ch <= ' ') {
next();
}
},
word = function () {
// true, false, or null.
switch (ch) {
case 't':
next('t');
next('r');
next('u');
next('e');
return true;
case 'f':
next('f');
next('a');
next('l');
next('s');
next('e');
return false;
case 'n':
next('n');
next('u');
next('l');
next('l');
return null;
}
error("Unexpected '" + ch + "'");
},
value, // Place holder for the value function.
array = function () {
// Parse an array value.
var array = [];
if (ch === '[') {
next('[');
white();
if (ch === ']') {
next(']');
return array; // empty array
}
while (ch) {
array.push(value());
white();
if (ch === ']') {
next(']');
return array;
}
next(',');
white();
}
}
error("Bad array");
},
object = function () {
// Parse an object value.
var key,
object = {};
if (ch === '{') {
next('{');
white();
if (ch === '}') {
next('}');
return object; // empty object
}
while (ch) {
key = string();
white();
next(':');
if (Object.hasOwnProperty.call(object, key)) {
error('Duplicate key "' + key + '"');
}
object[key] = value();
white();
if (ch === '}') {
next('}');
return object;
}
next(',');
white();
}
}
error("Bad object");
};
value = function () {
// Parse a JSON value. It could be an object, an array, a string, a number,
// or a word.
white();
switch (ch) {
case '{':
return object();
case '[':
return array();
case '"':
return string();
case '-':
return number();
default:
return ch >= '0' && ch <= '9' ? number() : word();
}
};
// Return the json_parse function. It will have access to all of the above
// functions and variables.
module.exports = function (source, reviver) {
var result;
text = source;
at = 0;
ch = ' ';
result = value();
white();
if (ch) {
error("Syntax error");
}
// If there is a reviver function, we recursively walk the new structure,
// passing each name/value pair to the reviver function for possible
// transformation, starting with a temporary root object that holds the result
// in an empty key. If there is not a reviver function, we simply return the
// result.
return typeof reviver === 'function' ? (function walk(holder, key) {
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}({'': result}, '')) : result;
};
},{}],113:[function(require,module,exports){
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
case 'object':
if (!value) return 'null';
gap += indent;
partial = [];
// Array.isArray
if (Object.prototype.toString.apply(value) === '[object Array]') {
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and
// wrap them in brackets.
v = partial.length === 0 ? '[]' : gap ?
'[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be
// stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' : gap ?
'{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
'{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
module.exports = function (value, replacer, space) {
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
}
// If the space parameter is a string, it will be used as the indent string.
else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function'
&& (typeof replacer !== 'object' || typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
},{}],114:[function(require,module,exports){
(function (global){
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global global, define, System, Reflect, Promise */
var __extends;
var __assign;
var __rest;
var __decorate;
var __param;
var __metadata;
var __awaiter;
var __generator;
var __exportStar;
var __values;
var __read;
var __spread;
var __await;
var __asyncGenerator;
var __asyncDelegator;
var __asyncValues;
(function (factory) {
var root = typeof global === "object" ? global : typeof self === "object" ? self : typeof this === "object" ? this : {};
if (typeof define === "function" && define.amd) {
define("tslib", ["exports"], function (exports) { factory(createExporter(root, createExporter(exports))); });
}
else if (typeof module === "object" && typeof module.exports === "object") {
factory(createExporter(root, createExporter(module.exports)));
}
else {
factory(createExporter(root));
}
function createExporter(exports, previous) {
return function (id, v) { return exports[id] = previous ? previous(id, v) : v; };
}
})
(function (exporter) {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
__extends = function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
__assign = Object.assign || function (t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
__rest = function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
};
__decorate = function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
__param = function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
__metadata = function (metadataKey, metadataValue) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
};
__awaiter = function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
__generator = function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
__exportStar = function (m, exports) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
};
__values = function (o) {
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
if (m) return m.call(o);
return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
};
__read = function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
__spread = function () {
for (var ar = [], i = 0; i < arguments.length; i++)
ar = ar.concat(__read(arguments[i]));
return ar;
};
__await = function (v) {
return this instanceof __await ? (this.v = v, this) : new __await(v);
};
__asyncGenerator = function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
__asyncDelegator = function (o) {
var i, p;
return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; }
};
__asyncValues = function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator];
return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator]();
};
exporter("__extends", __extends);
exporter("__assign", __assign);
exporter("__rest", __rest);
exporter("__decorate", __decorate);
exporter("__param", __param);
exporter("__metadata", __metadata);
exporter("__awaiter", __awaiter);
exporter("__generator", __generator);
exporter("__exportStar", __exportStar);
exporter("__values", __values);
exporter("__read", __read);
exporter("__spread", __spread);
exporter("__await", __await);
exporter("__asyncGenerator", __asyncGenerator);
exporter("__asyncDelegator", __asyncDelegator);
exporter("__asyncValues", __asyncValues);
});
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],115:[function(require,module,exports){
(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';
/**
* Parse an event selector string.
* Returns an array of event stream definitions.
*/
var eventSelector = function(selector, source, marks) {
DEFAULT_SOURCE = source || VIEW;
MARKS = marks || DEFAULT_MARKS;
return parseMerge(selector.trim()).map(parseSelector);
};
var VIEW = 'view';
var LBRACK = '[';
var RBRACK = ']';
var LBRACE = '{';
var RBRACE = '}';
var COLON = ':';
var COMMA = ',';
var NAME = '@';
var GT = '>';
var ILLEGAL = /[[\]{}]/;
var DEFAULT_SOURCE;
var MARKS;
var DEFAULT_MARKS = {
'*': 1,
arc: 1,
area: 1,
group: 1,
image: 1,
line: 1,
path: 1,
rect: 1,
rule: 1,
shape: 1,
symbol: 1,
text: 1,
trail: 1
};
function isMarkType(type) {
return MARKS.hasOwnProperty(type);
}
function find(s, i, endChar, pushChar, popChar) {
var count = 0,
n = s.length,
c;
for (; i<n; ++i) {
c = s[i];
if (!count && c === endChar) return i;
else if (popChar && popChar.indexOf(c) >= 0) --count;
else if (pushChar && pushChar.indexOf(c) >= 0) ++count;
}
return i;
}
function parseMerge(s) {
var output = [],
start = 0,
n = s.length,
i = 0;
while (i < n) {
i = find(s, i, COMMA, LBRACK + LBRACE, RBRACK + RBRACE);
output.push(s.substring(start, i).trim());
start = ++i;
}
if (output.length === 0) {
throw 'Empty event selector: ' + s;
}
return output;
}
function parseSelector(s) {
return s[0] === '['
? parseBetween(s)
: parseStream(s);
}
function parseBetween(s) {
var n = s.length,
i = 1,
b, stream;
i = find(s, i, RBRACK, LBRACK, RBRACK);
if (i === n) {
throw 'Empty between selector: ' + s;
}
b = parseMerge(s.substring(1, i));
if (b.length !== 2) {
throw 'Between selector must have two elements: ' + s;
}
s = s.slice(i + 1).trim();
if (s[0] !== GT) {
throw 'Expected \'>\' after between selector: ' + s;
}
b = b.map(parseSelector);
stream = parseSelector(s.slice(1).trim());
if (stream.between) {
return {
between: b,
stream: stream
};
} else {
stream.between = b;
}
return stream;
}
function parseStream(s) {
var stream = {source: DEFAULT_SOURCE},
source = [],
throttle = [0, 0],
markname = 0,
start = 0,
n = s.length,
i = 0, j,
filter;
// extract throttle from end
if (s[n-1] === RBRACE) {
i = s.lastIndexOf(LBRACE);
if (i >= 0) {
try {
throttle = parseThrottle(s.substring(i+1, n-1));
} catch (e) {
throw 'Invalid throttle specification: ' + s;
}
s = s.slice(0, i).trim();
n = s.length;
} else throw 'Unmatched right brace: ' + s;
i = 0;
}
if (!n) throw s;
// set name flag based on first char
if (s[0] === NAME) markname = ++i;
// extract first part of multi-part stream selector
j = find(s, i, COLON);
if (j < n) {
source.push(s.substring(start, j).trim());
start = i = ++j;
}
// extract remaining part of stream selector
i = find(s, i, LBRACK);
if (i === n) {
source.push(s.substring(start, n).trim());
} else {
source.push(s.substring(start, i).trim());
filter = [];
start = ++i;
if (start === n) throw 'Unmatched left bracket: ' + s;
}
// extract filters
while (i < n) {
i = find(s, i, RBRACK);
if (i === n) throw 'Unmatched left bracket: ' + s;
filter.push(s.substring(start, i).trim());
if (i < n-1 && s[++i] !== LBRACK) throw 'Expected left bracket: ' + s;
start = ++i;
}
// marshall event stream specification
if (!(n = source.length) || ILLEGAL.test(source[n-1])) {
throw 'Invalid event selector: ' + s;
}
if (n > 1) {
stream.type = source[1];
if (markname) {
stream.markname = source[0].slice(1);
} else if (isMarkType(source[0])) {
stream.marktype = source[0];
} else {
stream.source = source[0];
}
} else {
stream.type = source[0];
}
if (stream.type.slice(-1) === '!') {
stream.consume = true;
stream.type = stream.type.slice(0, -1);
}
if (filter != null) stream.filter = filter;
if (throttle[0]) stream.throttle = throttle[0];
if (throttle[1]) stream.debounce = throttle[1];
return stream;
}
function parseThrottle(s) {
var a = s.split(COMMA);
if (!s.length || a.length > 2) throw s;
return a.map(function(_) {
var x = +_;
if (x !== x) throw s;
return x;
});
}
exports.selector = eventSelector;
Object.defineProperty(exports, '__esModule', { value: true });
})));
},{}],116:[function(require,module,exports){
(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 = 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<n; ++j) {
c = p[j];
if (c === '\\') {
s += p.substring(i, j);
i = ++j;
} else if (c === q) {
push();
q = null;
b = -1;
} else if (q) {
continue;
} else if (i === b && c === '"') {
i = j + 1;
q = c;
} else if (i === b && c === "'") {
i = j + 1;
q = c;
} else if (c === '.' && !b) {
if (j > i) {
push();
} else {
i = j + 1;
}
} else if (c === '[') {
if (j > i) push();
b = i = j + 1;
} else if (c === ']') {
if (!b) error('Access path missing open bracket: ' + p);
if (b > 0) push();
b = 0;
i = j + 1;
}
}
if (b) error('Access path missing closing bracket: ' + p);
if (q) error('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 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<len; ++i) {
x = arguments[i];
for (k in x) { _[k] = x[k]; }
}
return _;
};
var extentIndex = function(array, f) {
var i = -1,
n = array.length,
a, b, c, u, v;
if (f == null) {
while (++i < n) {
b = array[i];
if (b != null && b >= 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) {
fields = fields ? array(fields) : fields;
var fn = !(fields && fields.length)
? function() { return ''; }
: Function('_', 'return \'\'+' +
fields.map(function(f) {
return '_[' + 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<n0 && i1<n1; ++i) {
merged[i] = compare(array0[i0], array1[i1]) > 0
? array1[i1++]
: array0[i0++];
}
for (; i0<n0; ++i0, ++i) {
merged[i] = array0[i0];
}
for (; i1<n1; ++i1, ++i) {
merged[i] = array1[i1];
}
return merged;
};
var repeat = function(str, reps) {
var s = '';
while (--reps >= 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 peek = function(array) {
return array[array.length - 1];
};
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 toNumber = function(_) {
return _ == null || _ === '' ? null : +_;
};
var toString = function(_) {
return _ == null || _ === '' ? null : _ + '';
};
var toSet = function(_) {
for (var s={}, i=0, n=_.length; i<n; ++i) s[_[i]] = 1;
return s;
};
var truncate = function(str, length, align, ellipsis) {
var e = ellipsis != null ? ellipsis : '\u2026',
s = str + '',
n = s.length,
l = Math.max(0, length - e.length);
return n <= length ? s
: align === 'left' ? e + s.slice(n - l)
: align === 'center' ? s.slice(0, Math.ceil(l/2)) + e + s.slice(n - ~~(l/2))
: s.slice(0, l) + e;
};
var visitArray = function(array, filter, visitor) {
if (array) {
var i = 0, n = array.length, t;
if (filter) {
for (; i<n; ++i) {
if (t = filter(array[i])) visitor(t, i, array);
}
} else {
array.forEach(visitor);
}
}
};
exports.accessor = accessor;
exports.accessorName = accessorName;
exports.accessorFields = accessorFields;
exports.id = id;
exports.identity = identity;
exports.zero = zero;
exports.one = one;
exports.truthy = truthy;
exports.falsy = falsy;
exports.logger = logger;
exports.None = None;
exports.Error = Error$1;
exports.Warn = Warn;
exports.Info = Info;
exports.Debug = Debug;
exports.array = array;
exports.compare = compare;
exports.constant = constant;
exports.debounce = debounce;
exports.error = error;
exports.extend = extend;
exports.extentIndex = extentIndex;
exports.fastmap = fastmap;
exports.field = field;
exports.inherits = inherits;
exports.isArray = isArray;
exports.isBoolean = isBoolean;
exports.isDate = isDate;
exports.isFunction = isFunction;
exports.isNumber = isNumber;
exports.isObject = isObject;
exports.isRegExp = isRegExp;
exports.isString = isString;
exports.key = key;
exports.merge = merge;
exports.pad = pad;
exports.peek = peek;
exports.repeat = repeat;
exports.splitAccessPath = splitAccessPath;
exports.stringValue = $;
exports.toBoolean = toBoolean;
exports.toDate = toDate;
exports.toNumber = toNumber;
exports.toString = toString;
exports.toSet = toSet;
exports.truncate = truncate;
exports.visitArray = visitArray;
Object.defineProperty(exports, '__esModule', { value: true });
})));
},{}],117:[function(require,module,exports){
"use strict";
/**
* Parse a vega schema url into library and version.
*/
function default_1(url) {
var regex = /\/schema\/([\w-]+)\/([\w\.\-]+)\.json$/g;
var _a = regex.exec(url).slice(1, 3), library = _a[0], version = _a[1];
return { library: library, version: version };
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
},{}],118:[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<n; ++j) {
c = p[j];
if (c === '\\') {
s += p.substring(i, j);
i = ++j;
} else if (c === q) {
push();
q = null;
b = -1;
} else if (q) {
continue;
} else if (i === b && c === '"') {
i = j + 1;
q = c;
} else if (i === b && c === "'") {
i = j + 1;
q = c;
} else if (c === '.' && !b) {
if (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 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<len; ++i) {
x = arguments[i];
for (k in x) { _[k] = x[k]; }
}
return _;
};
var extentIndex = function(array, f) {
var i = -1,
n = array.length,
a, b, c, u, v;
if (f == null) {
while (++i < n) {
b = array[i];
if (b != null && b >= 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) {
fields = fields ? array(fields) : fields;
var fn = !(fields && fields.length)
? function() { return ''; }
: Function('_', 'return \'\'+' +
fields.map(function(f) {
return '_[' + 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<n0 && i1<n1; ++i) {
merged[i] = compare(array0[i0], array1[i1]) > 0
? array1[i1++]
: array0[i0++];
}
for (; i0<n0; ++i0, ++i) {
merged[i] = array0[i0];
}
for (; i1<n1; ++i1, ++i) {
merged[i] = array1[i1];
}
return merged;
};
var repeat = function(str, reps) {
var s = '';
while (--reps >= 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 peek = function(array) {
return array[array.length - 1];
};
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 toNumber = function(_) {
return _ == null || _ === '' ? null : +_;
};
var toString = function(_) {
return _ == null || _ === '' ? null : _ + '';
};
var toSet = function(_) {
for (var s={}, i=0, n=_.length; i<n; ++i) s[_[i]] = 1;
return s;
};
var truncate = function(str, length, align, ellipsis) {
var e = ellipsis != null ? ellipsis : '\u2026',
s = str + '',
n = s.length,
l = Math.max(0, length - e.length);
return n <= length ? s
: align === 'left' ? e + s.slice(n - l)
: align === 'center' ? s.slice(0, Math.ceil(l/2)) + e + s.slice(n - ~~(l/2))
: s.slice(0, l) + e;
};
var visitArray = function(array, filter, visitor) {
if (array) {
var i = 0, n = array.length, t;
if (filter) {
for (; i<n; ++i) {
if (t = filter(array[i])) visitor(t, i, array);
}
} else {
array.forEach(visitor);
}
}
};
function UniqueList(idFunc) {
var $$$1 = idFunc || identity,
list = [],
ids = {};
list.add = function(_) {
var id$$1 = $$$1(_);
if (!ids[id$$1]) {
ids[id$$1] = 1;
list.push(_);
}
return list;
};
list.remove = function(_) {
var id$$1 = $$$1(_), idx;
if (ids[id$$1]) {
ids[id$$1] = 0;
if ((idx = list.indexOf(_)) >= 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<n; ++i) add.push(d[i]);
return this;
},
remove: function(t) {
var a = isFunction(t) ? remp : rem,
d = array(t), i = 0, n = d.length;
for (; i<n; ++i) a.push(d[i]);
return this;
},
modify: function(t, field$$1, value) {
var m = {field: field$$1, value: constant(value)};
if (isFunction(t)) {
m.filter = t;
modp.push(m);
} else {
m.tuple = t;
mod.push(m);
}
return this;
},
encode: function(t, set) {
if (isFunction(t)) modp.push({filter: t, field: set});
else mod.push({tuple: t, field: set});
return this;
},
reflow: function() {
reflow = true;
return this;
},
pulse: function(pulse, tuples) {
var out, i, n, m, f, t, id$$1;
// add
for (i=0, n=add.length; i<n; ++i) {
pulse.add.push(ingest(add[i]));
}
// remove
for (out={}, i=0, n=rem.length; i<n; ++i) {
t = rem[i];
out[tupleid(t)] = t;
}
for (i=0, n=remp.length; i<n; ++i) {
f = remp[i];
tuples.forEach(function(t) {
if (f(t)) out[tupleid(t)] = t;
});
}
for (id$$1 in out) pulse.rem.push(out[id$$1]);
// modify
function modify(t, f, v) {
if (v) t[f] = v(t); else pulse.encode = f;
if (!reflow) out[tupleid(t)] = t;
}
for (out={}, i=0, n=mod.length; i<n; ++i) {
m = mod[i];
modify(m.tuple, m.field, m.value);
pulse.modifies(m.field);
}
for (i=0, n=modp.length; i<n; ++i) {
m = modp[i];
f = m.filter;
tuples.forEach(function(t) {
if (f(t)) modify(t, m.field, m.value);
});
pulse.modifies(m.field);
}
// reflow?
if (reflow) {
pulse.mod = rem.length || remp.length
? tuples.filter(function(t) { return out.hasOwnProperty(tupleid(t)); })
: tuples.slice();
} else {
for (id$$1 in out) pulse.mod.push(out[id$$1]);
}
return pulse;
}
};
}
var CACHE = '_:mod:_';
/**
* Hash that tracks modifications to assigned values.
* Callers *must* use the set method to update values.
*/
function Parameters() {
Object.defineProperty(this, CACHE, {writable:true, value: {}});
}
var prototype$2 = Parameters.prototype;
/**
* Set a parameter value. If the parameter value changes, the parameter
* will be recorded as modified.
* @param {string} name - The parameter name.
* @param {number} index - The index into an array-value parameter. Ignored if
* the argument is undefined, null or less than zero.
* @param {*} value - The parameter value to set.
* @param {boolean} [force=false] - If true, records the parameter as modified
* even if the value is unchanged.
* @return {Parameters} - This parameter object.
*/
prototype$2.set = function(name, index, value, force) {
var o = this,
v = o[name],
mod = o[CACHE];
if (index != null && index >= 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<name.length; ++k) {
if (mod[name[k]]) return true;
}
return false;
}
return (index != null && index >= 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<n; ++i) add(name, i, value[i]);
} else {
add(name, -1, value);
}
}
this.marshall().clear(); // initialize values
return deps;
};
/**
* Internal method for marshalling parameter values.
* Visits each operator dependency to pull the latest value.
* @return {Parameters} A Parameters object to pass to the update function.
*/
prototype$1.marshall = function(stamp) {
var argval = this._argval || NO_PARAMS,
argops = this._argops, item, i, n, op, mod;
if (argops && (n = argops.length)) {
for (i=0; i<n; ++i) {
item = argops[i];
op = item.op;
mod = op.modified() && op.stamp === stamp;
argval.set(item.name, item.index, op.value, mod);
}
}
return argval;
};
/**
* Delegate method to perform operator processing.
* Subclasses can override this method to perform custom processing.
* By default, it marshalls parameters and calls the update function
* if that function is defined. If the update function does not
* change the operator value then StopPropagation is returned.
* If no update function is defined, this method does nothing.
* @param {Pulse} pulse - the current dataflow pulse.
* @return The output pulse or StopPropagation. A falsy return value
* (including undefined) will let the input pulse pass through.
*/
prototype$1.evaluate = function(pulse) {
if (this._update) {
var params = this.marshall(pulse.stamp),
v = this._update(params, pulse);
params.clear();
if (v !== this.value) {
this.value = v;
} else if (!this.modified()) {
return pulse.StopPropagation;
}
}
};
/**
* Run this operator for the current pulse. If this operator has already
* been run at (or after) the pulse timestamp, returns StopPropagation.
* 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$1.run = function(pulse) {
if (pulse.stamp <= this.stamp) return pulse.StopPropagation;
var rv;
if (this.skip()) {
this.skip(false);
rv = 0;
} else {
rv = this.evaluate(pulse);
}
this.stamp = pulse.stamp;
this.pulse = rv;
return rv || pulse;
};
/**
* Add an operator to the dataflow graph. This function accepts a
* variety of input argument types. The basic signature supports an
* initial value, update function and parameters. If the first parameter
* is an Operator instance, it will be added directly. If it is a
* constructor for an Operator subclass, a new instance will be instantiated.
* Otherwise, if the first parameter is a function instance, it will be used
* as the update function and a null initial value is assumed.
* @param {*} init - One of: the operator to add, the initial value of
* the operator, an operator class to instantiate, or an update function.
* @param {function} [update] - The operator update function.
* @param {object} [params] - The operator parameters.
* @param {boolean} [react=true] - Flag indicating if this operator should
* listen for changes to upstream operators included as parameters.
* @return {Operator} - The added operator.
*/
var add = function(init, update, params, react) {
var shift = 1,
op;
if (init instanceof Operator) {
op = init;
} else if (init && init.prototype instanceof Operator) {
op = new init();
} else if (isFunction(init)) {
op = new Operator(null, init);
} else {
shift = 0;
op = new Operator(init, update);
}
this.rank(op);
if (shift) {
react = params;
params = update;
}
if (params) this.connect(op, op.parameters(params, react));
this.touch(op);
return op;
};
/**
* Connect a target operator as a dependent of source operators.
* If necessary, this method will rerank the target operator and its
* dependents to ensure propagation proceeds in a topologically sorted order.
* @param {Operator} target - The target operator.
* @param {Array<Operator>} - 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<n; ++i) {
if (targetRank < sources[i].rank) {
this.rerank(target);
return;
}
}
};
var STREAM_ID = 0;
/**
* Models an event stream.
* @constructor
* @param {function(Object, number): boolean} [filter] - Filter predicate.
* Events pass through when truthy, events are suppressed when falsy.
* @param {function(Object): *} [apply] - Applied to input events to produce
* new event values.
* @param {function(Object)} [receive] - Event callback function to invoke
* upon receipt of a new event. Use to override standard event processing.
*/
function EventStream(filter, apply, receive) {
this.id = ++STREAM_ID;
this.value = null;
if (receive) this.receive = receive;
if (filter) this._filter = filter;
if (apply) this._apply = apply;
}
/**
* Creates a new event stream instance with the provided
* (optional) filter, apply and receive functions.
* @param {function(Object, number): boolean} [filter] - Filter predicate.
* Events pass through when truthy, events are suppressed when falsy.
* @param {function(Object): *} [apply] - Applied to input events to produce
* new event values.
* @see EventStream
*/
function stream(filter, apply, receive) {
return new EventStream(filter, apply, receive);
}
var prototype$3 = EventStream.prototype;
prototype$3._filter = truthy;
prototype$3._apply = identity;
prototype$3.targets = function() {
return this._targets || (this._targets = UniqueList(id));
};
prototype$3.consume = function(_) {
if (!arguments.length) return !!this._consume;
this._consume = !!_;
return this;
};
prototype$3.receive = function(evt) {
if (this._filter(evt)) {
var val = (this.value = this._apply(evt)),
trg = this._targets,
n = trg ? trg.length : 0,
i = 0;
for (; i<n; ++i) trg[i].receive(val);
if (this._consume) {
evt.preventDefault();
evt.stopPropagation();
}
}
};
prototype$3.filter = function(filter) {
var s = stream(filter);
this.targets().add(s);
return s;
};
prototype$3.apply = function(apply) {
var s = stream(null, apply);
this.targets().add(s);
return s;
};
prototype$3.merge = function() {
var s = stream();
this.targets().add(s);
for (var i=0, n=arguments.length; i<n; ++i) {
arguments[i].targets().add(s);
}
return s;
};
prototype$3.throttle = function(pause) {
var t = -1;
return this.filter(function() {
var now = Date.now();
if ((now - t) > 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<n; ++i) {
sources[i].addEventListener(type, send);
}
return s;
};
var prefix = "$";
function Map() {}
Map.prototype = map.prototype = {
constructor: Map,
has: function(key) {
return (prefix + key) in this;
},
get: function(key) {
return this[prefix + key];
},
set: function(key, value) {
this[prefix + key] = value;
return this;
},
remove: function(key) {
var property = prefix + key;
return property in this && delete this[property];
},
clear: function() {
for (var property in this) if (property[0] === prefix) delete this[property];
},
keys: function() {
var keys = [];
for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
return keys;
},
values: function() {
var values = [];
for (var property in this) if (property[0] === prefix) values.push(this[property]);
return values;
},
entries: function() {
var entries = [];
for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
return entries;
},
size: function() {
var size = 0;
for (var property in this) if (property[0] === prefix) ++size;
return size;
},
empty: function() {
for (var property in this) if (property[0] === prefix) return false;
return true;
},
each: function(f) {
for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
}
};
function map(object, f) {
var map = new Map;
// Copy constructor.
if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });
// Index array by numeric index or specified key function.
else if (Array.isArray(object)) {
var i = -1,
n = object.length,
o;
if (f == null) while (++i < n) map.set(i, object[i]);
else while (++i < n) map.set(f(o = object[i], i, object), o);
}
// Convert object to map.
else if (object) for (var key in object) map.set(key, object[key]);
return map;
}
var nest = function() {
var keys = [],
sortKeys = [],
sortValues,
rollup,
nest;
function apply(array, depth, createResult, setResult) {
if (depth >= 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
}
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]"),
delimiterCode = 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 EOL = {}, // sentinel value for end-of-line
EOF = {}, // sentinel value for end-of-file
rows = [], // output rows
N = text.length,
I = 0, // current character index
n = 0, // the current line number
t, // the current token
eol; // is the current token followed by EOL?
function token() {
if (I >= N) return EOF; // special case: end of file
if (eol) return eol = false, EOL; // special case: end of line
// special case: quotes
var j = I, c;
if (text.charCodeAt(j) === 34) {
var i = j;
while (i++ < N) {
if (text.charCodeAt(i) === 34) {
if (text.charCodeAt(i + 1) !== 34) break;
++i;
}
}
I = i + 2;
c = text.charCodeAt(i + 1);
if (c === 13) {
eol = true;
if (text.charCodeAt(i + 2) === 10) ++I;
} else if (c === 10) {
eol = true;
}
return text.slice(j + 1, i).replace(/""/g, "\"");
}
// common case: find next delimiter or newline
while (I < N) {
var k = 1;
c = text.charCodeAt(I++);
if (c === 10) eol = true; // \n
else if (c === 13) { eol = true; if (text.charCodeAt(I) === 10) ++I, ++k; } // \r|\r\n
else if (c !== delimiterCode) continue;
return text.slice(j, I - k);
}
// special case: last token before EOF
return text.slice(j);
}
while ((t = token()) !== EOF) {
var a = [];
while (t !== EOL && t !== EOF) {
a.push(t);
t = token();
}
if (f && (a = f(a, n++)) == null) continue;
rows.push(a);
}
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 and return
result.href = uri;
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
};
var typeTests = [
isBoolean$1,
isInteger,
isNumber$1,
isDate$1
];
var typeList = [
'boolean',
'integer',
'number',
'date'
];
function inferType(values, field$$1) {
var tests = typeTests.slice(),
value, i, n, j;
for (i=0, n=values.length; i<n; ++i) {
value = field$$1 ? values[i][field$$1] : values[i];
for (j=0; j<tests.length; ++j) {
if (isValid(value) && !tests[j](value)) {
tests.splice(j, 1); --j;
}
}
if (tests.length === 0) return 'string';
}
return typeList[typeTests.indexOf(tests[0])];
}
function inferTypes(data, fields) {
return fields.reduce(function(types, field$$1) {
types[field$$1] = inferType(data, field$$1);
return types;
}, {});
}
// -- Type Checks ----
function isValid(_) {
return _ != null && _ === _;
}
function isBoolean$1(_) {
return _ === 'true' || _ === 'false' || _ === true || _ === false;
}
function isDate$1(_) {
return !isNaN(Date.parse(_));
}
function isNumber$1(_) {
return !isNaN(+_) && !(_ instanceof Date);
}
function isInteger(_) {
return isNumber$1(_) && (_=+_) === ~~_;
}
function delimitedFormat(delimiter) {
return function(data, format) {
var delim = {delimiter: delimiter};
return dsv$1(data, format ? extend(format, delim) : delim);
};
}
function dsv$1(data, format) {
if (format.header) {
data = format.header
.map($)
.join(format.delimiter) + '\n' + data;
}
return dsvFormat(format.delimiter).parse(data+'');
}
function isBuffer(_) {
return (typeof Buffer === 'function' && isFunction(Buffer.isBuffer))
? Buffer.isBuffer(_) : false;
}
var json$1 = function(data, format) {
var prop = (format && format.property) ? field(format.property) : identity;
return isObject(data) && !isBuffer(data)
? parseJSON(prop(data))
: prop(JSON.parse(data));
};
function parseJSON(data, format) {
return (format && format.copy)
? JSON.parse(JSON.stringify(data))
: data;
}
var noop$1 = function() {};
function absolute(transform) {
if (!transform) return noop$1;
var x0,
y0,
kx = transform.scale[0],
ky = transform.scale[1],
dx = transform.translate[0],
dy = transform.translate[1];
return function(point, i) {
if (!i) x0 = y0 = 0;
point[0] = (x0 += point[0]) * kx + dx;
point[1] = (y0 += point[1]) * ky + dy;
};
}
function reverse(array, n) {
var t, j = array.length, i = j - n;
while (i < --j) t = array[i], array[i++] = array[j], array[j] = t;
}
function bisect(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 feature = function(topology, o) {
return o.type === "GeometryCollection" ? {
type: "FeatureCollection",
features: o.geometries.map(function(o) { return feature$1(topology, o); })
} : feature$1(topology, o);
};
function feature$1(topology, o) {
var f = {
type: "Feature",
id: o.id,
properties: o.properties || {},
geometry: object(topology, o)
};
if (o.id == null) delete f.id;
return f;
}
function object(topology, o) {
var absolute$$1 = absolute(topology.transform),
arcs = topology.arcs;
function arc(i, points) {
if (points.length) points.pop();
for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length, p; k < n; ++k) {
points.push(p = a[k].slice());
absolute$$1(p, k);
}
if (i < 0) reverse(points, n);
}
function point(p) {
p = p.slice();
absolute$$1(p, 0);
return p;
}
function line(arcs) {
var points = [];
for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points);
if (points.length < 2) points.push(points[0].slice());
return points;
}
function ring(arcs) {
var points = line(arcs);
while (points.length < 4) points.push(points[0].slice());
return points;
}
function polygon(arcs) {
return arcs.map(ring);
}
function geometry(o) {
var t = o.type;
return t === "GeometryCollection" ? {type: t, geometries: o.geometries.map(geometry)}
: t in geometryType ? {type: t, coordinates: geometryType[t](o)}
: null;
}
var geometryType = {
Point: function(o) { return point(o.coordinates); },
MultiPoint: function(o) { return o.coordinates.map(point); },
LineString: function(o) { return line(o.arcs); },
MultiLineString: function(o) { return o.arcs.map(line); },
Polygon: function(o) { return polygon(o.arcs); },
MultiPolygon: function(o) { return o.arcs.map(polygon); }
};
return geometry(o);
}
var stitchArcs = function(topology, arcs) {
var stitchedArcs = {},
fragmentByStart = {},
fragmentByEnd = {},
fragments = [],
emptyIndex = -1;
// Stitch empty arcs first, since they may be subsumed by other arcs.
arcs.forEach(function(i, j) {
var arc = topology.arcs[i < 0 ? ~i : i], t;
if (arc.length < 3 && !arc[1][0] && !arc[1][1]) {
t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t;
}
});
arcs.forEach(function(i) {
var e = ends(i),
start = e[0],
end = e[1],
f, g;
if (f = fragmentByEnd[start]) {
delete fragmentByEnd[f.end];
f.push(i);
f.end = end;
if (g = fragmentByStart[end]) {
delete fragmentByStart[g.start];
var fg = g === f ? f : f.concat(g);
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
} else {
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
}
} else if (f = fragmentByStart[end]) {
delete fragmentByStart[f.start];
f.unshift(i);
f.start = start;
if (g = fragmentByEnd[start]) {
delete fragmentByEnd[g.end];
var gf = g === f ? f : g.concat(f);
fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
} else {
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
}
} else {
f = [i];
fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f;
}
});
function ends(i) {
var arc = topology.arcs[i < 0 ? ~i : i], p0 = arc[0], p1;
if (topology.transform) p1 = [0, 0], arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; });
else p1 = arc[arc.length - 1];
return i < 0 ? [p1, p0] : [p0, p1];
}
function flush(fragmentByEnd, fragmentByStart) {
for (var k in fragmentByEnd) {
var f = fragmentByEnd[k];
delete fragmentByStart[f.start];
delete f.start;
delete f.end;
f.forEach(function(i) { stitchedArcs[i < 0 ? ~i : i] = 1; });
fragments.push(f);
}
}
flush(fragmentByEnd, fragmentByStart);
flush(fragmentByStart, fragmentByEnd);
arcs.forEach(function(i) { if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]); });
return fragments;
};
var mesh = function(topology) {
return object(topology, meshArcs.apply(this, arguments));
};
function meshArcs(topology, o, filter) {
var arcs = [];
function arc(i) {
var j = i < 0 ? ~i : i;
(geomsByArc[j] || (geomsByArc[j] = [])).push({i: i, g: geom});
}
function line(arcs) {
arcs.forEach(arc);
}
function polygon(arcs) {
arcs.forEach(line);
}
function geometry(o) {
if (o.type === "GeometryCollection") o.geometries.forEach(geometry);
else if (o.type in geometryType) geom = o, geometryType[o.type](o.arcs);
}
if (arguments.length > 1) {
var geomsByArc = [],
geom;
var geometryType = {
LineString: line,
MultiLineString: polygon,
Polygon: polygon,
MultiPolygon: function(arcs) { arcs.forEach(polygon); }
};
geometry(o);
geomsByArc.forEach(arguments.length < 3
? function(geoms) { arcs.push(geoms[0].i); }
: function(geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); });
} else {
for (var i = 0, n = topology.arcs.length; i < n; ++i) arcs.push(i);
}
return {type: "MultiLineString", arcs: stitchArcs(topology, arcs)};
}
var topojson = function(data, format) {
var object, property;
data = json$1(data, format);
if (format && (property = format.feature)) {
return (object = data.objects[property])
? feature(data, object).features
: error$1('Invalid TopoJSON object: ' + property);
}
else if (format && (property = format.mesh)) {
return (object = data.objects[property])
? [mesh(data, object)]
: error$1('Invalid TopoJSON object: ' + property);
}
error$1('Missing TopoJSON feature or mesh parameter.');
};
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 = [];
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(new Date(+start)); while (offseti(start, step), floori(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,
"H": formatHour24,
"I": formatHour12,
"j": formatDayOfYear,
"L": formatMilliseconds,
"m": formatMonthNumber,
"M": formatMinutes,
"p": formatPeriod,
"S": formatSeconds,
"U": formatWeekNumberSunday,
"w": formatWeekdayNumber,
"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,
"H": formatUTCHour24,
"I": formatUTCHour12,
"j": formatUTCDayOfYear,
"L": formatUTCMilliseconds,
"m": formatUTCMonthNumber,
"M": formatUTCMinutes,
"p": formatUTCPeriod,
"S": formatUTCSeconds,
"U": formatUTCWeekNumberSunday,
"w": formatUTCWeekdayNumber,
"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,
"H": parseHour24,
"I": parseHour24,
"j": parseDayOfYear,
"L": parseMilliseconds,
"m": parseMonthNumber,
"M": parseMinutes,
"p": parsePeriod,
"S": parseSeconds,
"U": parseWeekNumberSunday,
"w": parseWeekdayNumber,
"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);
if (i != string.length) return null;
// 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 ("W" in d || "U" in d) {
if (!("w" in d)) d.w = "W" in d ? 1 : 0;
var 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 parseWeekdayNumber(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 1));
return n ? (d.w = +n[0], i + n[0].length) : -1;
}
function parseWeekNumberSunday(d, string, i) {
var n = numberRe.exec(string.slice(i));
return n ? (d.U = +n[0], i + n[0].length) : -1;
}
function parseWeekNumberMonday(d, string, i) {
var n = numberRe.exec(string.slice(i));
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 parseLiteralPercent(d, string, i) {
var n = percentRe.exec(string.slice(i, i + 1));
return n ? 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 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 formatWeekNumberSunday(d, p) {
return pad$1(sunday.count(year(d), d), p, 2);
}
function formatWeekdayNumber(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 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 formatUTCWeekNumberSunday(d, p) {
return pad$1(utcSunday.count(utcYear(d), d), p, 2);
}
function formatUTCWeekdayNumber(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 "%";
}
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) {
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<n; ++i) {
datum = data[i];
for (j=0; j<m; ++j) {
field$$1 = fields[j];
datum[field$$1] = parsers[j](datum[field$$1]);
}
}
}
function ingest$1(target, data, format) {
return this.pulse(target, this.changeset().insert(read(data, format)));
}
function loadPending(df) {
var accept, reject,
pending = new Promise(function(a, r) {
accept = a;
reject = r;
});
pending.requests = 0;
pending.done = function() {
if (--pending.requests === 0) {
df.runAfter(function() {
df._pending = null;
try {
df.run();
accept(df);
} catch (err) {
reject(err);
}
});
}
};
return (df._pending = pending);
}
function request(target, url, format) {
var df = this,
pending = df._pending || loadPending(df);
pending.requests += 1;
df.loader()
.load(url, {context:'dataflow'})
.then(
function(data) { df.ingest(target, data, format); },
function(error) { df.error('Loading failed', url, error); })
.catch(
function(error) { df.error('Data ingestion failed', url, error); })
.then(pending.done, pending.done);
}
var SKIP$1 = {skip: true};
/**
* Perform operator updates in response to events. Applies an
* update function to compute a new operator value. If the update function
* returns a {@link ChangeSet}, the operator will be pulsed with those tuple
* changes. Otherwise, the operator value will be updated to the return value.
* @param {EventStream|Operator} source - The event source to react to.
* This argument can be either an EventStream or an Operator.
* @param {Operator|function(object):Operator} target - The operator to update.
* This argument can either be an Operator instance or (if the source
* argument is an EventStream), a function that accepts an event object as
* input and returns an Operator to target.
* @param {function(Parameters,Event): *} [update] - Optional update function
* to compute the new operator value, or a literal value to set. Update
* functions expect to receive a parameter object and event as arguments.
* This function can either return a new operator value or (if the source
* argument is an EventStream) a {@link ChangeSet} instance to pulse
* the target operator with tuple changes.
* @param {object} [params] - The update function parameters.
* @param {object} [options] - Additional options hash. If not overridden,
* updated operators will be skipped by default.
* @param {boolean} [options.skip] - If true, the operator will
* be skipped: it will not be evaluated, but its dependents will be.
* @param {boolean} [options.force] - If true, the operator will
* be re-evaluated even if its value has not changed.
* @return {Dataflow}
*/
var on = function(source, target, update, params, options) {
var fn = source instanceof Operator ? onOperator : onStream;
fn(this, source, target, update, params, options);
return this;
};
function onStream(df, stream, target, update, params, options) {
var opt = extend({}, options, SKIP$1), func, op;
if (!isFunction(target)) target = constant(target);
if (update === undefined) {
func = function(e) {
df.touch(target(e));
};
} else if (isFunction(update)) {
op = new Operator(null, update, params, false);
func = function(e) {
var v, t = target(e);
op.evaluate(e);
isChangeSet(v = op.value) ? df.pulse(t, v, options) : df.update(t, v, opt);
};
} else {
func = function(e) {
df.update(target(e), update, opt);
};
}
stream.apply(func);
}
function onOperator(df, source, target, update, params, options) {
var func, op;
if (update === undefined) {
op = target;
} else {
func = isFunction(update) ? update : constant(update);
update = !target ? func : function(_, pulse) {
var value = func(_, pulse);
return target.skip()
? value
: (target.skip(true).value = value);
};
op = new Operator(null, update, params, false);
op.modified(options && options.force);
op.rank = 0;
if (target) {
op.skip(true); // skip first invocation
op.value = target.value;
op.targets().add(target);
}
}
source.targets().add(op);
}
/**
* Assigns a rank to an operator. Ranks are assigned in increasing order
* by incrementing an internal rank counter.
* @param {Operator} op - The operator to assign a rank.
*/
function rank(op) {
op.rank = ++this._rank;
}
/**
* Re-ranks an operator and all downstream target dependencies. This
* is necessary when upstream depencies of higher rank are added to
* a target operator.
* @param {Operator} op - The operator to re-rank.
*/
function rerank(op) {
var queue = [op],
cur, list, i;
while (queue.length) {
this.rank(cur = queue.pop());
if (list = cur._targets) {
for (i=list.length; --i >= 0;) {
queue.push(cur = list[i]);
if (cur === op) this.error('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<string>} _ - 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<string>} _ - 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<Pulse>} 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<n; ++i) {
pulse = pulses[i];
if (pulse.stamp !== stamp) continue;
if (pulse.fields) {
hash = p.fields || (p.fields = {});
for (f in pulse.fields) { hash[f] = 1; }
}
if (pulse.changed(p.ADD)) c |= p.ADD;
if (pulse.changed(p.REM)) c |= p.REM;
if (pulse.changed(p.MOD)) c |= p.MOD;
}
this.changes = c;
}
var prototype$5 = inherits(MultiPulse, Pulse);
/**
* Creates a new pulse based on the values of this pulse.
* The dataflow, time stamp and field modification values are copied over.
* @return {Pulse}
*/
prototype$5.fork = function(flags) {
var p = new Pulse(this.dataflow).init(this, flags & this.NO_FIELDS);
if (flags !== undefined) {
if (flags & p.ADD) {
this.visit(p.ADD, function(t) { return p.add.push(t); });
}
if (flags & p.REM) {
this.visit(p.REM, function(t) { return p.rem.push(t); });
}
if (flags & p.MOD) {
this.visit(p.MOD, function(t) { return p.mod.push(t); });
}
}
return p;
};
prototype$5.changed = function(flags) {
return this.changes & flags;
};
prototype$5.modified = function(_) {
var p = this, fields = p.fields;
return !(fields && (p.changes & p.MOD)) ? 0
: isArray(_) ? _.some(function(f) { return fields[f]; })
: fields[_];
};
prototype$5.filter = function() {
error$1('MultiPulse does not support filtering.');
};
prototype$5.materialize = function() {
error$1('MultiPulse does not support materialization.');
};
prototype$5.visit = function(flags, visitor) {
var p = this,
pulses = p.pulses,
n = pulses.length,
i = 0;
if (flags & p.SOURCE) {
for (; i<n; ++i) {
pulses[i].visit(flags, visitor);
}
} else {
for (; i<n; ++i) {
if (pulses[i].stamp === p.stamp) {
pulses[i].visit(flags, visitor);
}
}
}
return p;
};
/**
* Runs the dataflow. This method will increment the current timestamp
* and process all updated, pulsed and touched operators. When run for
* the first time, all registered operators will be processed. If there
* are pending data loading operations, this method will return immediately
* without evaluating the dataflow. Instead, the dataflow will be
* asynchronously invoked when data loading completes. To track when dataflow
* evaluation completes, use the {@link runAsync} method instead.
* @param {string} [encode] - The name of an encoding set to invoke during
* propagation. This value is added to generated Pulse instances;
* operators can then 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 run(encode) {
var df = this,
count = 0,
level = df.logLevel(),
op, next, dt, error;
if (df._pending) {
df.info('Awaiting requests, delaying dataflow run.');
return 0;
}
if (df._pulse) {
df.error('Dataflow invoked recursively. Use the runAfter method to queue invocation.');
return 0;
}
if (!df._touched.length) {
df.info('Dataflow invoked, but nothing to do.');
return 0;
}
df._pulse = new Pulse(df, ++df._clock, encode);
if (level >= 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.forEach(function(f) {
try { f(df); } catch (err) { df.error(err); }
});
}
return count;
}
/**
* 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 invovation when propagation is not
* currently occurring.
*/
function runAfter(callback, enqueue) {
if (this._pulse || enqueue) {
// pulse propagation is currently running, queue to run after
this._postrun.push(callback);
} else {
// pulse propagation already complete, invoke immediately
try { callback(this); } catch (err) { this.error(err); }
}
}
/**
* 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);
} else {
s = s && s.pulse;
p = this._pulses[op.id];
if (s && s !== StopPropagation) {
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;
this._loader = loader();
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(type);
return t && t.Definition || null;
}
function transform(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<n; ++i) {
k += '|' + f[i](x);
}
return k;
};
}
function groupkey(fields) {
return !fields || !fields.length ? function() { return ''; }
: fields.length === 1 ? fields[0]
: multikey(fields);
}
function measureName(op, field$$1, as) {
return as || (op + (!field$$1 ? '' : '_' + field$$1));
}
var AggregateOps = {
'values': measure({
name: 'values',
init: 'cell.store = true;',
set: 'cell.data.values()', idx: -1
}),
'count': measure({
name: 'count',
set: 'cell.num'
}),
'missing': measure({
name: 'missing',
set: 'this.missing'
}),
'valid': measure({
name: 'valid',
set: 'this.valid'
}),
'sum': measure({
name: 'sum',
init: 'this.sum = 0;',
add: 'this.sum += v;',
rem: 'this.sum -= v;',
set: 'this.sum'
}),
'mean': measure({
name: 'mean',
init: 'this.mean = 0;',
add: 'var d = v - this.mean; this.mean += d / this.valid;',
rem: 'var d = v - this.mean; this.mean -= this.valid ? d / this.valid : this.mean;',
set: 'this.mean'
}),
'average': measure({
name: 'average',
set: 'this.mean',
req: ['mean'], idx: 1
}),
'variance': measure({
name: 'variance',
init: 'this.dev = 0;',
add: 'this.dev += d * (v - this.mean);',
rem: 'this.dev -= d * (v - this.mean);',
set: 'this.valid > 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<n; ++i) {
v = step / div[i];
if (v >= 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;
}
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$1 = 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 = Math.sqrt(2);
var ticks = function(start, stop, count) {
var reverse = stop < start,
i = -1,
n,
ticks,
step;
if (reverse) 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 ? 2 : 1) * Math.pow(10, power)
: -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 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) 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<m; ++j) {
for (a=0, i=0; i<n; ++i) {
a += values[~~(Math.random() * n)];
}
mu[j] = a / n;
}
return [
threshold(mu.sort(ascending), alpha/2),
threshold(mu, 1-(alpha/2))
];
};
var integer = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
var dist = {},
a, b, d;
dist.min = function(_) {
if (arguments.length) {
a = _ || 0;
d = b - a;
return dist;
} else {
return a;
}
};
dist.max = function(_) {
if (arguments.length) {
b = _ || 0;
d = b - a;
return dist;
} else {
return b;
}
};
dist.sample = function() {
return a + Math.floor(d * Math.random());
};
dist.pdf = function(x) {
return (x === Math.floor(x) && x >= 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 = Math.random() * 2 - 1;
y = Math.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);
};
var quartiles = function(array, f) {
var values = numbers(array, f);
return [
threshold(values.sort(ascending), 0.25),
threshold(values, 0.50),
threshold(values, 0.75)
];
};
// 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[~~(Math.random() * n)] + bandwidth * kernel.sample();
};
dist.pdf = function(x) {
for (var y=0, i=0; i<n; ++i) {
y += kernel.pdf((x - support[i]) / bandwidth);
}
return y / bandwidth / n;
};
dist.cdf = function(x) {
for (var y=0, i=0; i<n; ++i) {
y += kernel.cdf((x - support[i]) / bandwidth);
}
return y / n;
};
dist.icdf = function() {
throw Error('KDE icdf not supported.');
};
return dist.data(support);
};
// Scott, D. W. (1992) Multivariate Density Estimation:
// Theory, Practice, and Visualization. Wiley.
function estimateBandwidth(array) {
var n = array.length,
q = quartiles(array),
h = (q[2] - q[0]) / 1.34;
return 1.06 * Math.min(Math.sqrt(variance(array)), h) * Math.pow(n, -0.2);
}
var randomMixture = function(dists, weights) {
var dist = {}, m = 0, w;
function normalize(x) {
var w = [], sum = 0, i;
for (i=0; i<m; ++i) { sum += (w[i] = (x[i]==null ? 1 : +x[i])); }
for (i=0; i<m; ++i) { w[i] /= sum; }
return w;
}
dist.weights = function(_) {
if (arguments.length) {
w = normalize(weights = (_ || []));
return dist;
}
return weights;
};
dist.distributions = function(_) {
if (arguments.length) {
if (_) {
m = _.length;
dists = _;
} else {
m = 0;
dists = [];
}
return dist.weights(weights);
}
return dists;
};
dist.sample = function() {
var r = Math.random(),
d = dists[m-1],
v = w[0],
i = 0;
// first select distribution
for (; i<m-1; v += w[++i]) {
if (r < v) { d = dists[i]; break; }
}
// then sample from it
return d.sample();
};
dist.pdf = function(x) {
for (var p=0, i=0; i<m; ++i) {
p += w[i] * dists[i].pdf(x);
}
return p;
};
dist.cdf = function(x) {
for (var p=0, i=0; i<m; ++i) {
p += w[i] * dists[i].cdf(x);
}
return p;
};
dist.icdf = function() {
throw Error('Mixture icdf not supported.');
};
return dist.distributions(dists).weights(weights);
};
var randomUniform = function(min, max) {
if (max == null) {
max = (min == null ? 1 : min);
min = 0;
}
var dist = {},
a, b, d;
dist.min = function(_) {
if (arguments.length) {
a = _ || 0;
d = b - a;
return dist;
} else {
return a;
}
};
dist.max = function(_) {
if (arguments.length) {
b = _ || 0;
d = b - a;
return dist;
} else {
return b;
}
};
dist.sample = function() {
return a + d * Math.random();
};
dist.pdf = function(x) {
return (x >= 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<m; ++i) {
map[k(r[i])] = 1;
}
for (i=0, j=0; i<n; ++i) {
if (map[k(v = a[i])]) {
map[k(v)] = 0;
} else {
x[j++] = v;
}
}
this._rem = [];
return (this._add = x);
};
// memoizing statistics methods
prototype$9.distinct = function(get) {
var v = this.values(),
n = v.length,
map = {},
count = 0, s;
while (--n >= 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<function(object): *>} [params.groupby] - An array of accessors to groupby.
* @param {Array<function(object): *>} [params.fields] - An array of accessors to aggregate.
* @param {Array<string>} [params.ops] - An array of strings indicating aggregation operations.
* @param {Array<string>} [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<n; ++i) {
vals[i][(v = t[dims[i]])] = v;
}
}
}
collect(aggr._prev);
collect(curr);
// iterate over key cross-product, create cells as needed
function generate(base, tuple, index) {
var name = dims[index],
v = vals[index++],
k, key$$1;
for (k in v) {
tuple[name] = v[k];
key$$1 = base ? base + '|' + k : k;
if (index < n) generate(key$$1, tuple, index);
else if (!curr[key$$1]) aggr.cell(key$$1, tuple);
}
}
generate('', {}, 0);
};
prototype$8.init = function(_) {
// initialize input and output fields
var inputs = (this._inputs = []),
outputs = (this._outputs = []),
inputMap = {};
function inputVisit(get) {
var fields = array(accessorFields(get)),
i = 0, n = fields.length, f;
for (; i<n; ++i) {
if (!inputMap[f=fields[i]]) {
inputMap[f] = 1;
inputs.push(f);
}
}
}
// initialize group-by dimensions
this._dims = array(_.groupby);
this._dnames = this._dims.map(function(d) {
var dname = accessorName(d);
inputVisit(d);
outputs.push(dname);
return dname;
});
this.cellkey = _.key ? _.key : groupkey(this._dims);
// initialize aggregate measures
this._countOnly = true;
this._counts = [];
this._measures = [];
var fields = _.fields || [null],
ops = _.ops || ['count'],
as = _.as || [],
n = fields.length,
map = {},
field$$1, op, m, mname, outname, i;
if (n !== ops.length) {
error$1('Unmatched number of fields and aggregate ops.');
}
for (i=0; i<n; ++i) {
field$$1 = fields[i];
op = ops[i];
if (field$$1 == null && op !== 'count') {
error$1('Null aggregate field specified.');
}
mname = accessorName(field$$1);
outname = measureName(op, mname, as[i]);
outputs.push(outname);
if (op === 'count') {
this._counts.push(outname);
continue;
}
m = map[mname];
if (!m) {
inputVisit(field$$1);
m = (map[mname] = []);
m.field = field$$1;
this._measures.push(m);
}
if (op !== 'count') this._countOnly = false;
m.push(createMeasure(op, outname));
}
this._measures = this._measures.map(function(m) {
return compileMeasures(m, m.field);
});
return {}; // aggregation cells (this.value)
};
// -- Cell Management -----
prototype$8.cellkey = groupkey();
prototype$8.cell = function(key$$1, t) {
var cell = this.value[key$$1];
if (!cell) {
cell = this.value[key$$1] = this.newcell(key$$1, t);
this._adds[this._alen++] = cell;
} else if (cell.num === 0 && this._drop && cell.stamp < this.stamp) {
cell.stamp = this.stamp;
this._adds[this._alen++] = cell;
} else if (cell.stamp < this.stamp) {
cell.stamp = this.stamp;
this._mods[this._mlen++] = cell;
}
return cell;
};
prototype$8.newcell = function(key$$1, t) {
var cell = {
key: key$$1,
num: 0,
agg: null,
tuple: this.newtuple(t, this._prev && this._prev[key$$1]),
stamp: this.stamp,
store: false
};
if (!this._countOnly) {
var measures = this._measures,
n = measures.length, i;
cell.agg = Array(n);
for (i=0; i<n; ++i) {
cell.agg[i] = new measures[i](cell);
}
}
if (cell.store) {
cell.data = new TupleStore();
}
return cell;
};
prototype$8.newtuple = function(t, p) {
var names = this._dnames,
dims = this._dims,
x = {}, i, n;
for (i=0, n=dims.length; i<n; ++i) {
x[names[i]] = dims[i](t);
}
return p ? replace(p.tuple, x) : ingest(x);
};
// -- Process Tuples -----
prototype$8.add = function(t) {
var key$$1 = this.cellkey(t),
cell = this.cell(key$$1, t),
agg, i, n;
cell.num += 1;
if (this._countOnly) return;
if (cell.store) cell.data.add(t);
agg = cell.agg;
for (i=0, n=agg.length; i<n; ++i) {
agg[i].add(agg[i].get(t), t);
}
};
prototype$8.rem = function(t) {
var key$$1 = this.cellkey(t),
cell = this.cell(key$$1, t),
agg, i, n;
cell.num -= 1;
if (this._countOnly) return;
if (cell.store) cell.data.rem(t);
agg = cell.agg;
for (i=0, n=agg.length; i<n; ++i) {
agg[i].rem(agg[i].get(t), t);
}
};
prototype$8.celltuple = function(cell) {
var tuple = cell.tuple,
counts = this._counts,
agg, i, n;
// consolidate stored values
if (cell.store) {
cell.data.values();
}
// update tuple properties
for (i=0, n=counts.length; i<n; ++i) {
tuple[counts[i]] = cell.num;
}
if (!this._countOnly) {
agg = cell.agg;
for (i=0, n=agg.length; i<n; ++i) {
agg[i].set(tuple);
}
}
return tuple;
};
prototype$8.changes = function(out) {
var adds = this._adds,
mods = this._mods,
prev = this._prev,
drop = this._drop,
add = out.add,
rem = out.rem,
mod = out.mod,
cell, key$$1, i, n;
if (prev) for (key$$1 in prev) {
cell = prev[key$$1];
if (!drop || cell.num) rem.push(cell.tuple);
}
for (i=0, n=this._alen; i<n; ++i) {
add.push(this.celltuple(adds[i]));
adds[i] = null; // for garbage collection
}
for (i=0, n=this._mlen; i<n; ++i) {
cell = mods[i];
(cell.num === 0 && drop ? rem : mod).push(this.celltuple(cell));
mods[i] = null; // for garbage collection
}
this._alen = this._mlen = 0; // reset list of active cells
this._prev = null;
return out;
};
/**
* Generates a binning function for discretizing data.
* @constructor
* @param {object} params - The parameters for this operator. The
* provided values should be valid options for the {@link bin} function.
* @param {function(object): *} params.field - The data field to bin.
*/
function Bin(params) {
Transform.call(this, null, params);
}
Bin.Definition = {
"type": "Bin",
"metadata": {"modifies": true},
"params": [
{ "name": "field", "type": "field", "required": true },
{ "name": "anchor", "type": "number" },
{ "name": "maxbins", "type": "number", "default": 20 },
{ "name": "base", "type": "number", "default": 10 },
{ "name": "divide", "type": "number", "array": true, "default": [5, 2] },
{ "name": "extent", "type": "number", "array": true, "length": 2, "required": true },
{ "name": "step", "type": "number" },
{ "name": "steps", "type": "number", "array": true },
{ "name": "minstep", "type": "number", "default": 0 },
{ "name": "nice", "type": "boolean", "default": true },
{ "name": "name", "type": "string" },
{ "name": "as", "type": "string", "array": true, "length": 2, "default": ["bin0", "bin1"] }
]
};
var prototype$10 = inherits(Bin, Transform);
prototype$10.transform = function(_, pulse) {
var bins = this._bins(_),
start = bins.start,
step = bins.step,
as = _.as || ['bin0', 'bin1'],
b0 = as[0],
b1 = as[1],
flag;
if (_.modified()) {
pulse = pulse.reflow(true);
flag = pulse.SOURCE;
} else {
flag = pulse.modified(accessorFields(_.field)) ? pulse.ADD_MOD : pulse.ADD;
}
pulse.visit(flag, function(t) {
var v = bins(t);
// minimum bin value (inclusive)
t[b0] = v;
// maximum bin value (exclusive)
// use convoluted math for better floating point agreement
// see https://github.com/vega/vega/issues/830
t[b1] = v == null ? null : start + step * (1 + (v - start) / step);
});
return pulse.modifies(as);
};
prototype$10._bins = function(_) {
if (this.value && !_.modified()) {
return this.value;
}
var field$$1 = _.field,
bins = bin(_),
start = bins.start,
stop = bins.stop,
step = bins.step,
a, d;
if ((a = _.anchor) != null) {
d = a - (start + step * Math.floor((a - start) / step));
start += d;
stop += d;
}
var f = function(t) {
var v = field$$1(t);
if (v == null) {
return null;
} else {
v = Math.max(start, Math.min(+v, stop - step));
return start + step * Math.floor((v - start) / step);
}
};
f.start = start;
f.stop = stop;
f.step = step;
return this.value = accessor(
f,
accessorFields(field$$1),
_.name || 'bin_' + accessorName(field$$1)
);
};
var SortedList = function(idFunc, source, input) {
var $$$1 = idFunc,
data = source || [],
add = input || [],
rem = {},
cnt = 0;
return {
add: function(t) { add.push(t); },
remove: function(t) { rem[$$$1(t)] = ++cnt; },
size: function() { return data.length; },
data: function(compare$$1, resort) {
if (cnt) {
data = data.filter(function(t) { return !rem[$$$1(t)]; });
rem = {};
cnt = 0;
}
if (resort && compare$$1) {
data.sort(compare$$1);
}
if (add.length) {
data = compare$$1
? merge(compare$$1, data, add.sort(compare$$1))
: data.concat(add);
add = [];
}
return data;
}
}
};
/**
* Collects all data tuples that pass through this operator.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(*,*): number} [params.sort] - An optional
* comparator function for additionally sorting the collected tuples.
*/
function Collect(params) {
Transform.call(this, [], params);
}
Collect.Definition = {
"type": "Collect",
"metadata": {"source": true},
"params": [
{ "name": "sort", "type": "compare" }
]
};
var prototype$11 = inherits(Collect, Transform);
prototype$11.transform = function(_, pulse) {
var out = pulse.fork(pulse.ALL),
list = SortedList(tupleid, this.value, out.materialize(out.ADD).add),
sort = _.sort,
mod = pulse.changed() || (sort &&
(_.modified('sort') || pulse.modified(sort.fields)));
out.visit(out.REM, list.remove);
this.modified(mod);
this.value = out.source = list.data(sort, mod);
return out;
};
/**
* Generates a comparator function.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<string>} params.fields - The fields to compare.
* @param {Array<string>} [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<n; ++i) {
if (!stop.test(t = tokens[i])) update(t);
}
};
}
var init = this._parameterCheck(_, pulse),
counts = this._counts,
match = this._match,
stop = this._stop,
get = _.field,
as = _.as || ['text', 'count'],
add = process(function(t) { counts[t] = 1 + (counts[t] || 0); }),
rem = process(function(t) { counts[t] -= 1; });
if (init) {
pulse.visit(pulse.SOURCE, add);
} else {
pulse.visit(pulse.ADD, add);
pulse.visit(pulse.REM, rem);
}
return this._finish(pulse, as); // generate output tuples
};
prototype$12._parameterCheck = function(_, pulse) {
var init = false;
if (_.modified('stopwords') || !this._stop) {
this._stop = new RegExp('^' + (_.stopwords || '') + '$', 'i');
init = true;
}
if (_.modified('pattern') || !this._match) {
this._match = new RegExp((_.pattern || '[\\w\']+'), 'g');
init = true;
}
if (_.modified('field') || pulse.modified(_.field.fields)) {
init = true;
}
if (init) this._counts = {};
return init;
};
prototype$12._finish = function(pulse, as) {
var counts = this._counts,
tuples = this._tuples || (this._tuples = {}),
text = as[0],
count = as[1],
out = pulse.fork(),
w, t, c;
for (w in counts) {
t = tuples[w];
c = counts[w] || 0;
if (!t && c) {
tuples[w] = (t = ingest({}));
t[text] = w;
t[count] = c;
out.add.push(t);
} else if (c === 0) {
if (t) out.rem.push(t);
counts[w] = null;
tuples[w] = null;
} else if (t[count] !== c) {
t[count] = c;
out.mod.push(t);
}
}
return out.modifies(as);
};
/**
* Perform a cross-product of a tuple stream with itself.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object):boolean} [params.filter] - An optional filter
* function for selectively including tuples in the cross product.
* @param {Array<string>} [params.as] - The names of the output fields.
*/
function Cross(params) {
Transform.call(this, null, params);
}
Cross.Definition = {
"type": "Cross",
"metadata": {"source": true, "generates": true, "changes": 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;
out.add = this.value = cross$1(pulse.source, 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<n; ++i) {
t[a] = left = input[i];
for (j=0; j<n; ++j) {
t[b] = input[j];
if (filter(t)) {
data.push(ingest(t));
t = {};
t[a] = left;
}
}
}
return data;
}
var Distributions = {
kde: randomKDE,
mixture: randomMixture,
normal: randomNormal,
uniform: randomUniform
};
var DISTRIBUTIONS = 'distributions';
var FUNCTION = 'function';
var FIELD = 'field';
/**
* Parse a parameter object for a probability distribution.
* @param {object} def - The distribution parameter object.
* @param {function():Array<object>} - 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<number>} [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, "source": 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 < 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<n; ++i) f(a[i], i, a);
};
}
var prototype$16 = inherits(Facet, Transform);
prototype$16.activate = function(flow) {
this._targets[this._targets.active++] = flow;
};
prototype$16.subflow = function(key$$1, flow, pulse, parent) {
var flows = this.value,
sf = flows.hasOwnProperty(key$$1) && flows[key$$1],
df, p;
if (!sf) {
p = parent || (p = this._group[key$$1]) && p.tuple;
df = pulse.dataflow;
sf = df.add(new Subflow(pulse.fork(pulse.NO_SOURCE), this))
.connect(flow(df, key$$1, p));
flows[key$$1] = sf;
this.activate(sf);
} else if (sf.value.stamp < pulse.stamp) {
sf.init(pulse);
this.activate(sf);
}
return sf;
};
prototype$16.transform = function(_, pulse) {
var df = pulse.dataflow,
self = this,
key$$1 = _.key,
flow = _.subflow,
cache = this._keys,
rekey = _.modified('key');
function subflow(key$$1) {
return self.subflow(key$$1, flow, pulse);
}
this._group = _.group || {};
this._targets.active = 0; // reset list of active subflows
pulse.visit(pulse.REM, function(t) {
var id$$1 = tupleid(t),
k = cache.get(id$$1);
if (k !== undefined) {
cache.delete(id$$1);
subflow(k).rem(t);
}
});
pulse.visit(pulse.ADD, function(t) {
var k = key$$1(t);
cache.set(tupleid(t), k);
subflow(k).add(t);
});
if (rekey || pulse.modified(key$$1.fields)) {
pulse.visit(pulse.MOD, function(t) {
var id$$1 = tupleid(t),
k0 = cache.get(id$$1),
k1 = key$$1(t);
if (k0 === k1) {
subflow(k1).mod(t);
} else {
cache.set(id$$1, k1);
subflow(k0).rem(t);
subflow(k1).add(t);
}
});
} else if (pulse.changed(pulse.MOD)) {
pulse.visit(pulse.MOD, function(t) {
subflow(cache.get(tupleid(t))).mod(t);
});
}
if (rekey) {
pulse.visit(pulse.REFLOW, function(t) {
var id$$1 = tupleid(t),
k0 = cache.get(id$$1),
k1 = key$$1(t);
if (k0 !== k1) {
cache.set(id$$1, k1);
subflow(k0).rem(t);
subflow(k1).add(t);
}
});
}
if (cache.empty > 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;
};
/**
* Folds one more tuple fields into multiple tuples in which the field
* name and values are available under new 'key' and 'value' fields.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.fields - An array of field accessors
* for the tuple fields that should be folded.
*/
function Fold(params) {
Transform.call(this, {}, params);
}
Fold.Definition = {
"type": "Fold",
"metadata": {"generates": true, "changes": true},
"params": [
{ "name": "fields", "type": "field", "array": true, "required": true },
{ "name": "as", "type": "string", "array": true, "length": 2, "default": ["key", "value"] }
]
};
var prototype$19 = inherits(Fold, Transform);
function keyFunction(f) {
return f.fields.join('|');
}
prototype$19.transform = function(_, pulse) {
var cache = this.value,
reset = _.modified('fields'),
fields = _.fields,
as = _.as || ['key', 'value'],
key$$1 = as[0],
value = as[1],
keys = fields.map(keyFunction),
n = fields.length,
stamp = pulse.stamp,
out = pulse.fork(pulse.NO_SOURCE),
i = 0, mask = 0, id$$1;
function add(t) {
var f = (cache[tupleid(t)] = Array(n)); // create cache of folded tuples
for (var i=0, ft; i<n; ++i) { // for each key, derive folds
ft = (f[i] = derive(t));
ft[key$$1] = keys[i];
ft[value] = fields[i](t);
out.add.push(ft);
}
}
function mod(t) {
var f = cache[tupleid(t)]; // get cache of folded tuples
for (var i=0, ft; i<n; ++i) { // for each key, rederive folds
if (!(mask & (1 << i))) continue; // field is unchanged
ft = rederive(t, f[i], stamp);
ft[key$$1] = keys[i];
ft[value] = fields[i](t);
out.mod.push(ft);
}
}
if (reset) {
// on reset, remove all folded tuples and clear cache
for (id$$1 in cache) out.rem.push.apply(out.rem, cache[id$$1]);
cache = this.value = {};
pulse.visit(pulse.SOURCE, add);
} else {
pulse.visit(pulse.ADD, add);
for (; i<n; ++i) {
if (pulse.modified(fields[i].fields)) mask |= (1 << i);
}
if (mask) pulse.visit(pulse.MOD, mod);
pulse.visit(pulse.REM, function(t) {
var id$$1 = tupleid(t);
out.rem.push.apply(out.rem, cache[id$$1]);
cache[id$$1] = null;
});
}
return out.modifies(as);
};
/**
* Invokes a function for each data tuple and saves the results as a new field.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.expr - The formula function to invoke for each tuple.
* @param {string} params.as - The field name under which to save the result.
* @param {boolean} [params.initonly=false] - If true, the formula is applied to
* added tuples only, and does not update in response to modifications.
*/
function Formula(params) {
Transform.call(this, null, params);
}
Formula.Definition = {
"type": "Formula",
"metadata": {"modifies": true},
"params": [
{ "name": "expr", "type": "expr", "required": true },
{ "name": "as", "type": "string", "required": true },
{ "name": "initonly", "type": "boolean" }
]
};
var prototype$20 = inherits(Formula, Transform);
prototype$20.transform = function(_, pulse) {
var func = _.expr,
as = _.as,
mod = _.modified(),
flag = _.initonly ? pulse.ADD
: mod ? pulse.SOURCE
: pulse.modified(func.fields) ? pulse.ADD_MOD
: pulse.ADD;
function set(t) {
t[as] = func(t, _);
}
if (mod) {
// parameters updated, need to reflow
pulse = pulse.materialize().reflow(true);
}
if (!_.initonly) {
pulse.modifies(as);
}
return pulse.visit(flag, set);
};
/**
* Generates data tuples using a provided generator function.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(Parameters): object} params.generator - A tuple generator
* function. This function is given the operator parameters as input.
* Changes to any additional parameters will not trigger re-calculation
* of previously generated tuples. Only future tuples are affected.
* @param {number} params.size - The number of tuples to produce.
*/
function Generate(params) {
Transform.call(this, [], params);
}
var prototype$21 = inherits(Generate, Transform);
prototype$21.transform = function(_, pulse) {
var data = this.value,
out = pulse.fork(pulse.ALL),
num = _.size - data.length,
gen = _.generator,
add, rem, t;
if (num > 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<function(object): *>} [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": {"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$22 = 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$22.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<l; ++g) {
group = groups[g];
gVals = group.values;
value = NaN;
// add tuples for missing values
for (j=0; j<m; ++j) {
if (group[j] != null) continue;
kVal = groups.domain[j];
t = {_impute: true};
for (i=0, n=gVals.length; i<n; ++i) t[gNames[i]] = gVals[i];
t[kName] = kVal;
t[fName] = isNaN(value) ? (value = impute(group, field$$1)) : value;
curr.push(ingest(t));
}
}
// update pulse with imputed tuples
if (curr.length) out.add = out.materialize(out.ADD).add.concat(curr);
if (prev.length) out.rem = out.materialize(out.REM).rem.concat(prev);
this.value = curr;
return out;
};
function partition(data, groupby, key$$1, keyvals) {
var get = function(f) { return f(t); },
groups = [],
domain = keyvals ? keyvals.slice() : [],
kMap = {},
gMap = {}, gVals, gKey,
group, i, j, k, n, t;
domain.forEach(function(k, i) { kMap[k] = i + 1; });
for (i=0, n=data.length; i<n; ++i) {
t = data[i];
k = key$$1(t);
j = kMap[k] || (kMap[k] = domain.push(k));
gKey = (gVals = groupby ? groupby.map(get) : Empty) + '';
if (!(group = gMap[gKey])) {
group = (gMap[gKey] = []);
groups.push(group);
group.values = gVals;
}
group[j-1] = t;
}
groups.domain = domain;
return groups;
}
/**
* Extend input tuples with aggregate values.
* Calcuates aggregate values and joins them with the input stream.
* @constructor
*/
function JoinAggregate(params) {
Aggregate.call(this, params);
}
JoinAggregate.Definition = {
"type": "JoinAggregate",
"metadata": {"modifies": true},
"params": [
{ "name": "groupby", "type": "field", "array": true },
{ "name": "fields", "type": "field", "null": true, "array": true },
{ "name": "ops", "type": "enum", "array": true, "values": ValidAggregateOps },
{ "name": "as", "type": "string", "null": true, "array": true },
{ "name": "key", "type": "field" }
]
};
var prototype$23 = inherits(JoinAggregate, Aggregate);
prototype$23.transform = function(_, pulse) {
var aggr = this,
mod = _.modified(),
cells;
// process all input tuples to calculate aggregates
if (aggr.value && (mod || pulse.modified(aggr._inputs))) {
cells = aggr.value = mod ? aggr.init(_) : {};
pulse.visit(pulse.SOURCE, function(t) { aggr.add(t); });
} else {
cells = aggr.value = aggr.value || this.init(_);
pulse.visit(pulse.REM, function(t) { aggr.rem(t); });
pulse.visit(pulse.ADD, function(t) { aggr.add(t); });
}
// update aggregation cells
aggr.changes();
// write aggregate values to input tuples
pulse.visit(pulse.SOURCE, function(t) {
extend(t, cells[aggr.cellkey(t)].tuple);
});
return pulse.reflow(mod).modifies(this._outputs);
};
prototype$23.changes = function() {
var adds = this._adds,
mods = this._mods,
i, n;
for (i=0, n=this._alen; i<n; ++i) {
this.celltuple(adds[i]);
adds[i] = null; // for garbage collection
}
for (i=0, n=this._mlen; i<n; ++i) {
this.celltuple(mods[i]);
mods[i] = null; // for garbage collection
}
this._alen = this._mlen = 0; // reset list of active cells
};
/**
* Generates a key function.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<string>} params.fields - The field name(s) for the key function.
*/
function Key(params) {
Operator.call(this, null, update$3, params);
}
inherits(Key, Operator);
function update$3(_) {
return (this.value && !_.modified()) ? this.value : key(_.fields);
}
/**
* 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<function(object): *} params.fields - The fields to lookup.
* @param {Array<string>} 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$24 = inherits(Lookup, Transform);
prototype$24.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<n; ++i) {
v = index.get(keys[i](t));
if (v == null) for (j=0; j<m; ++j, ++k) t[as[k]] = defaultValue;
else for (j=0; j<m; ++j, ++k) t[as[k]] = values[j](v);
}
};
} else {
if (!as) {
error$1('Missing output field names.');
}
set = function(t) {
for (var i=0, v; i<n; ++i) {
v = index.get(keys[i](t));
t[as[i]] = v==null ? defaultValue : v;
}
};
}
if (reset) {
out = pulse.reflow(true);
} else {
mods = keys.some(function(k) { return pulse.modified(k.fields); });
flag |= (mods ? pulse.MOD : 0);
}
pulse.visit(flag, set);
return out.modifies(as);
};
/**
* Computes global min/max extents over a collection of extents.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<Array<number>>} 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<n; ++i) {
e = ext[i];
if (e[0] < min) min = e[0];
if (e[1] > max) max = e[1];
}
return [min, max];
}
/**
* Merge a collection of value arrays.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<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
};
/**
* 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<object>} params.field - The field
* accessor for an array of subflow tuple objects.
*/
function PreFacet(params) {
Facet.call(this, params);
}
var prototype$25 = inherits(PreFacet, Facet);
prototype$25.transform = function(_, pulse) {
var self = this,
flow = _.subflow,
field$$1 = _.field;
if (_.modified('field') || field$$1 && pulse.modified(accessorFields(field$$1))) {
error$1('PreFacet does not support field modification.');
}
this._targets.active = 0; // reset list of active subflows
pulse.visit(pulse.MOD, function(t) {
var sf = self.subflow(tupleid(t), flow, pulse, t);
field$$1 ? field$$1(t).forEach(function(_) { sf.mod(_); }) : sf.mod(t);
});
pulse.visit(pulse.ADD, function(t) {
var sf = self.subflow(tupleid(t), flow, pulse, t);
field$$1 ? field$$1(t).forEach(function(_) { sf.add(ingest(_)); }) : sf.add(t);
});
pulse.visit(pulse.REM, function(t) {
var sf = self.subflow(tupleid(t), flow, pulse, t);
field$$1 ? field$$1(t).forEach(function(_) { sf.rem(_); }) : sf.rem(t);
});
return pulse;
};
/**
* Proxy the value of another operator as a pure signal value.
* Ensures no tuples are propagated.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {*} params.value - The value to proxy, becomes the value of this operator.
*/
function Proxy(params) {
Transform.call(this, null, params);
}
var prototype$26 = inherits(Proxy, Transform);
prototype$26.transform = function(_, pulse) {
this.value = _.value;
return _.modified('value')
? pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS)
: pulse.StopPropagation;
};
/**
* Relays a data stream between data processing pipelines.
* If the derive parameter is set, this transform will create derived
* copies of observed tuples. This provides derived data streams in which
* modifications to the tuples do not pollute an upstream data source.
* @param {object} params - The parameters for this operator.
* @param {number} [params.derive=false] - Boolean flag indicating if
* the transform should make derived copies of incoming tuples.
* @constructor
*/
function Relay(params) {
Transform.call(this, null, params);
}
var prototype$27 = inherits(Relay, Transform);
prototype$27.transform = function(_, pulse) {
var out, lut;
if (this.value) {
lut = this.value;
} else {
out = pulse = pulse.addAll();
lut = this.value = {};
}
if (_.derive) {
out = pulse.fork();
pulse.visit(pulse.REM, function(t) {
var id$$1 = tupleid(t);
out.rem.push(lut[id$$1]);
lut[id$$1] = null;
});
pulse.visit(pulse.ADD, function(t) {
var dt = derive(t);
lut[tupleid(t)] = dt;
out.add.push(dt);
});
pulse.visit(pulse.MOD, function(t) {
out.mod.push(rederive(t, lut[tupleid(t)]));
});
}
return out;
};
/**
* Samples tuples passing through this operator.
* Uses reservoir sampling to maintain a representative sample.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {number} [params.size=1000] - The maximum number of samples.
*/
function Sample(params) {
Transform.call(this, [], params);
this.count = 0;
}
Sample.Definition = {
"type": "Sample",
"metadata": {"source": true, "changes": true},
"params": [
{ "name": "size", "type": "number", "default": 1000 }
]
};
var prototype$28 = inherits(Sample, Transform);
prototype$28.transform = function(_, pulse) {
var out = pulse.fork(),
mod = _.modified('size'),
num = _.size,
res = this.value,
cnt = this.count,
cap = 0,
map = res.reduce(function(m, t) {
m[tupleid(t)] = 1;
return m;
}, {});
// sample reservoir update function
function update(t) {
var p, idx;
if (res.length < num) {
res.push(t);
} else {
idx = ~~((cnt + 1) * Math.random());
if (idx < res.length && idx >= cap) {
p = res[idx];
if (map[tupleid(p)]) out.rem.push(p); // eviction
res[idx] = t;
}
}
++cnt;
}
if (pulse.rem.length) {
// find all tuples that should be removed, add to output
pulse.visit(pulse.REM, function(t) {
var id$$1 = tupleid(t);
if (map[id$$1]) {
map[id$$1] = -1;
out.rem.push(t);
}
--cnt;
});
// filter removed tuples out of the sample reservoir
res = res.filter(function(t) { return map[tupleid(t)] !== -1; });
}
if ((pulse.rem.length || mod) && res.length < num && pulse.source) {
// replenish sample if backing data source is available
cap = cnt = res.length;
pulse.visit(pulse.SOURCE, function(t) {
// update, but skip previously sampled tuples
if (!map[tupleid(t)]) update(t);
});
cap = -1;
}
if (mod && res.length > num) {
for (var i=0, n=res.length-num; i<n; ++i) {
map[tupleid(res[i])] = -1;
out.rem.push(res[i]);
}
res = res.slice(n);
}
if (pulse.mod.length) {
// propagate modified tuples in the sample reservoir
pulse.visit(pulse.MOD, function(t) {
if (map[tupleid(t)]) out.mod.push(t);
});
}
if (pulse.add.length) {
// update sample reservoir
pulse.visit(pulse.ADD, update);
}
if (pulse.add.length || cap < 0) {
// output newly added tuples
out.add = res.filter(function(t) { return !map[tupleid(t)]; });
}
this.count = cnt;
this.value = out.source = res;
return out;
};
/**
* Generates data tuples for a specified sequence range of numbers.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {number} params.start - The first number in the sequence.
* @param {number} params.stop - The last number (exclusive) in the sequence.
* @param {number} [params.step=1] - The step size between numbers in the sequence.
*/
function Sequence(params) {
Transform.call(this, null, params);
}
Sequence.Definition = {
"type": "Sequence",
"metadata": {"generates": true, "source": true},
"params": [
{ "name": "start", "type": "number", "required": true },
{ "name": "stop", "type": "number", "required": true },
{ "name": "step", "type": "number", "default": 1 }
],
"output": ["value"]
};
var prototype$29 = inherits(Sequence, Transform);
prototype$29.transform = function(_, pulse) {
if (this.value && !_.modified()) return;
var out = pulse.materialize().fork(pulse.MOD);
out.rem = this.value ? pulse.rem.concat(this.value) : pulse.rem;
out.source = this.value = sequence(_.start, _.stop, _.step || 1).map(ingest);
out.add = pulse.add.concat(this.value);
return out;
};
/**
* Propagates a new pulse without any tuples so long as the input
* pulse contains some added, removed or modified tuples.
* @param {object} params - The parameters for this operator.
* @constructor
*/
function Sieve(params) {
Transform.call(this, null, params);
this.modified(true); // always treat as modified
}
var prototype$30 = inherits(Sieve, Transform);
prototype$30.transform = function(_, pulse) {
this.value = pulse.source;
return pulse.changed()
? pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS)
: pulse.StopPropagation;
};
/**
* An index that maps from unique, string-coerced, field values to tuples.
* Assumes that the field serves as a unique key with no duplicate values.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The field accessor to index.
*/
function TupleIndex(params) {
Transform.call(this, fastmap(), params);
}
var prototype$31 = inherits(TupleIndex, Transform);
prototype$31.transform = function(_, pulse) {
var df = pulse.dataflow,
field$$1 = _.field,
index = this.value,
mod = true;
function set(t) { index.set(field$$1(t), t); }
if (_.modified('field') || pulse.modified(field$$1.fields)) {
index.clear();
pulse.visit(pulse.SOURCE, set);
} else if (pulse.changed()) {
pulse.visit(pulse.REM, function(t) { index.delete(field$$1(t)); });
pulse.visit(pulse.ADD, set);
} else {
mod = false;
}
this.modified(mod);
if (index.empty > df.cleanThreshold) df.runAfter(index.clean);
return pulse.fork();
};
/**
* Extracts an array of values. Assumes the source data has already been
* reduced as needed (e.g., by an upstream Aggregate transform).
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The domain field to extract.
* @param {function(*,*): number} [params.sort] - An optional
* comparator function for sorting the values. The comparator will be
* applied to backing tuples prior to value extraction.
*/
function Values(params) {
Transform.call(this, null, params);
}
var prototype$32 = inherits(Values, Transform);
prototype$32.transform = function(_, pulse) {
var run = !this.value
|| _.modified('field')
|| _.modified('sort')
|| pulse.changed()
|| (_.sort && pulse.modified(_.sort.fields));
if (run) {
this.value = (_.sort
? pulse.source.slice().sort(_.sort)
: pulse.source).map(_.field);
}
};
function WindowOp(op, field$$1, param, as) {
var fn = WindowOps[op](field$$1, param);
return {
init: fn.init || zero,
update: function(w, t) { t[as] = fn.next(w); }
};
}
var WindowOps = {
row_number: function() {
return {
next: function(w) { return w.index + 1; }
};
},
rank: function() {
var rank;
return {
init: function() { rank = 1; },
next: function(w) {
var i = w.index,
data = w.data;
return (i && w.compare(data[i - 1], data[i])) ? (rank = i + 1) : rank;
}
};
},
dense_rank: function() {
var drank;
return {
init: function() { drank = 1; },
next: function(w) {
var i = w.index,
d = w.data;
return (i && w.compare(d[i - 1], d[i])) ? ++drank : drank;
}
};
},
percent_rank: function() {
var rank = WindowOps.rank(),
next = rank.next;
return {
init: rank.init,
next: function(w) {
return (next(w) - 1) / (w.data.length - 1);
}
};
},
cume_dist: function() {
var cume;
return {
init: function() { cume = 0; },
next: function(w) {
var i = w.index,
d = w.data,
c = w.compare;
if (cume < i) {
while (i + 1 < d.length && !c(d[i], d[i + 1])) ++i;
cume = i;
}
return (1 + cume) / d.length;
}
};
},
ntile: function(field$$1, num) {
num = +num;
if (!(num > 0)) error$1('ntile num must be greater than zero.');
var cume = WindowOps.cume_dist(),
next = cume.next;
return {
init: cume.init,
next: function(w) { return Math.ceil(num * next(w)); }
};
},
lag: function(field$$1, offset) {
offset = +offset || 1;
return {
next: function(w) {
var i = w.index - offset;
return i >= 0 ? field$$1(w.data[i]) : null;
}
};
},
lead: function(field$$1, offset) {
offset = +offset || 1;
return {
next: function(w) {
var i = w.index + offset,
d = w.data;
return i < d.length ? field$$1(d[i]) : null;
}
};
},
first_value: function(field$$1) {
return {
next: function(w) { return field$$1(w.data[w.i0]); }
};
},
last_value: function(field$$1) {
return {
next: function(w) { return field$$1(w.data[w.i1 - 1]); }
}
},
nth_value: function(field$$1, nth) {
nth = +nth;
if (!(nth > 0)) error$1('nth_value nth must be greater than zero.');
return {
next: function(w) {
var i = w.i0 + (nth - 1);
return i < w.i1 ? field$$1(w.data[i]) : null;
}
}
}
};
var ValidWindowOps = Object.keys(WindowOps);
function WindowState(_) {
var self = this,
ops = array(_.ops),
fields = array(_.fields),
params = array(_.params),
as = array(_.as),
outputs = self.outputs = [],
windows = self.windows = [],
inputs = {},
map = {},
countOnly = true,
counts = [],
measures = [];
function visitInputs(f) {
array(accessorFields(f)).forEach(function(_) { inputs[_] = 1; });
}
visitInputs(_.sort);
ops.forEach(function(op, i) {
var field$$1 = fields[i],
mname = accessorName(field$$1),
name = measureName(op, mname, as[i]);
visitInputs(field$$1);
outputs.push(name);
// Window operation
if (WindowOps.hasOwnProperty(op)) {
windows.push(WindowOp(op, fields[i], params[i], name));
}
// Aggregate operation
else {
if (field$$1 == null && op !== 'count') {
error$1('Null aggregate field specified.');
}
if (op === 'count') {
counts.push(name);
return;
}
countOnly = false;
var m = map[mname];
if (!m) {
m = (map[mname] = []);
m.field = field$$1;
measures.push(m);
}
m.push(createMeasure(op, name));
}
});
if (counts.length || measures.length) {
self.cell = cell(measures, counts, countOnly);
}
self.inputs = Object.keys(inputs);
}
var prototype$34 = WindowState.prototype;
prototype$34.init = function() {
this.windows.forEach(function(_) { _.init(); });
if (this.cell) this.cell.init();
};
prototype$34.update = function(w, t) {
var self = this,
cell = self.cell,
wind = self.windows,
data = w.data,
m = wind && wind.length,
j;
if (cell) {
for (j=w.p0; j<w.i0; ++j) cell.rem(data[j]);
for (j=w.p1; j<w.i1; ++j) cell.add(data[j]);
cell.set(t);
}
for (j=0; j<m; ++j) wind[j].update(w, t);
};
function cell(measures, counts, countOnly) {
measures = measures.map(function(m) {
return compileMeasures(m, m.field);
});
var cell = {
num: 0,
agg: null,
store: false,
count: counts
};
if (!countOnly) {
var n = measures.length,
a = cell.agg = Array(n),
i = 0;
for (; i<n; ++i) a[i] = new measures[i](cell);
}
if (cell.store) {
var store = cell.data = new TupleStore();
}
cell.add = function(t) {
cell.num += 1;
if (countOnly) return;
if (store) store.add(t);
for (var i=0; i<n; ++i) {
a[i].add(a[i].get(t), t);
}
};
cell.rem = function(t) {
cell.num -= 1;
if (countOnly) return;
if (store) store.rem(t);
for (var i=0; i<n; ++i) {
a[i].rem(a[i].get(t), t);
}
};
cell.set = function(t) {
var i, n;
// consolidate stored values
if (store) store.values();
// update tuple properties
for (i=0, n=counts.length; i<n; ++i) t[counts[i]] = cell.num;
if (!countOnly) for (i=0, n=a.length; i<n; ++i) a[i].set(t);
};
cell.init = function() {
cell.num = 0;
if (store) store.reset();
for (var i=0; i<n; ++i) a[i].init();
};
return cell;
}
/**
* Perform window calculations and write results to the input stream.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(*,*): number} [params.sort] - A comparator function for sorting tuples within a window.
* @param {Array<function(object): *>} [params.groupby] - An array of accessors by which to partition tuples into separate windows.
* @param {Array<string>} params.ops - An array of strings indicating window operations to perform.
* @param {Array<function(object): *>} [params.fields] - An array of accessors
* for data fields to use as inputs to window operations.
* @param {Array<*>} [params.params] - An array of parameter values for window operations.
* @param {Array<string>} [params.as] - An array of output field names for window operations.
* @param {Array<number>} [params.frame] - Window frame definition as two-element array.
* @param {boolean} [params.ignorePeers=false] - If true, base window frame boundaries on row
* number alone, ignoring peers with identical sort values. If false (default),
* the window boundaries will be adjusted to include peer values.
*/
function Window(params) {
Transform.call(this, {}, params);
this._mlen = 0;
this._mods = [];
}
Window.Definition = {
"type": "Window",
"metadata": {"modifies": true},
"params": [
{ "name": "sort", "type": "compare" },
{ "name": "groupby", "type": "field", "array": true },
{ "name": "ops", "type": "enum", "array": true, "values": ValidWindowOps.concat(ValidAggregateOps) },
{ "name": "params", "type": "number", "null": true, "array": true },
{ "name": "fields", "type": "field", "null": true, "array": true },
{ "name": "as", "type": "string", "null": true, "array": true },
{ "name": "frame", "type": "number", "null": true, "array": true, "length": 2, "default": [null, 0] },
{ "name": "ignorePeers", "type": "boolean", "default": false }
]
};
var prototype$33 = inherits(Window, Transform);
prototype$33.transform = function(_, pulse) {
var self = this,
state = self.state,
mod = _.modified(),
i, n;
this.stamp = pulse.stamp;
// initialize window state
if (!state || mod) {
state = self.state = new WindowState(_);
}
// retrieve group for a tuple
var key$$1 = groupkey(_.groupby);
function group(t) { return self.group(key$$1(t)); }
// partition input tuples
if (mod || pulse.modified(state.inputs)) {
self.value = {};
pulse.visit(pulse.SOURCE, function(t) { group(t).add(t); });
} else {
pulse.visit(pulse.REM, function(t) { group(t).remove(t); });
pulse.visit(pulse.ADD, function(t) { group(t).add(t); });
}
// perform window calculations for each modified partition
for (i=0, n=self._mlen; i<n; ++i) {
processPartition(self._mods[i], state, _);
}
self._mlen = 0;
self._mods = [];
// TODO don't reflow everything?
return pulse.reflow(mod).modifies(state.outputs);
};
prototype$33.group = function(key$$1) {
var self = this,
group = self.value[key$$1];
if (!group) {
group = self.value[key$$1] = SortedList(tupleid);
group.stamp = -1;
}
if (group.stamp < self.stamp) {
group.stamp = self.stamp;
self._mods[self._mlen++] = group;
}
return group;
};
function processPartition(list, state, _) {
var sort = _.sort,
range = sort && !_.ignorePeers,
frame = _.frame || [null, 0],
data = list.data(sort),
n = data.length,
i = 0,
b = range ? bisector(sort) : null,
w = {
i0: 0, i1: 0, p0: 0, p1: 0, index: 0,
data: data, compare: sort || constant(-1)
};
for (state.init(); i<n; ++i) {
setWindow(w, frame, i, n);
if (range) adjustRange(w, b);
state.update(w, data[i]);
}
}
function setWindow(w, f, i, n) {
w.p0 = w.i0;
w.p1 = w.i1;
w.i0 = f[0] == null ? 0 : Math.max(0, i - Math.abs(f[0]));
w.i1 = f[1] == null ? n : Math.min(n, i + Math.abs(f[1]) + 1);
w.index = i;
}
// if frame type is 'range', adjust window for peer values
function adjustRange(w, bisect) {
var r0 = w.i0,
r1 = w.i1 - 1,
c = w.compare,
d = w.data,
n = d.length - 1;
if (r0 > 0 && !c(d[r0], d[r0-1])) w.i0 = bisect.left(d, d[r0]);
if (r1 < n && !c(d[r1], d[r1+1])) w.i1 = bisect.right(d, d[r1]);
}
var tx = Object.freeze({
aggregate: Aggregate,
bin: Bin,
collect: Collect,
compare: Compare,
countpattern: CountPattern,
cross: Cross,
density: Density,
extent: Extent,
facet: Facet,
field: Field,
filter: Filter,
fold: Fold,
formula: Formula,
generate: Generate,
impute: Impute,
joinaggregate: JoinAggregate,
key: Key,
lookup: Lookup,
multiextent: MultiExtent,
multivalues: MultiValues,
params: Params,
prefacet: PreFacet,
proxy: Proxy,
relay: Relay,
sample: Sample,
sequence: Sequence,
sieve: Sieve,
subflow: Subflow,
tupleindex: TupleIndex,
values: Values,
window: Window
});
function Bounds(b) {
this.clear();
if (b) this.union(b);
}
var prototype$36 = Bounds.prototype;
prototype$36.clone = function() {
return new Bounds(this);
};
prototype$36.clear = function() {
this.x1 = +Number.MAX_VALUE;
this.y1 = +Number.MAX_VALUE;
this.x2 = -Number.MAX_VALUE;
this.y2 = -Number.MAX_VALUE;
return this;
};
prototype$36.empty = function() {
return (
this.x1 === +Number.MAX_VALUE &&
this.y1 === +Number.MAX_VALUE &&
this.x2 === -Number.MAX_VALUE &&
this.y2 === -Number.MAX_VALUE
);
};
prototype$36.set = function(x1, y1, x2, y2) {
if (x2 < x1) {
this.x2 = x1;
this.x1 = x2;
} else {
this.x1 = x1;
this.x2 = x2;
}
if (y2 < y1) {
this.y2 = y1;
this.y1 = y2;
} else {
this.y1 = y1;
this.y2 = y2;
}
return this;
};
prototype$36.add = function(x, y) {
if (x < this.x1) this.x1 = x;
if (y < this.y1) this.y1 = y;
if (x > this.x2) this.x2 = x;
if (y > this.y2) this.y2 = y;
return this;
};
prototype$36.expand = function(d) {
this.x1 -= d;
this.y1 -= d;
this.x2 += d;
this.y2 += d;
return this;
};
prototype$36.round = function() {
this.x1 = Math.floor(this.x1);
this.y1 = Math.floor(this.y1);
this.x2 = Math.ceil(this.x2);
this.y2 = Math.ceil(this.y2);
return this;
};
prototype$36.translate = function(dx, dy) {
this.x1 += dx;
this.x2 += dx;
this.y1 += dy;
this.y2 += dy;
return this;
};
prototype$36.rotate = function(angle, x, y) {
var cos = Math.cos(angle),
sin = Math.sin(angle),
cx = x - x*cos + y*sin,
cy = y - x*sin - y*cos,
x1 = this.x1, x2 = this.x2,
y1 = this.y1, y2 = this.y2;
return this.clear()
.add(cos*x1 - sin*y1 + cx, sin*x1 + cos*y1 + cy)
.add(cos*x1 - sin*y2 + cx, sin*x1 + cos*y2 + cy)
.add(cos*x2 - sin*y1 + cx, sin*x2 + cos*y1 + cy)
.add(cos*x2 - sin*y2 + cx, sin*x2 + cos*y2 + cy);
};
prototype$36.union = function(b) {
if (b.x1 < this.x1) this.x1 = b.x1;
if (b.y1 < this.y1) this.y1 = b.y1;
if (b.x2 > this.x2) this.x2 = b.x2;
if (b.y2 > this.y2) this.y2 = b.y2;
return this;
};
prototype$36.intersect = function(b) {
if (b.x1 > this.x1) this.x1 = b.x1;
if (b.y1 > this.y1) this.y1 = b.y1;
if (b.x2 < this.x2) this.x2 = b.x2;
if (b.y2 < this.y2) this.y2 = b.y2;
return this;
};
prototype$36.encloses = function(b) {
return b && (
this.x1 <= b.x1 &&
this.x2 >= b.x2 &&
this.y1 <= b.y1 &&
this.y2 >= b.y2
);
};
prototype$36.alignsWith = function(b) {
return b && (
this.x1 == b.x1 ||
this.x2 == b.x2 ||
this.y1 == b.y1 ||
this.y2 == b.y2
);
};
prototype$36.intersects = function(b) {
return b && !(
this.x2 < b.x1 ||
this.x1 > b.x2 ||
this.y2 < b.y1 ||
this.y1 > b.y2
);
};
prototype$36.contains = function(x, y) {
return !(
x < this.x1 ||
x > this.x2 ||
y < this.y1 ||
y > this.y2
);
};
prototype$36.width = function() {
return this.x2 - this.x1;
};
prototype$36.height = function() {
return this.y2 - this.y1;
};
var gradient_id = 0;
var Gradient = function(p0, p1) {
var stops = [], gradient;
return gradient = {
id: 'gradient_' + (gradient_id++),
x1: p0 ? p0[0] : 0,
y1: p0 ? p0[1] : 0,
x2: p1 ? p1[0] : 1,
y2: p1 ? p1[1] : 0,
stops: stops,
stop: function(offset, color) {
stops.push({offset: offset, color: color});
return gradient;
}
};
};
function Item(mark) {
this.mark = mark;
this.bounds = (this.bounds || new Bounds());
}
function GroupItem(mark) {
Item.call(this, mark);
this.items = (this.items || []);
}
inherits(GroupItem, Item);
// create a new DOM element
function domCreate(doc, tag, ns) {
if (!doc && typeof document !== 'undefined' && document.createElement) {
doc = document;
}
return doc
? (ns ? doc.createElementNS(ns, tag) : doc.createElement(tag))
: null;
}
// find first child element with matching tag
function domFind(el, tag) {
tag = tag.toLowerCase();
var nodes = el.childNodes, i = 0, n = nodes.length;
for (; i<n; ++i) if (nodes[i].tagName.toLowerCase() === tag) {
return nodes[i];
}
}
// retrieve child element at given index
// create & insert if doesn't exist or if tags do not match
function domChild(el, index, tag, ns) {
var a = el.childNodes[index], b;
if (!a || a.tagName.toLowerCase() !== tag.toLowerCase()) {
b = a || null;
a = domCreate(el.ownerDocument, tag, ns);
el.insertBefore(a, b);
}
return a;
}
// remove all child elements at or above the given index
function domClear(el, index) {
var nodes = el.childNodes,
curr = nodes.length;
while (curr > index) el.removeChild(nodes[--curr]);
return el;
}
// generate css class name for mark
function cssClass(mark) {
return 'mark-' + mark.marktype
+ (mark.role ? ' role-' + mark.role : '')
+ (mark.name ? ' ' + mark.name : '');
}
var Canvas;
try { Canvas = require('canvas'); } catch (e) { Canvas = null; }
var Canvas$1 = function(w, h) {
var canvas = domCreate(null, 'canvas');
if (canvas && canvas.getContext) {
canvas.width = w;
canvas.height = h;
} else if (Canvas) {
try {
canvas = new Canvas(w, h);
} catch (e) {
canvas = null;
}
}
return canvas;
};
var Image$1 = typeof Image !== 'undefined' ? Image
: (Canvas && Canvas.Image || null);
function ResourceLoader(customLoader) {
this._pending = 0;
this._loader = customLoader || loader();
}
var prototype$37 = ResourceLoader.prototype;
prototype$37.pending = function() {
return this._pending;
};
function increment(loader$$1) {
loader$$1._pending += 1;
}
function decrement(loader$$1) {
loader$$1._pending -= 1;
}
prototype$37.sanitizeURL = function(uri) {
var loader$$1 = this;
increment(loader$$1);
return loader$$1._loader.sanitize(uri, {context:'href'})
.then(function(opt) {
decrement(loader$$1);
return opt;
})
.catch(function() {
decrement(loader$$1);
return null;
});
};
prototype$37.loadImage = function(uri) {
var loader$$1 = this;
increment(loader$$1);
return loader$$1._loader
.sanitize(uri, {context:'image'})
.then(function(opt) {
var url = opt.href;
if (!url || !Image$1) throw {url: url};
var image = new Image$1();
image.onload = function() {
decrement(loader$$1);
image.loaded = true;
};
image.onerror = function() {
decrement(loader$$1);
image.loaded = false;
};
image.src = url;
return image;
})
.catch(function(e) {
decrement(loader$$1);
return {loaded: false, width: 0, height: 0, src: e && e.url || ''};
});
};
prototype$37.ready = function() {
var loader$$1 = this;
return new Promise(function(accept) {
function poll(value) {
if (!loader$$1.pending()) accept(value);
else setTimeout(function() { poll(true); }, 10);
}
poll(false);
});
};
var pi = Math.PI;
var tau = 2 * pi;
var epsilon = 1e-6;
var tauEpsilon = tau - epsilon;
function Path() {
this._x0 = this._y0 = // start of current subpath
this._x1 = this._y1 = null; // end of current subpath
this._ = "";
}
function path() {
return new Path;
}
Path.prototype = path.prototype = {
constructor: Path,
moveTo: function(x, y) {
this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y);
},
closePath: function() {
if (this._x1 !== null) {
this._x1 = this._x0, this._y1 = this._y0;
this._ += "Z";
}
},
lineTo: function(x, y) {
this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y);
},
quadraticCurveTo: function(x1, y1, x, y) {
this._ += "Q" + (+x1) + "," + (+y1) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
},
bezierCurveTo: function(x1, y1, x2, y2, x, y) {
this._ += "C" + (+x1) + "," + (+y1) + "," + (+x2) + "," + (+y2) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
},
arcTo: function(x1, y1, x2, y2, r) {
x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;
var x0 = this._x1,
y0 = this._y1,
x21 = x2 - x1,
y21 = y2 - y1,
x01 = x0 - x1,
y01 = y0 - y1,
l01_2 = x01 * x01 + y01 * y01;
// Is the radius negative? Error.
if (r < 0) throw new Error("negative radius: " + r);
// Is this path empty? Move to (x1,y1).
if (this._x1 === null) {
this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1);
}
// Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
else if (!(l01_2 > epsilon)) {}
// Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
// Equivalently, is (x1,y1) coincident with (x2,y2)?
// Or, is the radius zero? Line to (x1,y1).
else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {
this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1);
}
// Otherwise, draw an arc!
else {
var x20 = x2 - x0,
y20 = y2 - y0,
l21_2 = x21 * x21 + y21 * y21,
l20_2 = x20 * x20 + y20 * y20,
l21 = Math.sqrt(l21_2),
l01 = Math.sqrt(l01_2),
l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
t01 = l / l01,
t21 = l / l21;
// If the start tangent is not coincident with (x0,y0), line to.
if (Math.abs(t01 - 1) > epsilon) {
this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01);
}
this._ += "A" + r + "," + r + ",0,0," + (+(y01 * x20 > x01 * y20)) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21);
}
},
arc: function(x, y, r, a0, a1, ccw) {
x = +x, y = +y, r = +r;
var dx = r * Math.cos(a0),
dy = r * Math.sin(a0),
x0 = x + dx,
y0 = y + dy,
cw = 1 ^ ccw,
da = ccw ? a0 - a1 : a1 - a0;
// Is the radius negative? Error.
if (r < 0) throw new Error("negative radius: " + r);
// Is this path empty? Move to (x0,y0).
if (this._x1 === null) {
this._ += "M" + x0 + "," + y0;
}
// Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {
this._ += "L" + x0 + "," + y0;
}
// Is this arc empty? We’re done.
if (!r) return;
// Does the angle go the wrong way? Flip the direction.
if (da < 0) da = da % tau + tau;
// Is this a complete circle? Draw two arcs to complete the circle.
if (da > tauEpsilon) {
this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0);
}
// Is this arc non-empty? Draw an arc!
else if (da > epsilon) {
this._ += "A" + r + "," + r + ",0," + (+(da >= pi)) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1));
}
},
rect: function(x, y, w, h) {
this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + (+w) + "v" + (+h) + "h" + (-w) + "Z";
},
toString: function() {
return this._;
}
};
var constant$2 = function(x) {
return function constant() {
return x;
};
};
var abs = Math.abs;
var atan2 = Math.atan2;
var cos = Math.cos;
var max$1 = Math.max;
var min$1 = Math.min;
var sin = Math.sin;
var sqrt = Math.sqrt;
var epsilon$1 = 1e-12;
var pi$1 = Math.PI;
var halfPi = pi$1 / 2;
var tau$1 = 2 * pi$1;
function acos(x) {
return x > 1 ? 0 : x < -1 ? pi$1 : Math.acos(x);
}
function asin(x) {
return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);
}
function arcInnerRadius(d) {
return d.innerRadius;
}
function arcOuterRadius(d) {
return d.outerRadius;
}
function arcStartAngle(d) {
return d.startAngle;
}
function arcEndAngle(d) {
return d.endAngle;
}
function arcPadAngle(d) {
return d && d.padAngle; // Note: optional!
}
function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
var x10 = x1 - x0, y10 = y1 - y0,
x32 = x3 - x2, y32 = y3 - y2,
t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10);
return [x0 + t * x10, y0 + t * y10];
}
// Compute perpendicular offset line of length rc.
// http://mathworld.wolfram.com/Circle-LineIntersection.html
function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
var x01 = x0 - x1,
y01 = y0 - y1,
lo = (cw ? rc : -rc) / sqrt(x01 * x01 + y01 * y01),
ox = lo * y01,
oy = -lo * x01,
x11 = x0 + ox,
y11 = y0 + oy,
x10 = x1 + ox,
y10 = y1 + oy,
x00 = (x11 + x10) / 2,
y00 = (y11 + y10) / 2,
dx = x10 - x11,
dy = y10 - y11,
d2 = dx * dx + dy * dy,
r = r1 - rc,
D = x11 * y10 - x10 * y11,
d = (dy < 0 ? -1 : 1) * sqrt(max$1(0, r * r * d2 - D * D)),
cx0 = (D * dy - dx * d) / d2,
cy0 = (-D * dx - dy * d) / d2,
cx1 = (D * dy + dx * d) / d2,
cy1 = (-D * dx + dy * d) / d2,
dx0 = cx0 - x00,
dy0 = cy0 - y00,
dx1 = cx1 - x00,
dy1 = cy1 - y00;
// Pick the closer of the two intersection points.
// TODO Is there a faster way to determine which intersection to use?
if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
return {
cx: cx0,
cy: cy0,
x01: -ox,
y01: -oy,
x11: cx0 * (r1 / r - 1),
y11: cy0 * (r1 / r - 1)
};
}
var d3_arc = function() {
var innerRadius = arcInnerRadius,
outerRadius = arcOuterRadius,
cornerRadius = constant$2(0),
padRadius = null,
startAngle = arcStartAngle,
endAngle = arcEndAngle,
padAngle = arcPadAngle,
context = null;
function arc() {
var buffer,
r,
r0 = +innerRadius.apply(this, arguments),
r1 = +outerRadius.apply(this, arguments),
a0 = startAngle.apply(this, arguments) - halfPi,
a1 = endAngle.apply(this, arguments) - halfPi,
da = abs(a1 - a0),
cw = a1 > a0;
if (!context) context = buffer = path();
// Ensure that the outer radius is always larger than the inner radius.
if (r1 < r0) r = r1, r1 = r0, r0 = r;
// Is it a point?
if (!(r1 > epsilon$1)) context.moveTo(0, 0);
// Or is it a circle or annulus?
else if (da > tau$1 - epsilon$1) {
context.moveTo(r1 * cos(a0), r1 * sin(a0));
context.arc(0, 0, r1, a0, a1, !cw);
if (r0 > epsilon$1) {
context.moveTo(r0 * cos(a1), r0 * sin(a1));
context.arc(0, 0, r0, a1, a0, cw);
}
}
// Or is it a circular or annular sector?
else {
var a01 = a0,
a11 = a1,
a00 = a0,
a10 = a1,
da0 = da,
da1 = da,
ap = padAngle.apply(this, arguments) / 2,
rp = (ap > epsilon$1) && (padRadius ? +padRadius.apply(this, arguments) : sqrt(r0 * r0 + r1 * r1)),
rc = min$1(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
rc0 = rc,
rc1 = rc,
t0,
t1;
// Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
if (rp > epsilon$1) {
var p0 = asin(rp / r0 * sin(ap)),
p1 = asin(rp / r1 * sin(ap));
if ((da0 -= p0 * 2) > epsilon$1) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;
else da0 = 0, a00 = a10 = (a0 + a1) / 2;
if ((da1 -= p1 * 2) > epsilon$1) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;
else da1 = 0, a01 = a11 = (a0 + a1) / 2;
}
var x01 = r1 * cos(a01),
y01 = r1 * sin(a01),
x10 = r0 * cos(a10),
y10 = r0 * sin(a10);
// Apply rounded corners?
if (rc > epsilon$1) {
var x11 = r1 * cos(a11),
y11 = r1 * sin(a11),
x00 = r0 * cos(a00),
y00 = r0 * sin(a00);
// Restrict the corner radius according to the sector angle.
if (da < pi$1) {
var oc = da0 > epsilon$1 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10],
ax = x01 - oc[0],
ay = y01 - oc[1],
bx = x11 - oc[0],
by = y11 - oc[1],
kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt(ax * ax + ay * ay) * sqrt(bx * bx + by * by))) / 2),
lc = sqrt(oc[0] * oc[0] + oc[1] * oc[1]);
rc0 = min$1(rc, (r0 - lc) / (kc - 1));
rc1 = min$1(rc, (r1 - lc) / (kc + 1));
}
}
// Is the sector collapsed to a line?
if (!(da1 > epsilon$1)) context.moveTo(x01, y01);
// Does the sector’s outer ring have rounded corners?
else if (rc1 > epsilon$1) {
t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);
context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);
// Have the corners merged?
if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);
// Otherwise, draw the two corners and the ring.
else {
context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);
context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);
}
}
// Or is the outer ring just a circular arc?
else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);
// Is there no inner ring, and it’s a circular sector?
// Or perhaps it’s an annular sector collapsed due to padding?
if (!(r0 > epsilon$1) || !(da0 > epsilon$1)) context.lineTo(x10, y10);
// Does the sector’s inner ring (or point) have rounded corners?
else if (rc0 > epsilon$1) {
t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
// Have the corners merged?
if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);
// Otherwise, draw the two corners and the ring.
else {
context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);
context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);
context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);
}
}
// Or is the inner ring just a circular arc?
else context.arc(0, 0, r0, a10, a00, cw);
}
context.closePath();
if (buffer) return context = null, buffer + "" || null;
}
arc.centroid = function() {
var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$1 / 2;
return [cos(a) * r, sin(a) * r];
};
arc.innerRadius = function(_) {
return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : innerRadius;
};
arc.outerRadius = function(_) {
return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : outerRadius;
};
arc.cornerRadius = function(_) {
return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : cornerRadius;
};
arc.padRadius = function(_) {
return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), arc) : padRadius;
};
arc.startAngle = function(_) {
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : startAngle;
};
arc.endAngle = function(_) {
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : endAngle;
};
arc.padAngle = function(_) {
return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : padAngle;
};
arc.context = function(_) {
return arguments.length ? ((context = _ == null ? null : _), arc) : context;
};
return arc;
};
function Linear(context) {
this._context = context;
}
Linear.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; // proceed
default: this._context.lineTo(x, y); break;
}
}
};
var curveLinear = function(context) {
return new Linear(context);
};
function x$1(p) {
return p[0];
}
function y$1(p) {
return p[1];
}
var line$1 = function() {
var x = x$1,
y = y$1,
defined = constant$2(true),
context = null,
curve = curveLinear,
output = null;
function line(data) {
var i,
n = data.length,
d,
defined0 = false,
buffer;
if (context == null) output = curve(buffer = path());
for (i = 0; i <= n; ++i) {
if (!(i < n && defined(d = data[i], i, data)) === defined0) {
if (defined0 = !defined0) output.lineStart();
else output.lineEnd();
}
if (defined0) output.point(+x(d, i, data), +y(d, i, data));
}
if (buffer) return output = null, buffer + "" || null;
}
line.x = function(_) {
return arguments.length ? (x = typeof _ === "function" ? _ : constant$2(+_), line) : x;
};
line.y = function(_) {
return arguments.length ? (y = typeof _ === "function" ? _ : constant$2(+_), line) : y;
};
line.defined = function(_) {
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$2(!!_), line) : defined;
};
line.curve = function(_) {
return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;
};
line.context = function(_) {
return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;
};
return line;
};
var area$1 = function() {
var x0 = x$1,
x1 = null,
y0 = constant$2(0),
y1 = y$1,
defined = constant$2(true),
context = null,
curve = curveLinear,
output = null;
function area(data) {
var i,
j,
k,
n = data.length,
d,
defined0 = false,
buffer,
x0z = new Array(n),
y0z = new Array(n);
if (context == null) output = curve(buffer = path());
for (i = 0; i <= n; ++i) {
if (!(i < n && defined(d = data[i], i, data)) === defined0) {
if (defined0 = !defined0) {
j = i;
output.areaStart();
output.lineStart();
} else {
output.lineEnd();
output.lineStart();
for (k = i - 1; k >= j; --k) {
output.point(x0z[k], y0z[k]);
}
output.lineEnd();
output.areaEnd();
}
}
if (defined0) {
x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);
output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);
}
}
if (buffer) return output = null, buffer + "" || null;
}
function arealine() {
return line$1().defined(defined).curve(curve).context(context);
}
area.x = function(_) {
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$2(+_), x1 = null, area) : x0;
};
area.x0 = function(_) {
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$2(+_), area) : x0;
};
area.x1 = function(_) {
return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), area) : x1;
};
area.y = function(_) {
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$2(+_), y1 = null, area) : y0;
};
area.y0 = function(_) {
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$2(+_), area) : y0;
};
area.y1 = function(_) {
return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), area) : y1;
};
area.lineX0 =
area.lineY0 = function() {
return arealine().x(x0).y(y0);
};
area.lineY1 = function() {
return arealine().x(x0).y(y1);
};
area.lineX1 = function() {
return arealine().x(x1).y(y0);
};
area.defined = function(_) {
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$2(!!_), area) : defined;
};
area.curve = function(_) {
return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;
};
area.context = function(_) {
return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;
};
return area;
};
var circle = {
draw: function(context, size) {
var r = Math.sqrt(size / pi$1);
context.moveTo(r, 0);
context.arc(0, 0, r, 0, tau$1);
}
};
var d3_symbol = function() {
var type = constant$2(circle),
size = constant$2(64),
context = null;
function symbol() {
var buffer;
if (!context) context = buffer = path();
type.apply(this, arguments).draw(context, +size.apply(this, arguments));
if (buffer) return context = null, buffer + "" || null;
}
symbol.type = function(_) {
return arguments.length ? (type = typeof _ === "function" ? _ : constant$2(_), symbol) : type;
};
symbol.size = function(_) {
return arguments.length ? (size = typeof _ === "function" ? _ : constant$2(+_), symbol) : size;
};
symbol.context = function(_) {
return arguments.length ? (context = _ == null ? null : _, symbol) : context;
};
return symbol;
};
var noop$2 = function() {};
function point(that, x, y) {
that._context.bezierCurveTo(
(2 * that._x0 + that._x1) / 3,
(2 * that._y0 + that._y1) / 3,
(that._x0 + 2 * that._x1) / 3,
(that._y0 + 2 * that._y1) / 3,
(that._x0 + 4 * that._x1 + x) / 6,
(that._y0 + 4 * that._y1 + y) / 6
);
}
function Basis(context) {
this._context = context;
}
Basis.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 =
this._y0 = this._y1 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 3: point(this, this._x1, this._y1); // proceed
case 2: this._context.lineTo(this._x1, this._y1); break;
}
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; break;
case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed
default: point(this, x, y); break;
}
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
var curveBasis = function(context) {
return new Basis(context);
};
function BasisClosed(context) {
this._context = context;
}
BasisClosed.prototype = {
areaStart: noop$2,
areaEnd: noop$2,
lineStart: function() {
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 1: {
this._context.moveTo(this._x2, this._y2);
this._context.closePath();
break;
}
case 2: {
this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);
this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);
this._context.closePath();
break;
}
case 3: {
this.point(this._x2, this._y2);
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
break;
}
}
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._x2 = x, this._y2 = y; break;
case 1: this._point = 2; this._x3 = x, this._y3 = y; break;
case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;
default: point(this, x, y); break;
}
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
var curveBasisClosed = function(context) {
return new BasisClosed(context);
};
function BasisOpen(context) {
this._context = context;
}
BasisOpen.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 =
this._y0 = this._y1 = NaN;
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; break;
case 1: this._point = 2; break;
case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;
case 3: this._point = 4; // proceed
default: point(this, x, y); break;
}
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
var curveBasisOpen = function(context) {
return new BasisOpen(context);
};
function Bundle(context, beta) {
this._basis = new Basis(context);
this._beta = beta;
}
Bundle.prototype = {
lineStart: function() {
this._x = [];
this._y = [];
this._basis.lineStart();
},
lineEnd: function() {
var x = this._x,
y = this._y,
j = x.length - 1;
if (j > 0) {
var x0 = x[0],
y0 = y[0],
dx = x[j] - x0,
dy = y[j] - y0,
i = -1,
t;
while (++i <= j) {
t = i / j;
this._basis.point(
this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),
this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)
);
}
}
this._x = this._y = null;
this._basis.lineEnd();
},
point: function(x, y) {
this._x.push(+x);
this._y.push(+y);
}
};
var curveBundle = (function custom(beta) {
function bundle(context) {
return beta === 1 ? new Basis(context) : new Bundle(context, beta);
}
bundle.beta = function(beta) {
return custom(+beta);
};
return bundle;
})(0.85);
function point$1(that, x, y) {
that._context.bezierCurveTo(
that._x1 + that._k * (that._x2 - that._x0),
that._y1 + that._k * (that._y2 - that._y0),
that._x2 + that._k * (that._x1 - x),
that._y2 + that._k * (that._y1 - y),
that._x2,
that._y2
);
}
function Cardinal(context, tension) {
this._context = context;
this._k = (1 - tension) / 6;
}
Cardinal.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 = this._x2 =
this._y0 = this._y1 = this._y2 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 2: this._context.lineTo(this._x2, this._y2); break;
case 3: point$1(this, this._x1, this._y1); break;
}
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; this._x1 = x, this._y1 = y; break;
case 2: this._point = 3; // proceed
default: point$1(this, x, y); break;
}
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCardinal = (function custom(tension) {
function cardinal(context) {
return new Cardinal(context, tension);
}
cardinal.tension = function(tension) {
return custom(+tension);
};
return cardinal;
})(0);
function CardinalClosed(context, tension) {
this._context = context;
this._k = (1 - tension) / 6;
}
CardinalClosed.prototype = {
areaStart: noop$2,
areaEnd: noop$2,
lineStart: function() {
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 1: {
this._context.moveTo(this._x3, this._y3);
this._context.closePath();
break;
}
case 2: {
this._context.lineTo(this._x3, this._y3);
this._context.closePath();
break;
}
case 3: {
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
this.point(this._x5, this._y5);
break;
}
}
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
default: point$1(this, x, y); break;
}
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCardinalClosed = (function custom(tension) {
function cardinal(context) {
return new CardinalClosed(context, tension);
}
cardinal.tension = function(tension) {
return custom(+tension);
};
return cardinal;
})(0);
function CardinalOpen(context, tension) {
this._context = context;
this._k = (1 - tension) / 6;
}
CardinalOpen.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 = this._x2 =
this._y0 = this._y1 = this._y2 = NaN;
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; break;
case 1: this._point = 2; break;
case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
case 3: this._point = 4; // proceed
default: point$1(this, x, y); break;
}
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCardinalOpen = (function custom(tension) {
function cardinal(context) {
return new CardinalOpen(context, tension);
}
cardinal.tension = function(tension) {
return custom(+tension);
};
return cardinal;
})(0);
function point$2(that, x, y) {
var x1 = that._x1,
y1 = that._y1,
x2 = that._x2,
y2 = that._y2;
if (that._l01_a > epsilon$1) {
var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,
n = 3 * that._l01_a * (that._l01_a + that._l12_a);
x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;
y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;
}
if (that._l23_a > epsilon$1) {
var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,
m = 3 * that._l23_a * (that._l23_a + that._l12_a);
x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;
y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;
}
that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);
}
function CatmullRom(context, alpha) {
this._context = context;
this._alpha = alpha;
}
CatmullRom.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 = this._x2 =
this._y0 = this._y1 = this._y2 = NaN;
this._l01_a = this._l12_a = this._l23_a =
this._l01_2a = this._l12_2a = this._l23_2a =
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 2: this._context.lineTo(this._x2, this._y2); break;
case 3: this.point(this._x2, this._y2); break;
}
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
if (this._point) {
var x23 = this._x2 - x,
y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; break;
case 2: this._point = 3; // proceed
default: point$2(this, x, y); break;
}
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCatmullRom = (function custom(alpha) {
function catmullRom(context) {
return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);
}
catmullRom.alpha = function(alpha) {
return custom(+alpha);
};
return catmullRom;
})(0.5);
function CatmullRomClosed(context, alpha) {
this._context = context;
this._alpha = alpha;
}
CatmullRomClosed.prototype = {
areaStart: noop$2,
areaEnd: noop$2,
lineStart: function() {
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
this._l01_a = this._l12_a = this._l23_a =
this._l01_2a = this._l12_2a = this._l23_2a =
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 1: {
this._context.moveTo(this._x3, this._y3);
this._context.closePath();
break;
}
case 2: {
this._context.lineTo(this._x3, this._y3);
this._context.closePath();
break;
}
case 3: {
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
this.point(this._x5, this._y5);
break;
}
}
},
point: function(x, y) {
x = +x, y = +y;
if (this._point) {
var x23 = this._x2 - x,
y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
switch (this._point) {
case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
default: point$2(this, x, y); break;
}
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCatmullRomClosed = (function custom(alpha) {
function catmullRom(context) {
return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);
}
catmullRom.alpha = function(alpha) {
return custom(+alpha);
};
return catmullRom;
})(0.5);
function CatmullRomOpen(context, alpha) {
this._context = context;
this._alpha = alpha;
}
CatmullRomOpen.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 = this._x2 =
this._y0 = this._y1 = this._y2 = NaN;
this._l01_a = this._l12_a = this._l23_a =
this._l01_2a = this._l12_2a = this._l23_2a =
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
if (this._point) {
var x23 = this._x2 - x,
y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
switch (this._point) {
case 0: this._point = 1; break;
case 1: this._point = 2; break;
case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
case 3: this._point = 4; // proceed
default: point$2(this, x, y); break;
}
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCatmullRomOpen = (function custom(alpha) {
function catmullRom(context) {
return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);
}
catmullRom.alpha = function(alpha) {
return custom(+alpha);
};
return catmullRom;
})(0.5);
function LinearClosed(context) {
this._context = context;
}
LinearClosed.prototype = {
areaStart: noop$2,
areaEnd: noop$2,
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._point) this._context.closePath();
},
point: function(x, y) {
x = +x, y = +y;
if (this._point) this._context.lineTo(x, y);
else this._point = 1, this._context.moveTo(x, y);
}
};
var curveLinearClosed = function(context) {
return new LinearClosed(context);
};
function sign(x) {
return x < 0 ? -1 : 1;
}
// Calculate the slopes of the tangents (Hermite-type interpolation) based on
// the following paper: Steffen, M. 1990. A Simple Method for Monotonic
// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.
// NOV(II), P. 443, 1990.
function slope3(that, x2, y2) {
var h0 = that._x1 - that._x0,
h1 = x2 - that._x1,
s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),
s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),
p = (s0 * h1 + s1 * h0) / (h0 + h1);
return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
}
// Calculate a one-sided slope.
function slope2(that, t) {
var h = that._x1 - that._x0;
return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;
}
// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
// "you can express cubic Hermite interpolation in terms of cubic Bézier curves
// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
function point$3(that, t0, t1) {
var x0 = that._x0,
y0 = that._y0,
x1 = that._x1,
y1 = that._y1,
dx = (x1 - x0) / 3;
that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);
}
function MonotoneX(context) {
this._context = context;
}
MonotoneX.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 =
this._y0 = this._y1 =
this._t0 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 2: this._context.lineTo(this._x1, this._y1); break;
case 3: point$3(this, this._t0, slope2(this, this._t0)); break;
}
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
var t1 = NaN;
x = +x, y = +y;
if (x === this._x1 && y === this._y1) return; // Ignore coincident points.
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; break;
case 2: this._point = 3; point$3(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
default: point$3(this, this._t0, t1 = slope3(this, x, y)); break;
}
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
this._t0 = t1;
}
};
function MonotoneY(context) {
this._context = new ReflectContext(context);
}
(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {
MonotoneX.prototype.point.call(this, y, x);
};
function ReflectContext(context) {
this._context = context;
}
ReflectContext.prototype = {
moveTo: function(x, y) { this._context.moveTo(y, x); },
closePath: function() { this._context.closePath(); },
lineTo: function(x, y) { this._context.lineTo(y, x); },
bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }
};
function monotoneX(context) {
return new MonotoneX(context);
}
function monotoneY(context) {
return new MonotoneY(context);
}
function Natural(context) {
this._context = context;
}
Natural.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x = [];
this._y = [];
},
lineEnd: function() {
var x = this._x,
y = this._y,
n = x.length;
if (n) {
this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);
if (n === 2) {
this._context.lineTo(x[1], y[1]);
} else {
var px = controlPoints(x),
py = controlPoints(y);
for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
}
}
}
if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();
this._line = 1 - this._line;
this._x = this._y = null;
},
point: function(x, y) {
this._x.push(+x);
this._y.push(+y);
}
};
// See https://www.particleincell.com/2012/bezier-splines/ for derivation.
function controlPoints(x) {
var i,
n = x.length - 1,
m,
a = new Array(n),
b = new Array(n),
r = new Array(n);
a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];
for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];
a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];
for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];
a[n - 1] = r[n - 1] / b[n - 1];
for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];
b[n - 1] = (x[n] + a[n - 1]) / 2;
for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];
return [a, b];
}
var curveNatural = function(context) {
return new Natural(context);
};
function Step(context, t) {
this._context = context;
this._t = t;
}
Step.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x = this._y = NaN;
this._point = 0;
},
lineEnd: function() {
if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; // proceed
default: {
if (this._t <= 0) {
this._context.lineTo(this._x, y);
this._context.lineTo(x, y);
} else {
var x1 = this._x * (1 - this._t) + x * this._t;
this._context.lineTo(x1, this._y);
this._context.lineTo(x1, y);
}
break;
}
}
this._x = x, this._y = y;
}
};
var curveStep = function(context) {
return new Step(context, 0.5);
};
function stepBefore(context) {
return new Step(context, 0);
}
function stepAfter(context) {
return new Step(context, 1);
}
var lookup = {
'basis': {
curve: curveBasis
},
'basis-closed': {
curve: curveBasisClosed
},
'basis-open': {
curve: curveBasisOpen
},
'bundle': {
curve: curveBundle,
tension: 'beta',
value: 0.85
},
'cardinal': {
curve: curveCardinal,
tension: 'tension',
value: 0
},
'cardinal-open': {
curve: curveCardinalOpen,
tension: 'tension',
value: 0
},
'cardinal-closed': {
curve: curveCardinalClosed,
tension: 'tension',
value: 0
},
'catmull-rom': {
curve: curveCatmullRom,
tension: 'alpha',
value: 0.5
},
'catmull-rom-closed': {
curve: curveCatmullRomClosed,
tension: 'alpha',
value: 0.5
},
'catmull-rom-open': {
curve: curveCatmullRomOpen,
tension: 'alpha',
value: 0.5
},
'linear': {
curve: curveLinear
},
'linear-closed': {
curve: curveLinearClosed
},
'monotone': {
horizontal: monotoneY,
vertical: monotoneX
},
'natural': {
curve: curveNatural
},
'step': {
curve: curveStep
},
'step-after': {
curve: stepAfter
},
'step-before': {
curve: stepBefore
}
};
function curves(type, orientation, tension) {
var entry = lookup.hasOwnProperty(type) && lookup[type],
curve = null;
if (entry) {
curve = entry.curve || entry[orientation || 'vertical'];
if (entry.tension && tension != null) {
curve = curve[entry.tension](tension);
}
}
return curve;
}
// Path parsing and rendering code adapted from fabric.js -- Thanks!
var cmdlen = { m:2, l:2, h:1, v:1, c:6, s:4, q:4, t:2, a:7 };
var regexp = [/([MLHVCSQTAZmlhvcsqtaz])/g, /###/, /(\d)([-+])/g, /\s|,|###/];
var pathParse = function(pathstr) {
var result = [],
path,
curr,
chunks,
parsed, param,
cmd, len, i, j, n, m;
// First, break path into command sequence
path = pathstr
.slice()
.replace(regexp[0], '###$1')
.split(regexp[1])
.slice(1);
// Next, parse each command in turn
for (i=0, n=path.length; i<n; ++i) {
curr = path[i];
chunks = curr
.slice(1)
.trim()
.replace(regexp[2],'$1###$2')
.split(regexp[3]);
cmd = curr.charAt(0);
parsed = [cmd];
for (j=0, m=chunks.length; j<m; ++j) {
if ((param = +chunks[j]) === param) { // not NaN
parsed.push(param);
}
}
len = cmdlen[cmd.toLowerCase()];
if (parsed.length-1 > len) {
for (j=1, m=parsed.length; j<m; j+=len) {
result.push([cmd].concat(parsed.slice(j, j+len)));
}
}
else {
result.push(parsed);
}
}
return result;
};
var segmentCache = {};
var bezierCache = {};
var join = [].join;
// Copied from Inkscape svgtopdf, thanks!
function segments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
var key = join.call(arguments);
if (segmentCache[key]) {
return segmentCache[key];
}
var th = rotateX * (Math.PI/180);
var sin_th = Math.sin(th);
var cos_th = Math.cos(th);
rx = Math.abs(rx);
ry = Math.abs(ry);
var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
if (pl > 1) {
pl = Math.sqrt(pl);
rx *= pl;
ry *= pl;
}
var a00 = cos_th / rx;
var a01 = sin_th / rx;
var a10 = (-sin_th) / ry;
var a11 = (cos_th) / ry;
var x0 = a00 * ox + a01 * oy;
var y0 = a10 * ox + a11 * oy;
var x1 = a00 * x + a01 * y;
var y1 = a10 * x + a11 * y;
var d = (x1-x0) * (x1-x0) + (y1-y0) * (y1-y0);
var sfactor_sq = 1 / d - 0.25;
if (sfactor_sq < 0) sfactor_sq = 0;
var sfactor = Math.sqrt(sfactor_sq);
if (sweep == large) sfactor = -sfactor;
var xc = 0.5 * (x0 + x1) - sfactor * (y1-y0);
var yc = 0.5 * (y0 + y1) + sfactor * (x1-x0);
var th0 = Math.atan2(y0-yc, x0-xc);
var th1 = Math.atan2(y1-yc, x1-xc);
var th_arc = th1-th0;
if (th_arc < 0 && sweep === 1){
th_arc += 2 * Math.PI;
} else if (th_arc > 0 && sweep === 0) {
th_arc -= 2 * Math.PI;
}
var segs = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
var result = [];
for (var i=0; i<segs; ++i) {
var th2 = th0 + i * th_arc / segs;
var th3 = th0 + (i+1) * th_arc / segs;
result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
}
return (segmentCache[key] = result);
}
function bezier(params) {
var key = join.call(params);
if (bezierCache[key]) {
return bezierCache[key];
}
var cx = params[0],
cy = params[1],
th0 = params[2],
th1 = params[3],
rx = params[4],
ry = params[5],
sin_th = params[6],
cos_th = params[7];
var a00 = cos_th * rx;
var a01 = -sin_th * ry;
var a10 = sin_th * rx;
var a11 = cos_th * ry;
var cos_th0 = Math.cos(th0);
var sin_th0 = Math.sin(th0);
var cos_th1 = Math.cos(th1);
var sin_th1 = Math.sin(th1);
var th_half = 0.5 * (th1 - th0);
var sin_th_h2 = Math.sin(th_half * 0.5);
var t = (8/3) * sin_th_h2 * sin_th_h2 / Math.sin(th_half);
var x1 = cx + cos_th0 - t * sin_th0;
var y1 = cy + sin_th0 + t * cos_th0;
var x3 = cx + cos_th1;
var y3 = cy + sin_th1;
var x2 = x3 + t * sin_th1;
var y2 = y3 - t * cos_th1;
return (bezierCache[key] = [
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
a00 * x3 + a01 * y3, a10 * x3 + a11 * y3
]);
}
var temp$1 = ['l', 0, 0, 0, 0, 0, 0, 0];
function scale(current, s) {
var c = (temp$1[0] = current[0]);
if (c === 'a' || c === 'A') {
temp$1[1] = s * current[1];
temp$1[2] = s * current[2];
temp$1[6] = s * current[6];
temp$1[7] = s * current[7];
} else {
for (var i=1, n=current.length; i<n; ++i) {
temp$1[i] = s * current[i];
}
}
return temp$1;
}
var pathRender = function(context, path, l, t, s) {
var current, // current instruction
previous = null,
x = 0, // current x
y = 0, // current y
controlX = 0, // current control point x
controlY = 0, // current control point y
tempX,
tempY,
tempControlX,
tempControlY;
if (l == null) l = 0;
if (t == null) t = 0;
if (s == null) s = 1;
if (context.beginPath) context.beginPath();
for (var i=0, len=path.length; i<len; ++i) {
current = path[i];
if (s !== 1) current = scale(current, s);
switch (current[0]) { // first letter
case 'l': // lineto, relative
x += current[1];
y += current[2];
context.lineTo(x + l, y + t);
break;
case 'L': // lineto, absolute
x = current[1];
y = current[2];
context.lineTo(x + l, y + t);
break;
case 'h': // horizontal lineto, relative
x += current[1];
context.lineTo(x + l, y + t);
break;
case 'H': // horizontal lineto, absolute
x = current[1];
context.lineTo(x + l, y + t);
break;
case 'v': // vertical lineto, relative
y += current[1];
context.lineTo(x + l, y + t);
break;
case 'V': // verical lineto, absolute
y = current[1];
context.lineTo(x + l, y + t);
break;
case 'm': // moveTo, relative
x += current[1];
y += current[2];
context.moveTo(x + l, y + t);
break;
case 'M': // moveTo, absolute
x = current[1];
y = current[2];
context.moveTo(x + l, y + t);
break;
case 'c': // bezierCurveTo, relative
tempX = x + current[5];
tempY = y + current[6];
controlX = x + current[3];
controlY = y + current[4];
context.bezierCurveTo(
x + current[1] + l, // x1
y + current[2] + t, // y1
controlX + l, // x2
controlY + t, // y2
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
break;
case 'C': // bezierCurveTo, absolute
x = current[5];
y = current[6];
controlX = current[3];
controlY = current[4];
context.bezierCurveTo(
current[1] + l,
current[2] + t,
controlX + l,
controlY + t,
x + l,
y + t
);
break;
case 's': // shorthand cubic bezierCurveTo, relative
// transform to absolute x,y
tempX = x + current[3];
tempY = y + current[4];
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
context.bezierCurveTo(
controlX + l,
controlY + t,
x + current[1] + l,
y + current[2] + t,
tempX + l,
tempY + t
);
// set control point to 2nd one of this command
// the first control point is assumed to be the reflection of
// the second control point on the previous command relative
// to the current point.
controlX = x + current[1];
controlY = y + current[2];
x = tempX;
y = tempY;
break;
case 'S': // shorthand cubic bezierCurveTo, absolute
tempX = current[3];
tempY = current[4];
// calculate reflection of previous control points
controlX = 2*x - controlX;
controlY = 2*y - controlY;
context.bezierCurveTo(
controlX + l,
controlY + t,
current[1] + l,
current[2] + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
// set control point to 2nd one of this command
// the first control point is assumed to be the reflection of
// the second control point on the previous command relative
// to the current point.
controlX = current[1];
controlY = current[2];
break;
case 'q': // quadraticCurveTo, relative
// transform to absolute x,y
tempX = x + current[3];
tempY = y + current[4];
controlX = x + current[1];
controlY = y + current[2];
context.quadraticCurveTo(
controlX + l,
controlY + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
break;
case 'Q': // quadraticCurveTo, absolute
tempX = current[3];
tempY = current[4];
context.quadraticCurveTo(
current[1] + l,
current[2] + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
controlX = current[1];
controlY = current[2];
break;
case 't': // shorthand quadraticCurveTo, relative
// transform to absolute x,y
tempX = x + current[1];
tempY = y + current[2];
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
}
else if (previous[0] === 't') {
// calculate reflection of previous control points for t
controlX = 2 * x - tempControlX;
controlY = 2 * y - tempControlY;
}
else if (previous[0] === 'q') {
// calculate reflection of previous control points for q
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
tempControlX = controlX;
tempControlY = controlY;
context.quadraticCurveTo(
controlX + l,
controlY + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
controlX = x + current[1];
controlY = y + current[2];
break;
case 'T':
tempX = current[1];
tempY = current[2];
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
context.quadraticCurveTo(
controlX + l,
controlY + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
break;
case 'a':
drawArc(context, x + l, y + t, [
current[1],
current[2],
current[3],
current[4],
current[5],
current[6] + x + l,
current[7] + y + t
]);
x += current[6];
y += current[7];
break;
case 'A':
drawArc(context, x + l, y + t, [
current[1],
current[2],
current[3],
current[4],
current[5],
current[6] + l,
current[7] + t
]);
x = current[6];
y = current[7];
break;
case 'z':
case 'Z':
context.closePath();
break;
}
previous = current;
}
};
function drawArc(context, x, y, coords) {
var seg = segments(
coords[5], // end x
coords[6], // end y
coords[0], // radius x
coords[1], // radius y
coords[3], // large flag
coords[4], // sweep flag
coords[2], // rotation
x, y
);
for (var i=0; i<seg.length; ++i) {
var bez = bezier(seg[i]);
context.bezierCurveTo(bez[0], bez[1], bez[2], bez[3], bez[4], bez[5]);
}
}
var tau$2 = 2 * Math.PI;
var halfSqrt3 = Math.sqrt(3) / 2;
var builtins = {
'circle': {
draw: function(context, size) {
var r = Math.sqrt(size) / 2;
context.moveTo(r, 0);
context.arc(0, 0, r, 0, tau$2);
}
},
'cross': {
draw: function(context, size) {
var r = Math.sqrt(size) / 2,
s = r / 2.5;
context.moveTo(-r, -s);
context.lineTo(-r, s);
context.lineTo(-s, s);
context.lineTo(-s, r);
context.lineTo(s, r);
context.lineTo(s, s);
context.lineTo(r, s);
context.lineTo(r, -s);
context.lineTo(s, -s);
context.lineTo(s, -r);
context.lineTo(-s, -r);
context.lineTo(-s, -s);
context.closePath();
}
},
'diamond': {
draw: function(context, size) {
var r = Math.sqrt(size) / 2;
context.moveTo(-r, 0);
context.lineTo(0, -r);
context.lineTo(r, 0);
context.lineTo(0, r);
context.closePath();
}
},
'square': {
draw: function(context, size) {
var w = Math.sqrt(size),
x = -w / 2;
context.rect(x, x, w, w);
}
},
'triangle-up': {
draw: function(context, size) {
var r = Math.sqrt(size) / 2,
h = halfSqrt3 * r;
context.moveTo(0, -h);
context.lineTo(-r, h);
context.lineTo(r, h);
context.closePath();
}
},
'triangle-down': {
draw: function(context, size) {
var r = Math.sqrt(size) / 2,
h = halfSqrt3 * r;
context.moveTo(0, h);
context.lineTo(-r, -h);
context.lineTo(r, -h);
context.closePath();
}
},
'triangle-right': {
draw: function(context, size) {
var r = Math.sqrt(size) / 2,
h = halfSqrt3 * r;
context.moveTo(h, 0);
context.lineTo(-h, -r);
context.lineTo(-h, r);
context.closePath();
}
},
'triangle-left': {
draw: function(context, size) {
var r = Math.sqrt(size) / 2,
h = halfSqrt3 * r;
context.moveTo(-h, 0);
context.lineTo(h, -r);
context.lineTo(h, r);
context.closePath();
}
}
};
function symbols$1(_) {
return builtins.hasOwnProperty(_) ? builtins[_] : customSymbol(_);
}
var custom = {};
function customSymbol(path) {
if (!custom.hasOwnProperty(path)) {
var parsed = pathParse(path);
custom[path] = {
draw: function(context, size) {
pathRender(context, parsed, 0, 0, Math.sqrt(size) / 2);
}
};
}
return custom[path];
}
function rectangleX(d) {
return d.x;
}
function rectangleY(d) {
return d.y;
}
function rectangleWidth(d) {
return d.width;
}
function rectangleHeight(d) {
return d.height;
}
function constant$3(_) {
return function() { return _; };
}
var vg_rect = function() {
var x = rectangleX,
y = rectangleY,
width = rectangleWidth,
height = rectangleHeight,
cornerRadius = constant$3(0),
context = null;
function rectangle(_, x0, y0) {
var buffer,
x1 = x0 != null ? x0 : +x.call(this, _),
y1 = y0 != null ? y0 : +y.call(this, _),
w = +width.call(this, _),
h = +height.call(this, _),
cr = +cornerRadius.call(this, _);
if (!context) context = buffer = path();
if (cr <= 0) {
context.rect(x1, y1, w, h);
} else {
var x2 = x1 + w,
y2 = y1 + h;
context.moveTo(x1 + cr, y1);
context.lineTo(x2 - cr, y1);
context.quadraticCurveTo(x2, y1, x2, y1 + cr);
context.lineTo(x2, y2 - cr);
context.quadraticCurveTo(x2, y2, x2 - cr, y2);
context.lineTo(x1 + cr, y2);
context.quadraticCurveTo(x1, y2, x1, y2 - cr);
context.lineTo(x1, y1 + cr);
context.quadraticCurveTo(x1, y1, x1 + cr, y1);
context.closePath();
}
if (buffer) {
context = null;
return buffer + '' || null;
}
}
rectangle.x = function(_) {
if (arguments.length) {
x = typeof _ === 'function' ? _ : constant$3(+_);
return rectangle;
} else {
return x;
}
};
rectangle.y = function(_) {
if (arguments.length) {
y = typeof _ === 'function' ? _ : constant$3(+_);
return rectangle;
} else {
return y;
}
};
rectangle.width = function(_) {
if (arguments.length) {
width = typeof _ === 'function' ? _ : constant$3(+_);
return rectangle;
} else {
return width;
}
};
rectangle.height = function(_) {
if (arguments.length) {
height = typeof _ === 'function' ? _ : constant$3(+_);
return rectangle;
} else {
return height;
}
};
rectangle.cornerRadius = function(_) {
if (arguments.length) {
cornerRadius = typeof _ === 'function' ? _ : constant$3(+_);
return rectangle;
} else {
return cornerRadius;
}
};
rectangle.context = function(_) {
if (arguments.length) {
context = _ == null ? null : _;
return rectangle;
} else {
return context;
}
};
return rectangle;
};
var pi$2 = Math.PI;
var vg_trail = function() {
var x,
y,
size,
defined,
context = null,
ready, x1, y1, r1;
function point(x2, y2, w2) {
var r2 = w2 / 2;
if (ready) {
var ux = y1 - y2,
uy = x2 - x1;
if (ux || uy) {
// get normal vector
var ud = Math.sqrt(ux * ux + uy * uy),
rx = (ux /= ud) * r1,
ry = (uy /= ud) * r1,
t = Math.atan2(uy, ux);
// draw segment
context.moveTo(x1 - rx, y1 - ry);
context.lineTo(x2 - ux * r2, y2 - uy * r2);
context.arc(x2, y2, r2, t - pi$2, t);
context.lineTo(x1 + rx, y1 + ry);
context.arc(x1, y1, r1, t, t + pi$2);
} else {
context.arc(x2, y2, r2, 0, 2*pi$2);
}
context.closePath();
} else {
ready = 1;
}
x1 = x2;
y1 = y2;
r1 = r2;
}
function trail(data) {
var i,
n = data.length,
d,
defined0 = false,
buffer;
if (context == null) context = buffer = path();
for (i = 0; i <= n; ++i) {
if (!(i < n && defined(d = data[i], i, data)) === defined0) {
if (defined0 = !defined0) ready = 0;
}
if (defined0) point(+x(d, i, data), +y(d, i, data), +size(d, i, data));
}
if (buffer) {
context = null;
return buffer + '' || null;
}
}
trail.x = function(_) {
if (arguments.length) {
x = _;
return trail;
} else {
return x;
}
};
trail.y = function(_) {
if (arguments.length) {
y = _;
return trail;
} else {
return y;
}
};
trail.size = function(_) {
if (arguments.length) {
size = _;
return trail;
} else {
return size;
}
};
trail.defined = function(_) {
if (arguments.length) {
defined = _;
return trail;
} else {
return defined;
}
};
trail.context = function(_) {
if (arguments.length) {
if (_ == null) {
context = null;
} else {
context = _;
}
return trail;
} else {
return context;
}
};
return trail;
};
function x(item) { return item.x || 0; }
function y(item) { return item.y || 0; }
function w(item) { return item.width || 0; }
function ts(item) { return item.size || 1; }
function h(item) { return item.height || 0; }
function xw(item) { return (item.x || 0) + (item.width || 0); }
function yh(item) { return (item.y || 0) + (item.height || 0); }
function cr(item) { return item.cornerRadius || 0; }
function pa(item) { return item.padAngle || 0; }
function def(item) { return !(item.defined === false); }
function size(item) { return item.size == null ? 64 : item.size; }
function type$1(item) { return symbols$1(item.shape || 'circle'); }
var arcShape = d3_arc().cornerRadius(cr).padAngle(pa);
var areavShape = area$1().x(x).y1(y).y0(yh).defined(def);
var areahShape = area$1().y(y).x1(x).x0(xw).defined(def);
var lineShape = line$1().x(x).y(y).defined(def);
var rectShape = vg_rect().x(x).y(y).width(w).height(h).cornerRadius(cr);
var symbolShape = d3_symbol().type(type$1).size(size);
var trailShape = vg_trail().x(x).y(y).defined(def).size(ts);
function arc$1(context, item) {
return arcShape.context(context)(item);
}
function area(context, items) {
var item = items[0],
interp = item.interpolate || 'linear';
return (item.orient === 'horizontal' ? areahShape : areavShape)
.curve(curves(interp, item.orient, item.tension))
.context(context)(items);
}
function line(context, items) {
var item = items[0],
interp = item.interpolate || 'linear';
return lineShape.curve(curves(interp, item.orient, item.tension))
.context(context)(items);
}
function rectangle(context, item, x, y) {
return rectShape.context(context)(item, x, y);
}
function shape(context, item) {
return (item.mark.shape || item.shape)
.context(context)(item);
}
function symbol(context, item) {
return symbolShape.context(context)(item);
}
function trail(context, items) {
return trailShape.context(context)(items);
}
var boundStroke = function(bounds, item) {
if (item.stroke && item.opacity !== 0 && item.strokeOpacity !== 0) {
bounds.expand(item.strokeWidth != null ? +item.strokeWidth : 1);
}
return bounds;
};
var bounds;
var tau$3 = Math.PI * 2;
var halfPi$1 = tau$3 / 4;
var circleThreshold = tau$3 - 1e-8;
function context(_) {
bounds = _;
return context;
}
function noop$3() {}
function add$1(x, y) { bounds.add(x, y); }
context.beginPath = noop$3;
context.closePath = noop$3;
context.moveTo = add$1;
context.lineTo = add$1;
context.rect = function(x, y, w, h) {
add$1(x, y);
add$1(x + w, y + h);
};
context.quadraticCurveTo = function(x1, y1, x2, y2) {
add$1(x1, y1);
add$1(x2, y2);
};
context.bezierCurveTo = function(x1, y1, x2, y2, x3, y3) {
add$1(x1, y1);
add$1(x2, y2);
add$1(x3, y3);
};
context.arc = function(cx, cy, r, sa, ea, ccw) {
if (Math.abs(ea - sa) > circleThreshold) {
add$1(cx - r, cy - r);
add$1(cx + r, cy + r);
return;
}
var xmin = Infinity, xmax = -Infinity,
ymin = Infinity, ymax = -Infinity,
s, i, x, y;
function update(a) {
x = r * Math.cos(a);
y = r * Math.sin(a);
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
if (y < ymin) ymin = y;
if (y > ymax) ymax = y;
}
// Sample end points and interior points aligned with 90 degrees
update(sa);
update(ea);
if (ea !== sa) {
sa = sa % tau$3; if (sa < 0) sa += tau$3;
ea = ea % tau$3; if (ea < 0) ea += tau$3;
if (ea < sa) {
ccw = !ccw; // flip direction
s = sa; sa = ea; ea = s; // swap end-points
}
if (ccw) {
ea -= tau$3;
s = sa - (sa % halfPi$1);
for (i=0; i<3 && s>ea; ++i, s-=halfPi$1) update(s);
} else {
s = sa - (sa % halfPi$1) + halfPi$1;
for (i=0; i<3 && s<ea; ++i, s=s+halfPi$1) update(s);
}
}
add$1(cx + xmin, cy + ymin);
add$1(cx + xmax, cy + ymax);
};
var gradient = function(context, gradient, bounds) {
var w = bounds.width(),
h = bounds.height(),
x1 = bounds.x1 + gradient.x1 * w,
y1 = bounds.y1 + gradient.y1 * h,
x2 = bounds.x1 + gradient.x2 * w,
y2 = bounds.y1 + gradient.y2 * h,
stop = gradient.stops,
i = 0,
n = stop.length,
linearGradient = context.createLinearGradient(x1, y1, x2, y2);
for (; i<n; ++i) {
linearGradient.addColorStop(stop[i].offset, stop[i].color);
}
return linearGradient;
};
var color = function(context, item, value) {
return (value.id) ?
gradient(context, value, item.bounds) :
value;
};
var fill = function(context, item, opacity) {
opacity *= (item.fillOpacity==null ? 1 : item.fillOpacity);
if (opacity > 0) {
context.globalAlpha = opacity;
context.fillStyle = color(context, item, item.fill);
return true;
} else {
return false;
}
};
var Empty$1 = [];
var stroke = function(context, item, opacity) {
var lw = (lw = item.strokeWidth) != null ? lw : 1;
if (lw <= 0) return false;
opacity *= (item.strokeOpacity==null ? 1 : item.strokeOpacity);
if (opacity > 0) {
context.globalAlpha = opacity;
context.strokeStyle = color(context, item, item.stroke);
context.lineWidth = lw;
context.lineCap = item.strokeCap || 'butt';
context.lineJoin = item.strokeJoin || 'miter';
context.miterLimit = item.strokeMiterLimit || 10;
if (context.setLineDash) {
context.setLineDash(item.strokeDash || Empty$1);
context.lineDashOffset = item.strokeDashOffset || 0;
}
return true;
} else {
return false;
}
};
function compare$1(a, b) {
return a.zindex - b.zindex || a.index - b.index;
}
function zorder(scene) {
if (!scene.zdirty) return scene.zitems;
var items = scene.items,
output = [], item, i, n;
for (i=0, n=items.length; i<n; ++i) {
item = items[i];
item.index = i;
if (item.zindex) output.push(item);
}
scene.zdirty = false;
return scene.zitems = output.sort(compare$1);
}
function visit(scene, visitor) {
var items = scene.items, i, n;
if (!items || !items.length) return;
var zitems = zorder(scene);
if (zitems && zitems.length) {
for (i=0, n=items.length; i<n; ++i) {
if (!items[i].zindex) visitor(items[i]);
}
items = zitems;
}
for (i=0, n=items.length; i<n; ++i) {
visitor(items[i]);
}
}
function pickVisit(scene, visitor) {
var items = scene.items, hit, i;
if (!items || !items.length) return null;
var zitems = zorder(scene);
if (zitems && zitems.length) items = zitems;
for (i=items.length; --i >= 0;) {
if (hit = visitor(items[i])) return hit;
}
if (items === zitems) {
for (items=scene.items, i=items.length; --i >= 0;) {
if (!items[i].zindex) {
if (hit = visitor(items[i])) return hit;
}
}
}
return null;
}
function drawAll(path) {
return function(context, scene, bounds) {
visit(scene, function(item) {
if (!bounds || bounds.intersects(item.bounds)) {
drawPath(path, context, item, item);
}
});
};
}
function drawOne(path) {
return function(context, scene, bounds) {
if (scene.items.length && (!bounds || bounds.intersects(scene.bounds))) {
drawPath(path, context, scene.items[0], scene.items);
}
};
}
function drawPath(path, context, item, items) {
var opacity = item.opacity == null ? 1 : item.opacity;
if (opacity === 0) return;
if (path(context, items)) return;
if (item.fill && fill(context, item, opacity)) {
context.fill();
}
if (item.stroke && stroke(context, item, opacity)) {
context.stroke();
}
}
var trueFunc = function() { return true; };
function pick(test) {
if (!test) test = trueFunc;
return function(context, scene, x, y, gx, gy) {
if (context.pixelRatio > 1) {
x *= context.pixelRatio;
y *= context.pixelRatio;
}
return pickVisit(scene, function(item) {
var b = item.bounds;
// first hit test against bounding box
if ((b && !b.contains(gx, gy)) || !b) return;
// if in bounding box, perform more careful test
if (test(context, item, x, y, gx, gy)) return item;
});
};
}
function hitPath(path, filled) {
return function(context, o, x, y) {
var item = Array.isArray(o) ? o[0] : o,
fill = (filled == null) ? item.fill : filled,
stroke = item.stroke && context.isPointInStroke, lw, lc;
if (stroke) {
lw = item.strokeWidth;
lc = item.strokeCap;
context.lineWidth = lw != null ? lw : 1;
context.lineCap = lc != null ? lc : 'butt';
}
return path(context, o) ? false :
(fill && context.isPointInPath(x, y)) ||
(stroke && context.isPointInStroke(x, y));
};
}
function pickPath(path) {
return pick(hitPath(path));
}
var translate = function(x, y) {
return 'translate(' + x + ',' + y + ')';
};
var translateItem = function(item) {
return translate(item.x || 0, item.y || 0);
};
var markItemPath = function(type, shape) {
function attr(emit, item) {
emit('transform', translateItem(item));
emit('d', shape(null, item));
}
function bound(bounds, item) {
shape(context(bounds), item);
return boundStroke(bounds, item)
.translate(item.x || 0, item.y || 0);
}
function draw(context$$1, item) {
var x = item.x || 0,
y = item.y || 0;
context$$1.translate(x, y);
context$$1.beginPath();
shape(context$$1, item);
context$$1.translate(-x, -y);
}
return {
type: type,
tag: 'path',
nested: false,
attr: attr,
bound: bound,
draw: drawAll(draw),
pick: pickPath(draw)
};
};
var arc = markItemPath('arc', arc$1);
var markMultiItemPath = function(type, shape) {
function attr(emit, item) {
var items = item.mark.items;
if (items.length) emit('d', shape(null, items));
}
function bound(bounds, mark) {
var items = mark.items;
if (items.length === 0) {
return bounds;
} else {
shape(context(bounds), items);
return boundStroke(bounds, items[0]);
}
}
function draw(context$$1, items) {
context$$1.beginPath();
shape(context$$1, items);
}
var hit = hitPath(draw);
function pick$$1(context$$1, scene, x, y, gx, gy) {
var items = scene.items,
b = scene.bounds;
if (!items || !items.length || b && !b.contains(gx, gy)) {
return null;
}
if (context$$1.pixelRatio > 1) {
x *= context$$1.pixelRatio;
y *= context$$1.pixelRatio;
}
return hit(context$$1, items, x, y) ? items[0] : null;
}
return {
type: type,
tag: 'path',
nested: true,
attr: attr,
bound: bound,
draw: drawOne(draw),
pick: pick$$1
};
};
var area$2 = markMultiItemPath('area', area);
var clip_id = 1;
function resetSVGClipId() {
clip_id = 1;
}
var clip = function(renderer, item, size) {
var defs = renderer._defs,
id = item.clip_id || (item.clip_id = 'clip' + clip_id++),
c = defs.clipping[id] || (defs.clipping[id] = {id: id});
c.width = size.width || 0;
c.height = size.height || 0;
return 'url(#' + id + ')';
};
var StrokeOffset = 0.5;
function attr(emit, item) {
emit('transform', translateItem(item));
}
function background(emit, item) {
var offset = item.stroke ? StrokeOffset : 0;
emit('class', 'background');
emit('d', rectangle(null, item, offset, offset));
}
function foreground(emit, item, renderer) {
var url = item.clip ? clip(renderer, item, item) : null;
emit('clip-path', url);
}
function bound(bounds, group) {
if (!group.clip && group.items) {
var items = group.items;
for (var j=0, m=items.length; j<m; ++j) {
bounds.union(items[j].bounds);
}
}
if (group.clip || group.width || group.height) {
boundStroke(
bounds.add(0, 0).add(group.width || 0, group.height || 0),
group
);
}
return bounds.translate(group.x || 0, group.y || 0);
}
function draw(context, scene, bounds) {
var renderer = this;
visit(scene, function(group) {
var gx = group.x || 0,
gy = group.y || 0,
w = group.width || 0,
h = group.height || 0,
offset, opacity;
// setup graphics context
context.save();
context.translate(gx, gy);
// draw group background
if (group.stroke || group.fill) {
opacity = group.opacity == null ? 1 : group.opacity;
if (opacity > 0) {
context.beginPath();
offset = group.stroke ? StrokeOffset : 0;
rectangle(context, group, offset, offset);
if (group.fill && fill(context, group, opacity)) {
context.fill();
}
if (group.stroke && stroke(context, group, opacity)) {
context.stroke();
}
}
}
// set clip and bounds
if (group.clip) {
context.beginPath();
context.rect(0, 0, w, h);
context.clip();
}
if (bounds) bounds.translate(-gx, -gy);
// draw group contents
visit(group, function(item) {
renderer.draw(context, item, bounds);
});
// restore graphics context
if (bounds) bounds.translate(gx, gy);
context.restore();
});
}
function pick$1(context, scene, x, y, gx, gy) {
if (scene.bounds && !scene.bounds.contains(gx, gy) || !scene.items) {
return null;
}
var handler = this;
return pickVisit(scene, function(group) {
var hit, dx, dy, b;
// first hit test against bounding box
// if a group is clipped, that should be handled by the bounds check.
b = group.bounds;
if (b && !b.contains(gx, gy)) return;
// passed bounds check, so test sub-groups
dx = (group.x || 0);
dy = (group.y || 0);
context.save();
context.translate(dx, dy);
dx = gx - dx;
dy = gy - dy;
hit = pickVisit(group, function(mark) {
return pickMark(mark, dx, dy)
? handler.pick(mark, x, y, dx, dy)
: null;
});
context.restore();
if (hit) return hit;
hit = scene.interactive !== false
&& (group.fill || group.stroke)
&& dx >= 0
&& dx <= group.width
&& dy >= 0
&& dy <= group.height;
return hit ? group : null;
});
}
function pickMark(mark, x, y) {
return (mark.interactive !== false || mark.marktype === 'group')
&& mark.bounds && mark.bounds.contains(x, y);
}
var group = {
type: 'group',
tag: 'g',
nested: false,
attr: attr,
bound: bound,
draw: draw,
pick: pick$1,
background: background,
foreground: foreground
};
function getImage(item, renderer) {
var image = item.image;
if (!image || image.url !== item.url) {
image = {loaded: false, width: 0, height: 0};
renderer.loadImage(item.url).then(function(image) {
item.image = image;
item.image.url = item.url;
});
}
return image;
}
function imageXOffset(align, w) {
return align === 'center' ? w / 2 : align === 'right' ? w : 0;
}
function imageYOffset(baseline, h) {
return baseline === 'middle' ? h / 2 : baseline === 'bottom' ? h : 0;
}
function attr$1(emit, item, renderer) {
var image = getImage(item, renderer),
x = item.x || 0,
y = item.y || 0,
w = (item.width != null ? item.width : image.width) || 0,
h = (item.height != null ? item.height : image.height) || 0,
a = item.aspect === false ? 'none' : 'xMidYMid';
x -= imageXOffset(item.align, w);
y -= imageYOffset(item.baseline, h);
emit('href', image.src || '', 'http://www.w3.org/1999/xlink', 'xlink:href');
emit('transform', translate(x, y));
emit('width', w);
emit('height', h);
emit('preserveAspectRatio', a);
}
function bound$1(bounds, item) {
var image = item.image,
x = item.x || 0,
y = item.y || 0,
w = (item.width != null ? item.width : (image && image.width)) || 0,
h = (item.height != null ? item.height : (image && image.height)) || 0;
x -= imageXOffset(item.align, w);
y -= imageYOffset(item.baseline, h);
return bounds.set(x, y, x + w, y + h);
}
function draw$1(context, scene, bounds) {
var renderer = this;
visit(scene, function(item) {
if (bounds && !bounds.intersects(item.bounds)) return; // bounds check
var image = getImage(item, renderer),
x = item.x || 0,
y = item.y || 0,
w = (item.width != null ? item.width : image.width) || 0,
h = (item.height != null ? item.height : image.height) || 0,
opacity, ar0, ar1, t;
x -= imageXOffset(item.align, w);
y -= imageYOffset(item.baseline, h);
if (item.aspect !== false) {
ar0 = image.width / image.height;
ar1 = item.width / item.height;
if (ar0 === ar0 && ar1 === ar1 && ar0 !== ar1) {
if (ar1 < ar0) {
t = w / ar0;
y += (h - t) / 2;
h = t;
} else {
t = h * ar0;
x += (w - t) / 2;
w = t;
}
}
}
if (image.loaded) {
context.globalAlpha = (opacity = item.opacity) != null ? opacity : 1;
context.drawImage(image, x, y, w, h);
}
});
}
var image = {
type: 'image',
tag: 'image',
nested: false,
attr: attr$1,
bound: bound$1,
draw: draw$1,
pick: pick(),
get: getImage,
xOffset: imageXOffset,
yOffset: imageYOffset
};
var line$2 = markMultiItemPath('line', line);
function attr$2(emit, item) {
emit('transform', translateItem(item));
emit('d', item.path);
}
function path$2(context$$1, item) {
var path = item.path;
if (path == null) return true;
var cache = item.pathCache;
if (!cache || cache.path !== path) {
(item.pathCache = cache = pathParse(path)).path = path;
}
pathRender(context$$1, cache, item.x, item.y);
}
function bound$2(bounds, item) {
return path$2(context(bounds), item)
? bounds.set(0, 0, 0, 0)
: boundStroke(bounds, item);
}
var path$3 = {
type: 'path',
tag: 'path',
nested: false,
attr: attr$2,
bound: bound$2,
draw: drawAll(path$2),
pick: pickPath(path$2)
};
function attr$3(emit, item) {
emit('d', rectangle(null, item));
}
function bound$3(bounds, item) {
var x, y;
return boundStroke(bounds.set(
x = item.x || 0,
y = item.y || 0,
(x + item.width) || 0,
(y + item.height) || 0
), item);
}
function draw$2(context, item) {
context.beginPath();
rectangle(context, item);
}
var rect = {
type: 'rect',
tag: 'path',
nested: false,
attr: attr$3,
bound: bound$3,
draw: drawAll(draw$2),
pick: pickPath(draw$2)
};
function attr$4(emit, item) {
emit('transform', translateItem(item));
emit('x2', item.x2 != null ? item.x2 - (item.x||0) : 0);
emit('y2', item.y2 != null ? item.y2 - (item.y||0) : 0);
}
function bound$4(bounds, item) {
var x1, y1;
return boundStroke(bounds.set(
x1 = item.x || 0,
y1 = item.y || 0,
item.x2 != null ? item.x2 : x1,
item.y2 != null ? item.y2 : y1
), item);
}
function path$4(context, item, opacity) {
var x1, y1, x2, y2;
if (item.stroke && stroke(context, item, opacity)) {
x1 = item.x || 0;
y1 = item.y || 0;
x2 = item.x2 != null ? item.x2 : x1;
y2 = item.y2 != null ? item.y2 : y1;
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
return true;
}
return false;
}
function draw$3(context, scene, bounds) {
visit(scene, function(item) {
if (bounds && !bounds.intersects(item.bounds)) return; // bounds check
var opacity = item.opacity == null ? 1 : item.opacity;
if (opacity && path$4(context, item, opacity)) {
context.stroke();
}
});
}
function hit(context, item, x, y) {
if (!context.isPointInStroke) return false;
return path$4(context, item, 1) && context.isPointInStroke(x, y);
}
var rule = {
type: 'rule',
tag: 'line',
nested: false,
attr: attr$4,
bound: bound$4,
draw: draw$3,
pick: pick(hit)
};
var shape$1 = markItemPath('shape', shape);
var symbol$1 = markItemPath('symbol', symbol);
var context$1;
var fontHeight;
var textMetrics = {
height: height,
measureWidth: measureWidth,
estimateWidth: estimateWidth,
width: estimateWidth,
canvas: canvas
};
canvas(true);
// make dumb, simple estimate if no canvas is available
function estimateWidth(item) {
fontHeight = height(item);
return estimate(textValue(item));
}
function estimate(text) {
return ~~(0.8 * text.length * fontHeight);
}
// measure text width if canvas is available
function measureWidth(item) {
context$1.font = font(item);
return measure$1(textValue(item));
}
function measure$1(text) {
return context$1.measureText(text).width;
}
function height(item) {
return item.fontSize != null ? item.fontSize : 11;
}
function canvas(use) {
context$1 = use && (context$1 = Canvas$1(1,1)) ? context$1.getContext('2d') : null;
textMetrics.width = context$1 ? measureWidth : estimateWidth;
}
function textValue(item) {
var s = item.text;
if (s == null) {
return '';
} else {
return item.limit > 0 ? truncate$1(item) : s + '';
}
}
function truncate$1(item) {
var limit = +item.limit,
text = item.text + '',
width;
if (context$1) {
context$1.font = font(item);
width = measure$1;
} else {
fontHeight = height(item);
width = estimate;
}
if (width(text) < limit) return text;
var ellipsis = item.ellipsis || '\u2026',
rtl = item.dir === 'rtl',
lo = 0,
hi = text.length, mid;
limit -= width(ellipsis);
if (rtl) {
while (lo < hi) {
mid = (lo + hi >>> 1);
if (width(text.slice(mid)) > limit) lo = mid + 1;
else hi = mid;
}
return ellipsis + text.slice(lo);
} else {
while (lo < hi) {
mid = 1 + (lo + hi >>> 1);
if (width(text.slice(0, mid)) < limit) lo = mid;
else hi = mid - 1;
}
return text.slice(0, lo) + ellipsis;
}
}
function font(item, quote) {
var font = item.font;
if (quote && font) {
font = String(font).replace(/"/g, '\'');
}
return '' +
(item.fontStyle ? item.fontStyle + ' ' : '') +
(item.fontVariant ? item.fontVariant + ' ' : '') +
(item.fontWeight ? item.fontWeight + ' ' : '') +
height(item) + 'px ' +
(font || 'sans-serif');
}
function offset(item) {
// perform our own font baseline calculation
// why? not all browsers support SVG 1.1 'alignment-baseline' :(
var baseline = item.baseline,
h = height(item);
return Math.round(
baseline === 'top' ? 0.93*h :
baseline === 'middle' ? 0.30*h :
baseline === 'bottom' ? -0.21*h : 0
);
}
var textAlign = {
'left': 'start',
'center': 'middle',
'right': 'end'
};
var tempBounds = new Bounds();
function attr$5(emit, item) {
var dx = item.dx || 0,
dy = (item.dy || 0) + offset(item),
x = item.x || 0,
y = item.y || 0,
a = item.angle || 0,
r = item.radius || 0, t;
if (r) {
t = (item.theta || 0) - Math.PI/2;
x += r * Math.cos(t);
y += r * Math.sin(t);
}
emit('text-anchor', textAlign[item.align] || 'start');
if (a) {
t = translate(x, y) + ' rotate('+a+')';
if (dx || dy) t += ' ' + translate(dx, dy);
} else {
t = translate(x + dx, y + dy);
}
emit('transform', t);
}
function bound$5(bounds, item, noRotate) {
var h = textMetrics.height(item),
a = item.align,
r = item.radius || 0,
x = item.x || 0,
y = item.y || 0,
dx = item.dx || 0,
dy = (item.dy || 0) + offset(item) - Math.round(0.8*h), // use 4/5 offset
w, t;
if (r) {
t = (item.theta || 0) - Math.PI/2;
x += r * Math.cos(t);
y += r * Math.sin(t);
}
// horizontal alignment
w = textMetrics.width(item);
if (a === 'center') {
dx -= (w / 2);
} else if (a === 'right') {
dx -= w;
} else {
// left by default, do nothing
}
bounds.set(dx+=x, dy+=y, dx+w, dy+h);
if (item.angle && !noRotate) {
bounds.rotate(item.angle*Math.PI/180, x, y);
}
return bounds.expand(noRotate || !w ? 0 : 1);
}
function draw$4(context, scene, bounds) {
visit(scene, function(item) {
var opacity, x, y, r, t, str;
if (bounds && !bounds.intersects(item.bounds)) return; // bounds check
if (!(str = textValue(item))) return; // get text string
opacity = item.opacity == null ? 1 : item.opacity;
if (opacity === 0) return;
context.font = font(item);
context.textAlign = item.align || 'left';
x = item.x || 0;
y = item.y || 0;
if ((r = item.radius)) {
t = (item.theta || 0) - Math.PI/2;
x += r * Math.cos(t);
y += r * Math.sin(t);
}
if (item.angle) {
context.save();
context.translate(x, y);
context.rotate(item.angle * Math.PI/180);
x = y = 0; // reset x, y
}
x += (item.dx || 0);
y += (item.dy || 0) + offset(item);
if (item.fill && fill(context, item, opacity)) {
context.fillText(str, x, y);
}
if (item.stroke && stroke(context, item, opacity)) {
context.strokeText(str, x, y);
}
if (item.angle) context.restore();
});
}
function hit$1(context, item, x, y, gx, gy) {
if (item.fontSize <= 0) return false;
if (!item.angle) return true; // bounds sufficient if no rotation
// project point into space of unrotated bounds
var b = bound$5(tempBounds, item, true),
a = -item.angle * Math.PI / 180,
cos = Math.cos(a),
sin = Math.sin(a),
ix = item.x,
iy = item.y,
px = cos*gx - sin*gy + (ix - ix*cos + iy*sin),
py = sin*gx + cos*gy + (iy - ix*sin - iy*cos);
return b.contains(px, py);
}
var text$1 = {
type: 'text',
tag: 'text',
nested: false,
attr: attr$5,
bound: bound$5,
draw: draw$4,
pick: pick(hit$1)
};
var trail$1 = markMultiItemPath('trail', trail);
var marks = {
arc: arc,
area: area$2,
group: group,
image: image,
line: line$2,
path: path$3,
rect: rect,
rule: rule,
shape: shape$1,
symbol: symbol$1,
text: text$1,
trail: trail$1
};
var boundItem$1 = function(item, func, opt) {
var type = marks[item.mark.marktype],
bound = func || type.bound;
if (type.nested) item = item.mark;
return bound(item.bounds || (item.bounds = new Bounds()), item, opt);
};
var DUMMY = {mark: null};
var boundMark = function(mark, bounds, opt) {
var type = marks[mark.marktype],
bound = type.bound,
items = mark.items,
hasItems = items && items.length,
i, n, item, b;
if (type.nested) {
if (hasItems) {
item = items[0];
} else {
// no items, fake it
DUMMY.mark = mark;
item = DUMMY;
}
b = boundItem$1(item, bound, opt);
bounds = bounds && bounds.union(b) || b;
return bounds;
}
bounds = bounds
|| mark.bounds && mark.bounds.clear()
|| new Bounds();
if (hasItems) {
for (i=0, n=items.length; i<n; ++i) {
bounds.union(boundItem$1(items[i], bound, opt));
}
}
return mark.bounds = bounds;
};
var keys$1 = [
'marktype', 'name', 'role', 'interactive', 'clip', 'items', 'zindex',
'x', 'y', 'width', 'height', 'align', 'baseline', // layout
'fill', 'fillOpacity', 'opacity', // fill
'stroke', 'strokeOpacity', 'strokeWidth', 'strokeCap', // stroke
'strokeDash', 'strokeDashOffset', // stroke dash
'startAngle', 'endAngle', 'innerRadius', 'outerRadius', // arc
'cornerRadius', 'padAngle', // arc, rect
'interpolate', 'tension', 'orient', 'defined', // area, line
'url', // image
'path', // path
'x2', 'y2', // rule
'size', 'shape', // symbol
'text', 'angle', 'theta', 'radius', 'dx', 'dy', // text
'font', 'fontSize', 'fontWeight', 'fontStyle', 'fontVariant' // font
];
function sceneToJSON(scene, indent) {
return JSON.stringify(scene, keys$1, indent);
}
function sceneFromJSON(json) {
var scene = (typeof json === 'string' ? JSON.parse(json) : json);
return initialize(scene);
}
function initialize(scene) {
var type = scene.marktype,
items = scene.items,
parent, i, n;
if (items) {
for (i=0, n=items.length; i<n; ++i) {
parent = type ? 'mark' : 'group';
items[i][parent] = scene;
if (items[i].zindex) items[i][parent].zdirty = true;
if ('group' === (type || parent)) initialize(items[i]);
}
}
if (type) boundMark(scene);
return scene;
}
function Scenegraph(scene) {
if (arguments.length) {
this.root = sceneFromJSON(scene);
} else {
this.root = createMark({
marktype: 'group',
name: 'root',
role: 'frame'
});
this.root.items = [new GroupItem(this.root)];
}
}
var prototype$38 = Scenegraph.prototype;
prototype$38.toJSON = function(indent) {
return sceneToJSON(this.root, indent || 0);
};
prototype$38.mark = function(markdef, group, index) {
group = group || this.root.items[0];
var mark = createMark(markdef, group);
group.items[index] = mark;
if (mark.zindex) mark.group.zdirty = true;
return mark;
};
function createMark(def, group) {
return {
bounds: new Bounds(),
clip: !!def.clip,
group: group,
interactive: def.interactive === false ? false : true,
items: [],
marktype: def.marktype,
name: def.name || undefined,
role: def.role || undefined,
zindex: def.zindex || 0
};
}
function Handler(customLoader) {
this._active = null;
this._handlers = {};
this._loader = customLoader || loader();
}
var prototype$39 = Handler.prototype;
prototype$39.initialize = function(el, origin, obj) {
this._el = el;
this._obj = obj || null;
return this.origin(origin);
};
prototype$39.element = function() {
return this._el;
};
prototype$39.origin = function(origin) {
this._origin = origin || [0, 0];
return this;
};
prototype$39.scene = function(scene) {
if (!arguments.length) return this._scene;
this._scene = scene;
return this;
};
// add an event handler
// subclasses should override
prototype$39.on = function(/*type, handler*/) {};
// remove an event handler
// subclasses should override
prototype$39.off = function(/*type, handler*/) {};
// return an array with all registered event handlers
prototype$39.handlers = function() {
var h = this._handlers, a = [], k;
for (k in h) { a.push.apply(a, h[k]); }
return a;
};
prototype$39.eventName = function(name) {
var i = name.indexOf('.');
return i < 0 ? name : name.slice(0,i);
};
prototype$39.handleHref = function(event, item, href) {
this._loader
.sanitize(href, {context:'href'})
.then(function(opt) {
var e = new MouseEvent(event.type, event),
a = domCreate(null, 'a');
for (var name in opt) a.setAttribute(name, opt[name]);
a.dispatchEvent(e);
})
.catch(function() { /* do nothing */ });
};
prototype$39.handleTooltip = function(event, item, tooltipText) {
this._el.setAttribute('title', tooltipText || '');
};
/**
* Create a new Renderer instance.
* @param {object} [loader] - Optional loader instance for
* image and href URL sanitization. If not specified, a
* standard loader instance will be generated.
* @constructor
*/
function Renderer(loader) {
this._el = null;
this._bgcolor = null;
this._loader = new ResourceLoader(loader);
}
var prototype$40 = Renderer.prototype;
/**
* Initialize a new Renderer instance.
* @param {DOMElement} el - The containing DOM element for the display.
* @param {number} width - The width of the display, in pixels.
* @param {number} height - The height of the display, in pixels.
* @param {Array<number>} origin - The origin of the display, in pixels.
* The coordinate system will be translated to this point.
* @return {Renderer} - This renderer instance;
*/
prototype$40.initialize = function(el, width, height, origin) {
this._el = el;
return this.resize(width, height, origin);
};
/**
* Returns the parent container element for a visualization.
* @return {DOMElement} - The containing DOM element.
*/
prototype$40.element = function() {
return this._el;
};
/**
* Returns the scene element (e.g., canvas or SVG) of the visualization
* Subclasses must override if the first child is not the scene element.
* @return {DOMElement} - The scene (e.g., canvas or SVG) element.
*/
prototype$40.scene = function() {
return this._el && this._el.firstChild;
};
/**
* Get / set the background color.
*/
prototype$40.background = function(bgcolor) {
if (arguments.length === 0) return this._bgcolor;
this._bgcolor = bgcolor;
return this;
};
/**
* Resize the display.
* @param {number} width - The new width of the display, in pixels.
* @param {number} height - The new height of the display, in pixels.
* @param {Array<number>} origin - The new origin of the display, in pixels.
* The coordinate system will be translated to this point.
* @return {Renderer} - This renderer instance;
*/
prototype$40.resize = function(width, height, origin) {
this._width = width;
this._height = height;
this._origin = origin || [0, 0];
return this;
};
/**
* Report a dirty item whose bounds should be redrawn.
* This base class method does nothing. Subclasses that perform
* incremental should implement this method.
* @param {Item} item - The dirty item whose bounds should be redrawn.
*/
prototype$40.dirty = function(/*item*/) {
};
/**
* Render an input scenegraph, potentially with a set of dirty items.
* This method will perform an immediate rendering with available resources.
* The renderer may also need to perform image loading to perform a complete
* render. This process can lead to asynchronous re-rendering of the scene
* after this method returns. To receive notification when rendering is
* complete, use the renderAsync method instead.
* @param {object} scene - The root mark of a scenegraph to render.
* @return {Renderer} - This renderer instance.
*/
prototype$40.render = function(scene) {
var r = this;
// bind arguments into a render call, and cache it
// this function may be subsequently called for async redraw
r._call = function() { r._render(scene); };
// invoke the renderer
r._call();
// clear the cached call for garbage collection
// async redraws will stash their own copy
r._call = null;
return r;
};
/**
* Internal rendering method. Renderer subclasses should override this
* method to actually perform rendering.
* @param {object} scene - The root mark of a scenegraph to render.
*/
prototype$40._render = function(/*scene*/) {
// subclasses to override
};
/**
* Asynchronous rendering method. Similar to render, but returns a Promise
* that resolves when all rendering is completed. Sometimes a renderer must
* perform image loading to get a complete rendering. The returned
* Promise will not resolve until this process completes.
* @param {object} scene - The root mark of a scenegraph to render.
* @return {Promise} - A Promise that resolves when rendering is complete.
*/
prototype$40.renderAsync = function(scene) {
var r = this.render(scene);
return this._ready
? this._ready.then(function() { return r; })
: Promise.resolve(r);
};
/**
* Internal method for asynchronous resource loading.
* Proxies method calls to the ImageLoader, and tracks loading
* progress to invoke a re-render once complete.
* @param {string} method - The method name to invoke on the ImageLoader.
* @param {string} uri - The URI for the requested resource.
* @return {Promise} - A Promise that resolves to the requested resource.
*/
prototype$40._load = function(method, uri) {
var r = this,
p = r._loader[method](uri);
if (!r._ready) {
// re-render the scene when loading completes
var call = r._call;
r._ready = r._loader.ready()
.then(function(redraw) {
if (redraw) call();
r._ready = null;
});
}
return p;
};
/**
* Sanitize a URL to include as a hyperlink in the rendered scene.
* This method proxies a call to ImageLoader.sanitizeURL, but also tracks
* image loading progress and invokes a re-render once complete.
* @param {string} uri - The URI string to sanitize.
* @return {Promise} - A Promise that resolves to the sanitized URL.
*/
prototype$40.sanitizeURL = function(uri) {
return this._load('sanitizeURL', uri);
};
/**
* Requests an image to include in the rendered scene.
* This method proxies a call to ImageLoader.loadImage, but also tracks
* image loading progress and invokes a re-render once complete.
* @param {string} uri - The URI string of the image.
* @return {Promise} - A Promise that resolves to the loaded Image.
*/
prototype$40.loadImage = function(uri) {
return this._load('loadImage', uri);
};
var point$4 = function(event, el) {
var rect = el.getBoundingClientRect();
return [
event.clientX - rect.left - (el.clientLeft || 0),
event.clientY - rect.top - (el.clientTop || 0)
];
};
function CanvasHandler(loader) {
Handler.call(this, loader);
this._down = null;
this._touch = null;
this._first = true;
}
var prototype$41 = inherits(CanvasHandler, Handler);
prototype$41.initialize = function(el, origin, obj) {
// add event listeners
var canvas = this._canvas = el && domFind(el, 'canvas');
if (canvas) {
var that = this;
this.events.forEach(function(type) {
canvas.addEventListener(type, function(evt) {
if (prototype$41[type]) {
prototype$41[type].call(that, evt);
} else {
that.fire(type, evt);
}
});
});
}
return Handler.prototype.initialize.call(this, el, origin, obj);
};
prototype$41.canvas = function() {
return this._canvas;
};
// retrieve the current canvas context
prototype$41.context = function() {
return this._canvas.getContext('2d');
};
// supported events
prototype$41.events = [
'keydown',
'keypress',
'keyup',
'dragenter',
'dragleave',
'dragover',
'mousedown',
'mouseup',
'mousemove',
'mouseout',
'mouseover',
'click',
'dblclick',
'wheel',
'mousewheel',
'touchstart',
'touchmove',
'touchend'
];
// to keep old versions of firefox happy
prototype$41.DOMMouseScroll = function(evt) {
this.fire('mousewheel', evt);
};
function move(moveEvent, overEvent, outEvent) {
return function(evt) {
var a = this._active,
p = this.pickEvent(evt);
if (p === a) {
// active item and picked item are the same
this.fire(moveEvent, evt); // fire move
} else {
// active item and picked item are different
if (!a || !a.exit) {
// fire out for prior active item
// suppress if active item was removed from scene
this.fire(outEvent, evt);
}
this._active = p; // set new active item
this.fire(overEvent, evt); // fire over for new active item
this.fire(moveEvent, evt); // fire move for new active item
}
};
}
function inactive(type) {
return function(evt) {
this.fire(type, evt);
this._active = null;
};
}
prototype$41.mousemove = move('mousemove', 'mouseover', 'mouseout');
prototype$41.dragover = move('dragover', 'dragenter', 'dragleave');
prototype$41.mouseout = inactive('mouseout');
prototype$41.dragleave = inactive('dragleave');
prototype$41.mousedown = function(evt) {
this._down = this._active;
this.fire('mousedown', evt);
};
prototype$41.click = function(evt) {
if (this._down === this._active) {
this.fire('click', evt);
this._down = null;
}
};
prototype$41.touchstart = function(evt) {
this._touch = this.pickEvent(evt.changedTouches[0]);
if (this._first) {
this._active = this._touch;
this._first = false;
}
this.fire('touchstart', evt, true);
};
prototype$41.touchmove = function(evt) {
this.fire('touchmove', evt, true);
};
prototype$41.touchend = function(evt) {
this.fire('touchend', evt, true);
this._touch = null;
};
// fire an event
prototype$41.fire = function(type, evt, touch) {
var a = touch ? this._touch : this._active,
h = this._handlers[type], i, len;
// if hyperlinked, handle link first
if (type === 'click' && a && a.href) {
this.handleHref(evt, a, a.href);
} else if ((type === 'mouseover' || type === 'mouseout') && a && a.tooltip) {
this.handleTooltip(evt, a, type === 'mouseover' ? a.tooltip : null);
}
// invoke all registered handlers
if (h) {
evt.vegaType = type;
for (i=0, len=h.length; i<len; ++i) {
h[i].handler.call(this._obj, evt, a);
}
}
};
// add an event handler
prototype$41.on = function(type, handler) {
var name = this.eventName(type),
h = this._handlers;
(h[name] || (h[name] = [])).push({
type: type,
handler: handler
});
return this;
};
// remove an event handler
prototype$41.off = function(type, handler) {
var name = this.eventName(type),
h = this._handlers[name], i;
if (!h) return;
for (i=h.length; --i>=0;) {
if (h[i].type !== type) continue;
if (!handler || h[i].handler === handler) h.splice(i, 1);
}
return this;
};
prototype$41.pickEvent = function(evt) {
var p = point$4(evt, this._canvas),
o = this._origin;
return this.pick(this._scene, p[0], p[1], p[0] - o[0], p[1] - o[1]);
};
// find the scenegraph item at the current mouse position
// x, y -- the absolute x, y mouse coordinates on the canvas element
// gx, gy -- the relative coordinates within the current group
prototype$41.pick = function(scene, x, y, gx, gy) {
var g = this.context(),
mark = marks[scene.marktype];
return mark.pick.call(this, g, scene, x, y, gx, gy);
};
var clip$1 = function(context, scene) {
var group = scene.group;
context.save();
context.beginPath();
context.rect(0, 0, group.width || 0, group.height || 0);
context.clip();
};
var devicePixelRatio = typeof window !== 'undefined'
? window.devicePixelRatio || 1 : 1;
var resize = function(canvas, width, height, origin) {
var scale = typeof HTMLElement !== 'undefined'
&& canvas instanceof HTMLElement
&& canvas.parentNode != null;
var context = canvas.getContext('2d'),
ratio = scale ? devicePixelRatio : 1;
canvas.width = width * ratio;
canvas.height = height * ratio;
if (ratio !== 1) {
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
}
context.pixelRatio = ratio;
context.setTransform(
ratio, 0, 0, ratio,
ratio * origin[0],
ratio * origin[1]
);
return canvas;
};
function CanvasRenderer(loader) {
Renderer.call(this, loader);
this._redraw = false;
this._dirty = new Bounds();
}
var prototype$42 = inherits(CanvasRenderer, Renderer);
var base = Renderer.prototype;
var tempBounds$1 = new Bounds();
prototype$42.initialize = function(el, width, height, origin) {
this._canvas = Canvas$1(1, 1); // instantiate a small canvas
if (el) {
domClear(el, 0).appendChild(this._canvas);
this._canvas.setAttribute('class', 'marks');
}
// this method will invoke resize to size the canvas appropriately
return base.initialize.call(this, el, width, height, origin);
};
prototype$42.resize = function(width, height, origin) {
base.resize.call(this, width, height, origin);
resize(this._canvas, this._width, this._height, this._origin);
this._redraw = true;
return this;
};
prototype$42.canvas = function() {
return this._canvas;
};
prototype$42.context = function() {
return this._canvas ? this._canvas.getContext('2d') : null;
};
prototype$42.dirty = function(item) {
var b = translate$1(item.bounds, item.mark.group);
this._dirty.union(b);
};
function clipToBounds(g, b, origin) {
// expand bounds by 1 pixel, then round to pixel boundaries
b.expand(1).round();
// to avoid artifacts translate if origin has fractional pixels
b.translate(-(origin[0] % 1), -(origin[1] % 1));
// set clipping path
g.beginPath();
g.rect(b.x1, b.y1, b.width(), b.height());
g.clip();
return b;
}
function translate$1(bounds, group) {
if (group == null) return bounds;
var b = tempBounds$1.clear().union(bounds);
for (; group != null; group = group.mark.group) {
b.translate(group.x || 0, group.y || 0);
}
return b;
}
prototype$42._render = function(scene) {
var g = this.context(),
o = this._origin,
w = this._width,
h = this._height,
b = this._dirty;
// setup
g.save();
if (this._redraw || b.empty()) {
this._redraw = false;
b = null;
} else {
b = clipToBounds(g, b, o);
}
this.clear(-o[0], -o[1], w, h);
// render
this.draw(g, scene, b);
// takedown
g.restore();
this._dirty.clear();
return this;
};
prototype$42.draw = function(ctx, scene, bounds) {
var mark = marks[scene.marktype];
if (scene.clip) clip$1(ctx, scene);
mark.draw.call(this, ctx, scene, bounds);
if (scene.clip) ctx.restore();
};
prototype$42.clear = function(x, y, w, h) {
var g = this.context();
g.clearRect(x, y, w, h);
if (this._bgcolor != null) {
g.fillStyle = this._bgcolor;
g.fillRect(x, y, w, h);
}
};
function SVGHandler(loader) {
Handler.call(this, loader);
var h = this;
h._hrefHandler = listener(h, function(evt, item) {
if (item && item.href) h.handleHref(evt, item, item.href);
});
h._tooltipHandler = listener(h, function(evt, item) {
if (item && item.tooltip) {
h.handleTooltip(evt, item, evt.type === 'mouseover' ? item.tooltip : null);
}
});
}
var prototype$43 = inherits(SVGHandler, Handler);
prototype$43.initialize = function(el, origin, obj) {
var svg = this._svg;
if (svg) {
svg.removeEventListener('click', this._hrefHandler);
svg.removeEventListener('mouseover', this._tooltipHandler);
svg.removeEventListener('mouseout', this._tooltipHandler);
}
this._svg = svg = el && domFind(el, 'svg');
if (svg) {
svg.addEventListener('click', this._hrefHandler);
svg.addEventListener('mouseover', this._tooltipHandler);
svg.addEventListener('mouseout', this._tooltipHandler);
}
return Handler.prototype.initialize.call(this, el, origin, obj);
};
prototype$43.svg = function() {
return this._svg;
};
// wrap an event listener for the SVG DOM
function listener(context, handler) {
return function(evt) {
var target = evt.target,
item = target.__data__;
evt.vegaType = evt.type;
item = Array.isArray(item) ? item[0] : item;
handler.call(context._obj, evt, item);
};
}
// add an event handler
prototype$43.on = function(type, handler) {
var name = this.eventName(type),
h = this._handlers,
x = {
type: type,
handler: handler,
listener: listener(this, handler)
};
(h[name] || (h[name] = [])).push(x);
if (this._svg) {
this._svg.addEventListener(name, x.listener);
}
return this;
};
// remove an event handler
prototype$43.off = function(type, handler) {
var name = this.eventName(type),
svg = this._svg,
h = this._handlers[name], i;
if (!h) return;
for (i=h.length; --i>=0;) {
if (h[i].type === type && !handler || h[i].handler === handler) {
if (this._svg) {
svg.removeEventListener(name, h[i].listener);
}
h.splice(i, 1);
}
}
return this;
};
// generate string for an opening xml tag
// tag: the name of the xml tag
// attr: hash of attribute name-value pairs to include
// raw: additional raw string to include in tag markup
function openTag(tag, attr, raw) {
var s = '<' + tag, key, val;
if (attr) {
for (key in attr) {
val = attr[key];
if (val != null) {
s += ' ' + key + '="' + val + '"';
}
}
}
if (raw) s += ' ' + raw;
return s + '>';
}
// generate string for closing xml tag
// tag: the name of the xml tag
function closeTag(tag) {
return '</' + tag + '>';
}
var metadata = {
'version': '1.1',
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink'
};
var styles = {
'fill': 'fill',
'fillOpacity': 'fill-opacity',
'stroke': 'stroke',
'strokeWidth': 'stroke-width',
'strokeOpacity': 'stroke-opacity',
'strokeCap': 'stroke-linecap',
'strokeJoin': 'stroke-linejoin',
'strokeDash': 'stroke-dasharray',
'strokeDashOffset': 'stroke-dashoffset',
'strokeMiterLimit': 'stroke-miterlimit',
'opacity': 'opacity'
};
var styleProperties = Object.keys(styles);
var ns = metadata.xmlns;
function SVGRenderer(loader) {
Renderer.call(this, loader);
this._dirtyID = 1;
this._dirty = [];
this._svg = null;
this._root = null;
this._defs = null;
}
var prototype$44 = inherits(SVGRenderer, Renderer);
var base$1 = Renderer.prototype;
prototype$44.initialize = function(el, width, height, padding) {
if (el) {
this._svg = domChild(el, 0, 'svg', ns);
this._svg.setAttribute('class', 'marks');
domClear(el, 1);
// set the svg root group
this._root = domChild(this._svg, 0, 'g', ns);
domClear(this._svg, 1);
}
// create the svg definitions cache
this._defs = {
gradient: {},
clipping: {}
};
// set background color if defined
this.background(this._bgcolor);
return base$1.initialize.call(this, el, width, height, padding);
};
prototype$44.background = function(bgcolor) {
if (arguments.length && this._svg) {
this._svg.style.setProperty('background-color', bgcolor);
}
return base$1.background.apply(this, arguments);
};
prototype$44.resize = function(width, height, origin) {
base$1.resize.call(this, width, height, origin);
if (this._svg) {
this._svg.setAttribute('width', this._width);
this._svg.setAttribute('height', this._height);
this._svg.setAttribute('viewBox', '0 0 ' + this._width + ' ' + this._height);
this._root.setAttribute('transform', 'translate(' + this._origin + ')');
}
this._dirty = [];
return this;
};
prototype$44.svg = function() {
if (!this._svg) return null;
var attr = {
'class': 'marks',
'width': this._width,
'height': this._height,
'viewBox': '0 0 ' + this._width + ' ' + this._height
};
for (var key$$1 in metadata) {
attr[key$$1] = metadata[key$$1];
}
return openTag('svg', attr) + this._svg.innerHTML + closeTag('svg');
};
// -- Render entry point --
prototype$44._render = function(scene) {
// perform spot updates and re-render markup
if (this._dirtyCheck()) {
if (this._dirtyAll) this._resetDefs();
this.draw(this._root, scene);
domClear(this._root, 1);
}
this.updateDefs();
this._dirty = [];
++this._dirtyID;
return this;
};
// -- Manage SVG definitions ('defs') block --
prototype$44.updateDefs = function() {
var svg = this._svg,
defs = this._defs,
el = defs.el,
index = 0, id$$1;
for (id$$1 in defs.gradient) {
if (!el) defs.el = (el = domChild(svg, 0, 'defs', ns));
updateGradient(el, defs.gradient[id$$1], index++);
}
for (id$$1 in defs.clipping) {
if (!el) defs.el = (el = domChild(svg, 0, 'defs', ns));
updateClipping(el, defs.clipping[id$$1], index++);
}
// clean-up
if (el) {
if (index === 0) {
svg.removeChild(el);
defs.el = null;
} else {
domClear(el, index);
}
}
};
function updateGradient(el, grad, index) {
var i, n, stop;
el = domChild(el, index, 'linearGradient', ns);
el.setAttribute('id', grad.id);
el.setAttribute('x1', grad.x1);
el.setAttribute('x2', grad.x2);
el.setAttribute('y1', grad.y1);
el.setAttribute('y2', grad.y2);
for (i=0, n=grad.stops.length; i<n; ++i) {
stop = domChild(el, i, 'stop', ns);
stop.setAttribute('offset', grad.stops[i].offset);
stop.setAttribute('stop-color', grad.stops[i].color);
}
domClear(el, i);
}
function updateClipping(el, clip$$1, index) {
var rect;
el = domChild(el, index, 'clipPath', ns);
el.setAttribute('id', clip$$1.id);
rect = domChild(el, 0, 'rect', ns);
rect.setAttribute('x', 0);
rect.setAttribute('y', 0);
rect.setAttribute('width', clip$$1.width);
rect.setAttribute('height', clip$$1.height);
}
prototype$44._resetDefs = function() {
var def = this._defs;
def.gradient = {};
def.clipping = {};
};
// -- Manage rendering of items marked as dirty --
prototype$44.dirty = function(item) {
if (item.dirty !== this._dirtyID) {
item.dirty = this._dirtyID;
this._dirty.push(item);
}
};
prototype$44.isDirty = function(item) {
return this._dirtyAll
|| !item._svg
|| item.dirty === this._dirtyID;
};
prototype$44._dirtyCheck = function() {
this._dirtyAll = true;
var items = this._dirty;
if (!items.length) return true;
var id$$1 = ++this._dirtyID,
item, mark, type, mdef, i, n, o;
for (i=0, n=items.length; i<n; ++i) {
item = items[i];
mark = item.mark;
if (mark.marktype !== type) {
// memoize mark instance lookup
type = mark.marktype;
mdef = marks[type];
}
if (mark.zdirty && mark.dirty !== id$$1) {
this._dirtyAll = false;
mark.dirty = id$$1;
dirtyParents(mark.group, id$$1);
}
if (item.exit) { // EXIT
if (mdef.nested && mark.items.length) {
// if nested mark with remaining points, update instead
o = mark.items[0];
if (o._svg) this._update(mdef, o._svg, o);
} else if (item._svg) {
// otherwise remove from DOM
o = item._svg.parentNode;
if (o) o.removeChild(item._svg);
}
item._svg = null;
continue;
}
item = (mdef.nested ? mark.items[0] : item);
if (item._update === id$$1) continue; // already visited
if (!item._svg || !item._svg.ownerSVGElement) {
// ENTER
this._dirtyAll = false;
dirtyParents(item, id$$1);
} else {
// IN-PLACE UPDATE
this._update(mdef, item._svg, item);
}
item._update = id$$1;
}
return !this._dirtyAll;
};
function dirtyParents(item, id$$1) {
for (; item && item.dirty !== id$$1; item=item.mark.group) {
item.dirty = id$$1;
if (item.mark && item.mark.dirty !== id$$1) {
item.mark.dirty = id$$1;
} else return;
}
}
// -- Construct & maintain scenegraph to SVG mapping ---
// Draw a mark container.
prototype$44.draw = function(el, scene, prev) {
if (!this.isDirty(scene)) return scene._svg;
var renderer = this,
mdef = marks[scene.marktype],
events = scene.interactive === false ? 'none' : null,
isGroup = mdef.tag === 'g',
sibling = null,
i = 0,
parent;
parent = bind(scene, el, prev, 'g');
parent.setAttribute('class', cssClass(scene));
if (!isGroup && events) {
parent.style.setProperty('pointer-events', events);
}
if (scene.clip) {
parent.setAttribute('clip-path', clip(renderer, scene, scene.group));
}
function process(item) {
var dirty = renderer.isDirty(item),
node = bind(item, parent, sibling, mdef.tag);
if (dirty) {
renderer._update(mdef, node, item);
if (isGroup) recurse(renderer, node, item);
}
sibling = node;
++i;
}
if (mdef.nested) {
if (scene.items.length) process(scene.items[0]);
} else {
visit(scene, process);
}
domClear(parent, i);
return parent;
};
// Recursively process group contents.
function recurse(renderer, el, group) {
el = el.lastChild;
var prev, idx = 0;
visit(group, function(item) {
prev = renderer.draw(el, item, prev);
++idx;
});
// remove any extraneous DOM elements
domClear(el, 1 + idx);
}
// Bind a scenegraph item to an SVG DOM element.
// Create new SVG elements as needed.
function bind(item, el, sibling, tag) {
var node = item._svg, doc;
// create a new dom node if needed
if (!node) {
doc = el.ownerDocument;
node = domCreate(doc, tag, ns);
item._svg = node;
if (item.mark) {
node.__data__ = item;
node.__values__ = {fill: 'default'};
// if group, create background and foreground elements
if (tag === 'g') {
var bg = domCreate(doc, 'path', ns);
bg.setAttribute('class', 'background');
node.appendChild(bg);
bg.__data__ = item;
var fg = domCreate(doc, 'g', ns);
node.appendChild(fg);
fg.__data__ = item;
}
}
}
if (doc || node.previousSibling !== sibling || !sibling) {
el.insertBefore(node, sibling ? sibling.nextSibling : el.firstChild);
}
return node;
}
// -- Set attributes & styles on SVG elements ---
var element = null;
var values$1 = null; // temp var for current values hash
// Extra configuration for certain mark types
var mark_extras = {
group: function(mdef, el, item) {
values$1 = el.__values__; // use parent's values hash
element = el.childNodes[1];
mdef.foreground(emit, item, this);
element = el.childNodes[0];
mdef.background(emit, item, this);
var value = item.mark.interactive === false ? 'none' : null;
if (value !== values$1.events) {
element.style.setProperty('pointer-events', value);
values$1.events = value;
}
},
text: function(mdef, el, item) {
var str = textValue(item);
if (str !== values$1.text) {
el.textContent = str;
values$1.text = str;
}
str = font(item);
if (str !== values$1.font) {
el.style.setProperty('font', str);
values$1.font = str;
}
}
};
prototype$44._update = function(mdef, el, item) {
// set dom element and values cache
// provides access to emit method
element = el;
values$1 = el.__values__;
// apply svg attributes
mdef.attr(emit, item, this);
// some marks need special treatment
var extra = mark_extras[mdef.type];
if (extra) extra.call(this, mdef, el, item);
// apply svg css styles
// note: element may be modified by 'extra' method
this.style(element, item);
};
function emit(name, value, ns) {
// early exit if value is unchanged
if (value === values$1[name]) return;
if (value != null) {
// if value is provided, update DOM attribute
if (ns) {
element.setAttributeNS(ns, name, value);
} else {
element.setAttribute(name, value);
}
} else {
// else remove DOM attribute
if (ns) {
element.removeAttributeNS(ns, name);
} else {
element.removeAttribute(name);
}
}
// note current value for future comparison
values$1[name] = value;
}
prototype$44.style = function(el, o) {
if (o == null) return;
var i, n, prop, name, value;
for (i=0, n=styleProperties.length; i<n; ++i) {
prop = styleProperties[i];
value = o[prop];
if (value === values$1[prop]) continue;
name = styles[prop];
if (value == null) {
if (name === 'fill') {
el.style.setProperty(name, 'none');
} else {
el.style.removeProperty(name);
}
} else {
if (value.id) {
// ensure definition is included
this._defs.gradient[value.id] = value;
value = 'url(' + href() + '#' + value.id + ')';
}
el.style.setProperty(name, value+'');
}
values$1[prop] = value;
}
};
function href() {
var loc;
return typeof window === 'undefined' ? ''
: (loc = window.location).hash ? loc.href.slice(0, -loc.hash.length)
: loc.href;
}
function SVGStringRenderer(loader) {
Renderer.call(this, loader);
this._text = {
head: '',
root: '',
foot: '',
defs: '',
body: ''
};
this._defs = {
gradient: {},
clipping: {}
};
}
var prototype$45 = inherits(SVGStringRenderer, Renderer);
var base$2 = Renderer.prototype;
prototype$45.resize = function(width, height, origin) {
base$2.resize.call(this, width, height, origin);
var o = this._origin,
t = this._text;
var attr = {
'class': 'marks',
'width': this._width,
'height': this._height,
'viewBox': '0 0 ' + this._width + ' ' + this._height
};
for (var key$$1 in metadata) {
attr[key$$1] = metadata[key$$1];
}
t.head = openTag('svg', attr);
t.root = openTag('g', {
transform: 'translate(' + o + ')'
});
t.foot = closeTag('g') + closeTag('svg');
return this;
};
prototype$45.svg = function() {
var t = this._text;
return t.head + t.defs + t.root + t.body + t.foot;
};
prototype$45._render = function(scene) {
this._text.body = this.mark(scene);
this._text.defs = this.buildDefs();
return this;
};
prototype$45.buildDefs = function() {
var all = this._defs,
defs = '',
i, id$$1, def, stops;
for (id$$1 in all.gradient) {
def = all.gradient[id$$1];
stops = def.stops;
defs += openTag('linearGradient', {
id: id$$1,
x1: def.x1,
x2: def.x2,
y1: def.y1,
y2: def.y2
});
for (i=0; i<stops.length; ++i) {
defs += openTag('stop', {
offset: stops[i].offset,
'stop-color': stops[i].color
}) + closeTag('stop');
}
defs += closeTag('linearGradient');
}
for (id$$1 in all.clipping) {
def = all.clipping[id$$1];
defs += openTag('clipPath', {id: id$$1});
defs += openTag('rect', {
x: 0,
y: 0,
width: def.width,
height: def.height
}) + closeTag('rect');
defs += closeTag('clipPath');
}
return (defs.length > 0) ? openTag('defs') + defs + closeTag('defs') : '';
};
var object$1;
function emit$1(name, value, ns, prefixed) {
object$1[prefixed || name] = value;
}
prototype$45.attributes = function(attr, item) {
object$1 = {};
attr(emit$1, item, this);
return object$1;
};
prototype$45.href = function(item) {
var that = this,
href = item.href,
attr;
if (href) {
if (attr = that._hrefs && that._hrefs[href]) {
return attr;
} else {
that.sanitizeURL(href).then(function(attr) {
// rewrite to use xlink namespace
// note that this will be deprecated in SVG 2.0
attr['xlink:href'] = attr.href;
attr.href = null;
(that._hrefs || (that._hrefs = {}))[href] = attr;
});
}
}
return null;
};
prototype$45.mark = function(scene) {
var renderer = this,
mdef = marks[scene.marktype],
tag = mdef.tag,
defs = this._defs,
str = '',
style;
if (tag !== 'g' && scene.interactive === false) {
style = 'style="pointer-events: none;"';
}
// render opening group tag
str += openTag('g', {
'class': cssClass(scene),
'clip-path': scene.clip ? clip(renderer, scene, scene.group) : null
}, style);
// render contained elements
function process(item) {
var href = renderer.href(item);
if (href) str += openTag('a', href);
style = (tag !== 'g') ? applyStyles(item, scene, tag, defs) : null;
str += openTag(tag, renderer.attributes(mdef.attr, item), style);
if (tag === 'text') {
str += escape_text(textValue(item));
} else if (tag === 'g') {
str += openTag('path', renderer.attributes(mdef.background, item),
applyStyles(item, scene, 'bgrect', defs)) + closeTag('path');
str += openTag('g', renderer.attributes(mdef.foreground, item))
+ renderer.markGroup(item)
+ closeTag('g');
}
str += closeTag(tag);
if (href) str += closeTag('a');
}
if (mdef.nested) {
if (scene.items && scene.items.length) process(scene.items[0]);
} else {
visit(scene, process);
}
// render closing group tag
return str + closeTag('g');
};
prototype$45.markGroup = function(scene) {
var renderer = this,
str = '';
visit(scene, function(item) {
str += renderer.mark(item);
});
return str;
};
function applyStyles(o, mark, tag, defs) {
if (o == null) return '';
var i, n, prop, name, value, s = '';
if (tag === 'bgrect' && mark.interactive === false) {
s += 'pointer-events: none;';
}
if (tag === 'text') {
s += 'font: ' + font(o) + ';';
}
for (i=0, n=styleProperties.length; i<n; ++i) {
prop = styleProperties[i];
name = styles[prop];
value = o[prop];
if (value == null) {
if (name === 'fill') {
s += (s.length ? ' ' : '') + 'fill: none;';
}
} else {
if (value.id) {
// ensure definition is included
defs.gradient[value.id] = value;
value = 'url(#' + value.id + ')';
}
s += (s.length ? ' ' : '') + name + ': ' + value + ';';
}
}
return s ? 'style="' + s + '"' : null;
}
function escape_text(s) {
return s.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
var Canvas$2 = 'canvas';
var PNG = 'png';
var SVG = 'svg';
var None$1 = 'none';
var RenderType = {
Canvas: Canvas$2,
PNG: PNG,
SVG: SVG,
None: None$1
};
var modules = {};
modules[Canvas$2] = modules[PNG] = {
renderer: CanvasRenderer,
headless: CanvasRenderer,
handler: CanvasHandler
};
modules[SVG] = {
renderer: SVGRenderer,
headless: SVGStringRenderer,
handler: SVGHandler
};
modules[None$1] = {};
function renderModule(name, _) {
name = String(name || '').toLowerCase();
if (arguments.length > 1) {
modules[name] = _;
return this;
} else {
return modules[name];
}
}
var TOLERANCE = 1e-9;
function sceneEqual(a, b, key$$1) {
return (a === b) ? true
: (key$$1 === 'path') ? pathEqual(a, b)
: (a instanceof Date && b instanceof Date) ? +a === +b
: (isNumber(a) && isNumber(b)) ? Math.abs(a - b) <= TOLERANCE
: (!a || !b || !isObject(a) && !isObject(b)) ? a == b
: (a == null || b == null) ? false
: objectEqual(a, b);
}
function pathEqual(a, b) {
return sceneEqual(pathParse(a), pathParse(b));
}
function objectEqual(a, b) {
var ka = Object.keys(a),
kb = Object.keys(b),
key$$1, i;
if (ka.length !== kb.length) return false;
ka.sort();
kb.sort();
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i]) return false;
}
for (i = ka.length - 1; i >= 0; i--) {
key$$1 = ka[i];
if (!sceneEqual(a[key$$1], b[key$$1], key$$1)) return false;
}
return typeof a === typeof b;
}
/**
* Calculate bounding boxes for scenegraph items.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {object} params.mark - The scenegraph mark instance to bound.
*/
function Bound(params) {
Transform.call(this, null, params);
}
var prototype$35 = inherits(Bound, Transform);
var temp = new Bounds();
prototype$35.transform = function(_, pulse) {
var view = pulse.dataflow,
mark = _.mark,
type = mark.marktype,
entry = marks[type],
bound = entry.bound,
clip = mark.clip,
markBounds = mark.bounds, rebound;
if (entry.nested) {
// multi-item marks have a single bounds instance
if (mark.items.length) view.dirty(mark.items[0]);
markBounds = boundItem(mark, bound);
mark.items.forEach(function(item) {
item.bounds.clear().union(markBounds);
});
}
else if (type === 'group' || _.modified()) {
// operator parameters modified -> re-bound all items
// updates group bounds in response to modified group content
pulse.visit(pulse.MOD, function(item) { view.dirty(item); });
markBounds.clear();
mark.items.forEach(function(item) {
markBounds.union(boundItem(item, bound));
});
}
else {
// incrementally update bounds, re-bound mark as needed
rebound = pulse.changed(pulse.REM);
pulse.visit(pulse.ADD, function(item) {
markBounds.union(boundItem(item, bound));
});
pulse.visit(pulse.MOD, function(item) {
rebound = rebound || markBounds.alignsWith(item.bounds);
view.dirty(item);
markBounds.union(boundItem(item, bound));
});
if (rebound && !clip) {
markBounds.clear();
mark.items.forEach(function(item) { markBounds.union(item.bounds); });
}
}
if (clip) {
markBounds.intersect(temp.set(0, 0, mark.group.width, mark.group.height));
}
return pulse.modifies('bounds');
};
function boundItem(item, bound, opt) {
return bound(item.bounds.clear(), item, opt);
}
var COUNTER_NAME = ':vega_identifier:';
/**
* Adds a unique identifier to all added tuples.
* This transform creates a new signal that serves as an id counter.
* As a result, the id counter is shared across all instances of this
* transform, generating unique ids across multiple data streams. In
* addition, this signal value can be included in a snapshot of the
* dataflow state, enabling correct resumption of id allocation.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {string} params.as - The field name for the generated identifier.
*/
function Identifier(params) {
Transform.call(this, 0, params);
}
Identifier.Definition = {
"type": "Identifier",
"metadata": {"modifies": true},
"params": [
{ "name": "as", "type": "string", "required": true }
]
};
var prototype$46 = inherits(Identifier, Transform);
prototype$46.transform = function(_, pulse) {
var counter = getCounter(pulse.dataflow),
id$$1 = counter.value,
as = _.as;
pulse.visit(pulse.ADD, function(t) {
if (!t[as]) t[as] = ++id$$1;
});
counter.set(this.value = id$$1);
return pulse;
};
function getCounter(view) {
var counter = view._signals[COUNTER_NAME];
if (!counter) {
view._signals[COUNTER_NAME] = (counter = view.add(0));
}
return counter;
}
/**
* Bind scenegraph items to a scenegraph mark instance.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {object} params.markdef - The mark definition for creating the mark.
* This is an object of legal scenegraph mark properties which *must* include
* the 'marktype' property.
* @param {Array<number>} params.scenepath - Scenegraph tree coordinates for the mark.
* The path is an array of integers, each indicating the index into
* a successive chain of items arrays.
*/
function Mark(params) {
Transform.call(this, null, params);
}
var prototype$47 = inherits(Mark, Transform);
prototype$47.transform = function(_, pulse) {
var mark = this.value;
// acquire mark on first invocation, bind context and group
if (!mark) {
mark = pulse.dataflow.scenegraph().mark(_.markdef, lookup$1(_), _.index);
mark.group.context = _.context;
if (!_.context.group) _.context.group = mark.group;
mark.source = this;
this.value = mark;
}
// initialize entering items
var Init = mark.marktype === 'group' ? GroupItem : Item;
pulse.visit(pulse.ADD, function(item) { Init.call(item, mark); });
// bind items array to scenegraph mark
mark.items = pulse.source;
return pulse;
};
function lookup$1(_) {
var g = _.groups, p = _.parent;
return g && g.size === 1 ? g.get(Object.keys(g.object)[0])
: g && p ? g.lookup(p)
: null;
}
/**
* Analyze items for overlap, changing opacity to hide items with
* overlapping bounding boxes. This transform will preserve at least
* two items (e.g., first and last) even if overlap persists.
* @param {object} params - The parameters for this operator.
* @param {object} params.method - The overlap removal method to apply.
* One of 'parity' (default, hide every other item until there is no
* more overlap) or 'greedy' (sequentially scan and hide and items that
* overlap with the last visible item).
* @constructor
*/
function Overlap(params) {
Transform.call(this, null, params);
}
var prototype$48 = inherits(Overlap, Transform);
var methods = {
parity: function(items) {
return items.filter(function(item, i) {
return i % 2 ? (item.opacity = 0) : 1;
});
},
greedy: function(items) {
var a;
return items.filter(function(b, i) {
if (!i || !intersect$1(a.bounds, b.bounds)) {
a = b;
return 1;
} else {
return b.opacity = 0;
}
});
}
};
// compute bounding box intersection
// allow 1 pixel of overlap tolerance
function intersect$1(a, b) {
return !(
a.x2 - 1 < b.x1 ||
a.x1 + 1 > b.x2 ||
a.y2 - 1 < b.y1 ||
a.y1 + 1 > b.y2
);
}
function hasOverlap(items) {
for (var i=1, n=items.length, a=items[0].bounds, b; i<n; a=b, ++i) {
if (intersect$1(a, b = items[i].bounds)) return true;
}
}
function hasBounds(item) {
var b = item.bounds;
return b.width() > 1 && b.height() > 1;
}
prototype$48.transform = function(_, pulse) {
var reduce = methods[_.method] || methods.parity,
source = pulse.materialize(pulse.SOURCE).source,
items = source;
if (!items) return;
if (_.method === 'greedy') {
items = source = source.filter(hasBounds);
}
if (items.length >= 3 && hasOverlap(items)) {
pulse = pulse.reflow(_.modified()).modifies('opacity');
do {
items = reduce(items);
} while (items.length >= 3 && hasOverlap(items));
if (items.length < 3 && !peek(source).opacity) {
if (items.length > 1) peek(items).opacity = 0;
peek(source).opacity = 1;
}
}
return pulse;
};
/**
* Queue modified scenegraph items for rendering.
* @constructor
*/
function Render(params) {
Transform.call(this, null, params);
}
var prototype$49 = inherits(Render, Transform);
prototype$49.transform = function(_, pulse) {
var view = pulse.dataflow;
pulse.visit(pulse.ALL, function(item) { view.dirty(item); });
// set z-index dirty flag as needed
if (pulse.fields && pulse.fields['zindex']) {
var item = pulse.source && pulse.source[0];
if (item) item.mark.zdirty = true;
}
};
var AxisRole$1 = 'axis';
var LegendRole$1 = 'legend';
var RowHeader$1 = 'row-header';
var RowFooter$1 = 'row-footer';
var RowTitle = 'row-title';
var ColHeader$1 = 'column-header';
var ColFooter$1 = 'column-footer';
var ColTitle = 'column-title';
function extractGroups(group) {
var groups = group.items,
n = groups.length,
i = 0, mark, items;
var views = {
marks: [],
rowheaders: [],
rowfooters: [],
colheaders: [],
colfooters: [],
rowtitle: null,
coltitle: null
};
// layout axes, gather legends, collect bounds
for (; i<n; ++i) {
mark = groups[i];
items = mark.items;
if (mark.marktype === 'group') {
switch (mark.role) {
case AxisRole$1:
case LegendRole$1:
break;
case RowHeader$1: addAll(items, views.rowheaders); break;
case RowFooter$1: addAll(items, views.rowfooters); break;
case ColHeader$1: addAll(items, views.colheaders); break;
case ColFooter$1: addAll(items, views.colfooters); break;
case RowTitle: views.rowtitle = items[0]; break;
case ColTitle: views.coltitle = items[0]; break;
default: addAll(items, views.marks);
}
}
}
return views;
}
function addAll(items, array$$1) {
for (var i=0, n=items.length; i<n; ++i) {
array$$1.push(items[i]);
}
}
function bboxFlush(item) {
return {x1: 0, y1: 0, x2: item.width || 0, y2: item.height || 0};
}
function bboxFull(item) {
var b = item.bounds.clone();
return b.empty()
? b.set(0, 0, 0, 0)
: b.translate(-(item.x||0), -(item.y||0));
}
function boundFlush(item, field$$1) {
return field$$1 === 'x1' ? (item.x || 0)
: field$$1 === 'y1' ? (item.y || 0)
: field$$1 === 'x2' ? (item.x || 0) + (item.width || 0)
: field$$1 === 'y2' ? (item.y || 0) + (item.height || 0)
: undefined;
}
function boundFull(item, field$$1) {
return item.bounds[field$$1];
}
function get$1(opt, key$$1, d) {
var v = isObject(opt) ? opt[key$$1] : opt;
return v != null ? v : (d !== undefined ? d : 0);
}
function offsetValue(v) {
return v < 0 ? Math.ceil(-v) : 0;
}
function gridLayout(view, group, opt) {
var views = extractGroups(group, opt),
groups = views.marks,
flush = opt.bounds === 'flush',
bbox = flush ? bboxFlush : bboxFull,
bounds = new Bounds(0, 0, 0, 0),
alignCol = get$1(opt.align, 'column'),
alignRow = get$1(opt.align, 'row'),
padCol = get$1(opt.padding, 'column'),
padRow = get$1(opt.padding, 'row'),
off = opt.offset,
ncols = group.columns || opt.columns || groups.length,
nrows = ncols < 0 ? 1 : Math.ceil(groups.length / ncols),
cells = nrows * ncols,
xOffset = [], xExtent = [], xInit = 0,
yOffset = [], yExtent = [], yInit = 0,
n = groups.length,
m, i, c, r, b, g, px, py, x, y, band, extent, offset;
for (i=0; i<ncols; ++i) {
xExtent[i] = 0;
}
for (i=0; i<nrows; ++i) {
yExtent[i] = 0;
}
// determine offsets for each group
for (i=0; i<n; ++i) {
b = bbox(groups[i]);
c = i % ncols;
r = ~~(i / ncols);
px = c ? Math.ceil(bbox(groups[i-1]).x2): 0;
py = r ? Math.ceil(bbox(groups[i-ncols]).y2): 0;
xExtent[c] = Math.max(xExtent[c], px);
yExtent[r] = Math.max(yExtent[r], py);
xOffset.push(padCol + offsetValue(b.x1));
yOffset.push(padRow + offsetValue(b.y1));
view.dirty(groups[i]);
}
// set initial alignment offsets
for (i=0; i<n; ++i) {
if (i % ncols === 0) xOffset[i] = xInit;
if (i < ncols) yOffset[i] = yInit;
}
// enforce column alignment constraints
if (alignCol === 'each') {
for (c=1; c<ncols; ++c) {
for (offset=0, i=c; i<n; i += ncols) {
if (offset < xOffset[i]) offset = xOffset[i];
}
for (i=c; i<n; i += ncols) {
xOffset[i] = offset + xExtent[c];
}
}
} else if (alignCol === 'all') {
for (extent=0, c=1; c<ncols; ++c) {
if (extent < xExtent[c]) extent = xExtent[c];
}
for (offset=0, i=0; i<n; ++i) {
if (i % ncols && offset < xOffset[i]) offset = xOffset[i];
}
for (i=0; i<n; ++i) {
if (i % ncols) xOffset[i] = offset + extent;
}
} else {
for (c=1; c<ncols; ++c) {
for (i=c; i<n; i += ncols) {
xOffset[i] += xExtent[c];
}
}
}
// enforce row alignment constraints
if (alignRow === 'each') {
for (r=1; r<nrows; ++r) {
for (offset=0, i=r*ncols, m=i+ncols; i<m; ++i) {
if (offset < yOffset[i]) offset = yOffset[i];
}
for (i=r*ncols; i<m; ++i) {
yOffset[i] = offset + yExtent[r];
}
}
} else if (alignRow === 'all') {
for (extent=0, r=1; r<nrows; ++r) {
if (extent < yExtent[r]) extent = yExtent[r];
}
for (offset=0, i=ncols; i<n; ++i) {
if (offset < yOffset[i]) offset = yOffset[i];
}
for (i=ncols; i<n; ++i) {
yOffset[i] = offset + extent;
}
} else {
for (r=1; r<nrows; ++r) {
for (i=r*ncols, m=i+ncols; i<m; ++i) {
yOffset[i] += yExtent[r];
}
}
}
// perform horizontal grid layout
for (x=0, i=0; i<n; ++i) {
g = groups[i];
px = g.x || 0;
g.x = (x = xOffset[i] + (i % ncols ? x : 0));
g.bounds.translate(x - px, 0);
}
// perform vertical grid layout
for (c=0; c<ncols; ++c) {
for (y=0, i=c; i<n; i += ncols) {
g = groups[i];
py = g.y || 0;
g.y = (y += yOffset[i]);
g.bounds.translate(0, y - py);
}
}
// update mark bounds, mark dirty
for (i=0; i<n; ++i) groups[i].mark.bounds.clear();
for (i=0; i<n; ++i) {
g = groups[i];
view.dirty(g);
bounds.union(g.mark.bounds.union(g.bounds));
}
// -- layout grid headers and footers --
// aggregation functions for grid margin determination
function min(a, b) { return Math.floor(Math.min(a, b)); }
function max(a, b) { return Math.ceil(Math.max(a, b)); }
// bounding box calculation methods
bbox = flush ? boundFlush : boundFull;
// perform row header layout
band = get$1(opt.headerBand, 'row', null);
x = layoutHeaders(view, views.rowheaders, groups, ncols, nrows, -get$1(off, 'rowHeader'), min, 0, bbox, 'x1', 0, ncols, 1, band);
// perform column header layout
band = get$1(opt.headerBand, 'column', null);
y = layoutHeaders(view, views.colheaders, groups, ncols, ncols, -get$1(off, 'columnHeader'), min, 1, bbox, 'y1', 0, 1, ncols, band);
// perform row footer layout
band = get$1(opt.footerBand, 'row', null);
layoutHeaders( view, views.rowfooters, groups, ncols, nrows, get$1(off, 'rowFooter'), max, 0, bbox, 'x2', ncols-1, ncols, 1, band);
// perform column footer layout
band = get$1(opt.footerBand, 'column', null);
layoutHeaders( view, views.colfooters, groups, ncols, ncols, get$1(off, 'columnFooter'), max, 1, bbox, 'y2', cells-ncols, 1, ncols, band);
// perform row title layout
if (views.rowtitle) {
offset = x - get$1(off, 'rowTitle');
band = get$1(opt.titleBand, 'row', 0.5);
layoutTitle$1(view, views.rowtitle, offset, 0, bounds, band);
}
// perform column title layout
if (views.coltitle) {
offset = y - get$1(off, 'columnTitle');
band = get$1(opt.titleBand, 'column', 0.5);
layoutTitle$1(view, views.coltitle, offset, 1, bounds, band);
}
}
function layoutHeaders(view, headers, groups, ncols, limit, offset, agg, isX, bound, bf, start, stride, back, band) {
var n = groups.length,
init = 0,
edge = 0,
i, j, k, m, b, h, g, x, y;
// compute margin
for (i=start; i<n; i+=stride) {
if (groups[i]) init = agg(init, bound(groups[i], bf));
}
// if no headers, return margin calculation
if (!headers.length) return init;
// check if number of headers exceeds number of rows or columns
if (headers.length > limit) {
view.warn('Grid headers exceed limit: ' + limit);
headers = headers.slice(0, limit);
}
// apply offset
init += offset;
// clear mark bounds for all headers
for (j=0, m=headers.length; j<m; ++j) {
view.dirty(headers[j]);
headers[j].mark.bounds.clear();
}
// layout each header
for (i=start, j=0, m=headers.length; j<m; ++j, i+=stride) {
h = headers[j];
b = h.mark.bounds;
// search for nearest group to align to
// necessary if table has empty cells
for (k=i; (g = groups[k]) == null; k-=back);
// assign coordinates and update bounds
if (isX) {
x = band == null ? g.x : Math.round(g.bounds.x1 + band * g.bounds.width());
y = init;
} else {
x = init;
y = band == null ? g.y : Math.round(g.bounds.y1 + band * g.bounds.height());
}
b.union(h.bounds.translate(x - (h.x || 0), y - (h.y || 0)));
h.x = x;
h.y = y;
view.dirty(h);
// update current edge of layout bounds
edge = agg(edge, b[bf]);
}
return edge;
}
function layoutTitle$1(view, g, offset, isX, bounds, band) {
if (!g) return;
view.dirty(g);
// compute title coordinates
var x = offset, y = offset;
isX
? (x = Math.round(bounds.x1 + band * bounds.width()))
: (y = Math.round(bounds.y1 + band * bounds.height()));
// assign coordinates and update bounds
g.bounds.translate(x - (g.x || 0), y - (g.y || 0));
g.mark.bounds.clear().union(g.bounds);
g.x = x;
g.y = y;
// queue title for redraw
view.dirty(g);
}
var Fit = 'fit';
var Pad = 'pad';
var None$2 = 'none';
var Padding = 'padding';
var Top = 'top';
var Left = 'left';
var Right = 'right';
var Bottom = 'bottom';
var AxisRole = 'axis';
var TitleRole = 'title';
var FrameRole = 'frame';
var LegendRole = 'legend';
var ScopeRole = 'scope';
var RowHeader = 'row-header';
var RowFooter = 'row-footer';
var ColHeader = 'column-header';
var ColFooter = 'column-footer';
var AxisOffset = 0.5;
var tempBounds$2 = new Bounds();
/**
* Layout view elements such as axes and legends.
* Also performs size adjustments.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {object} params.mark - Scenegraph mark of groups to layout.
*/
function ViewLayout(params) {
Transform.call(this, null, params);
}
var prototype$50 = inherits(ViewLayout, Transform);
prototype$50.transform = function(_, pulse) {
// TODO incremental update, output?
var view = pulse.dataflow;
_.mark.items.forEach(function(group) {
if (_.layout) gridLayout(view, group, _.layout);
layoutGroup(view, group, _);
});
return pulse;
};
function layoutGroup(view, group, _) {
var items = group.items,
width = Math.max(0, group.width || 0),
height = Math.max(0, group.height || 0),
viewBounds = new Bounds().set(0, 0, width, height),
axisBounds = viewBounds.clone(),
xBounds = viewBounds.clone(),
yBounds = viewBounds.clone(),
legends = [], title,
mark, flow, b, i, n;
// layout axes, gather legends, collect bounds
for (i=0, n=items.length; i<n; ++i) {
mark = items[i];
switch (mark.role) {
case AxisRole:
axisBounds.union(b = layoutAxis(view, mark, width, height));
(isYAxis(mark) ? xBounds : yBounds).union(b);
break;
case TitleRole:
title = mark; break;
case LegendRole:
legends.push(mark); break;
case FrameRole:
case ScopeRole:
case RowHeader:
case RowFooter:
case ColHeader:
case ColFooter:
xBounds.union(mark.bounds);
yBounds.union(mark.bounds);
break;
default:
viewBounds.union(mark.bounds);
}
}
// layout title, adjust bounds
if (title) {
axisBounds.union(b = layoutTitle(view, title, axisBounds));
(isYAxis(title) ? xBounds : yBounds).union(b);
}
// layout legends, adjust viewBounds
if (legends.length) {
flow = {left: 0, right: 0, top: 0, bottom: 0, margin: _.legendMargin || 8};
for (i=0, n=legends.length; i<n; ++i) {
b = layoutLegend(view, legends[i], flow, xBounds, yBounds, width, height);
if (_.autosize && _.autosize.type === Fit) {
// for autosize fit, incorporate the orthogonal dimension only
// legends that overrun the chart area will then be clipped
// otherwise the chart area gets reduced to nothing!
var orient = legends[i].items[0].datum.orient;
if (orient === Left || orient === Right) {
viewBounds.add(b.x1, 0).add(b.x2, 0);
} else if (orient === Top || orient === Bottom) {
viewBounds.add(0, b.y1).add(0, b.y2);
}
} else {
viewBounds.union(b);
}
}
}
// perform size adjustment
viewBounds.union(xBounds).union(yBounds).union(axisBounds);
layoutSize(view, group, viewBounds, _);
}
function set$3(item, property, value) {
if (item[property] === value) {
return 0;
} else {
item[property] = value;
return 1;
}
}
function isYAxis(mark) {
var orient = mark.items[0].datum.orient;
return orient === Left || orient === Right;
}
function axisIndices(datum) {
var index = +datum.grid;
return [
datum.ticks ? index++ : -1, // ticks index
datum.labels ? index++ : -1, // labels index
index + (+datum.domain) // title index
];
}
function layoutAxis(view, axis, width, height) {
var item = axis.items[0],
datum = item.datum,
orient = datum.orient,
indices = axisIndices(datum),
range = item.range,
offset = item.offset,
position = item.position,
minExtent = item.minExtent,
maxExtent = item.maxExtent,
title = datum.title && item.items[indices[2]].items[0],
titlePadding = item.titlePadding,
bounds = item.bounds,
x = 0, y = 0, i, s;
tempBounds$2.clear().union(bounds);
bounds.clear();
if ((i=indices[0]) > -1) bounds.union(item.items[i].bounds);
if ((i=indices[1]) > -1) bounds.union(item.items[i].bounds);
// position axis group and title
switch (orient) {
case Top:
x = position || 0;
y = -offset;
s = Math.max(minExtent, Math.min(maxExtent, -bounds.y1));
if (title) {
if (title.auto) {
s += titlePadding;
title.y = -s;
s += title.bounds.height();
} else {
bounds.union(title.bounds);
}
}
bounds.add(0, -s).add(range, 0);
break;
case Left:
x = -offset;
y = position || 0;
s = Math.max(minExtent, Math.min(maxExtent, -bounds.x1));
if (title) {
if (title.auto) {
s += titlePadding;
title.x = -s;
s += title.bounds.width();
} else {
bounds.union(title.bounds);
}
}
bounds.add(-s, 0).add(0, range);
break;
case Right:
x = width + offset;
y = position || 0;
s = Math.max(minExtent, Math.min(maxExtent, bounds.x2));
if (title) {
if (title.auto) {
s += titlePadding;
title.x = s;
s += title.bounds.width();
} else {
bounds.union(title.bounds);
}
}
bounds.add(0, 0).add(s, range);
break;
case Bottom:
x = position || 0;
y = height + offset;
s = Math.max(minExtent, Math.min(maxExtent, bounds.y2));
if (title) if (title.auto) {
s += titlePadding;
title.y = s;
s += title.bounds.height();
} else {
bounds.union(title.bounds);
}
bounds.add(0, 0).add(range, s);
break;
default:
x = item.x;
y = item.y;
}
// update bounds
boundStroke(bounds.translate(x, y), item);
if (set$3(item, 'x', x + AxisOffset) | set$3(item, 'y', y + AxisOffset)) {
item.bounds = tempBounds$2;
view.dirty(item);
item.bounds = bounds;
view.dirty(item);
}
return item.mark.bounds.clear().union(bounds);
}
function layoutTitle(view, title, axisBounds) {
var item = title.items[0],
datum = item.datum,
orient = datum.orient,
offset = item.offset,
bounds = item.bounds,
x = 0, y = 0;
tempBounds$2.clear().union(bounds);
// position axis group and title
switch (orient) {
case Top:
x = item.x;
y = axisBounds.y1 - offset;
break;
case Left:
x = axisBounds.x1 - offset;
y = item.y;
break;
case Right:
x = axisBounds.x2 + offset;
y = item.y;
break;
case Bottom:
x = item.x;
y = axisBounds.y2 + offset;
break;
default:
x = item.x;
y = item.y;
}
bounds.translate(x - item.x, y - item.y);
if (set$3(item, 'x', x) | set$3(item, 'y', y)) {
item.bounds = tempBounds$2;
view.dirty(item);
item.bounds = bounds;
view.dirty(item);
}
// update bounds
return title.bounds.clear().union(bounds);
}
function layoutLegend(view, legend, flow, xBounds, yBounds, width, height) {
var item = legend.items[0],
datum = item.datum,
orient = datum.orient,
offset = item.offset,
bounds = item.bounds,
x = 0,
y = 0,
w, h, axisBounds;
if (orient === Top || orient === Bottom) {
axisBounds = yBounds,
x = flow[orient];
} else if (orient === Left || orient === Right) {
axisBounds = xBounds;
y = flow[orient];
}
tempBounds$2.clear().union(bounds);
bounds.clear();
// aggregate bounds to determine size
// shave off 1 pixel because it looks better...
item.items.forEach(function(_) { bounds.union(_.bounds); });
w = Math.round(bounds.width()) + 2 * item.padding - 1;
h = Math.round(bounds.height()) + 2 * item.padding - 1;
switch (orient) {
case Left:
x -= w + offset - Math.floor(axisBounds.x1);
flow.left += h + flow.margin;
break;
case Right:
x += offset + Math.ceil(axisBounds.x2);
flow.right += h + flow.margin;
break;
case Top:
y -= h + offset - Math.floor(axisBounds.y1);
flow.top += w + flow.margin;
break;
case Bottom:
y += offset + Math.ceil(axisBounds.y2);
flow.bottom += w + flow.margin;
break;
case 'top-left':
x += offset;
y += offset;
break;
case 'top-right':
x += width - w - offset;
y += offset;
break;
case 'bottom-left':
x += offset;
y += height - h - offset;
break;
case 'bottom-right':
x += width - w - offset;
y += height - h - offset;
break;
default:
x = item.x;
y = item.y;
}
// update bounds
boundStroke(bounds.set(x, y, x + w, y + h), item);
// update legend layout
if (set$3(item, 'x', x) | set$3(item, 'width', w) |
set$3(item, 'y', y) | set$3(item, 'height', h)) {
item.bounds = tempBounds$2;
view.dirty(item);
item.bounds = bounds;
view.dirty(item);
}
return item.mark.bounds.clear().union(bounds);
}
function layoutSize(view, group, viewBounds, _) {
var auto = _.autosize || {},
type = auto.type,
viewWidth = view._width,
viewHeight = view._height,
padding = view.padding();
if (view._autosize < 1 || !type) return;
var width = Math.max(0, group.width || 0),
left = Math.max(0, Math.ceil(-viewBounds.x1)),
right = Math.max(0, Math.ceil(viewBounds.x2 - width)),
height = Math.max(0, group.height || 0),
top = Math.max(0, Math.ceil(-viewBounds.y1)),
bottom = Math.max(0, Math.ceil(viewBounds.y2 - height));
if (auto.contains === Padding) {
viewWidth -= padding.left + padding.right;
viewHeight -= padding.top + padding.bottom;
}
if (type === None$2) {
left = 0;
top = 0;
width = viewWidth;
height = viewHeight;
}
else if (type === Fit) {
width = Math.max(0, viewWidth - left - right);
height = Math.max(0, viewHeight - top - bottom);
}
else if (type === Pad) {
viewWidth = width + left + right;
viewHeight = height + top + bottom;
}
view._resizeView(
viewWidth, viewHeight,
width, height,
[left, top],
auto.resize
);
}
var vtx = Object.freeze({
bound: Bound,
identifier: Identifier,
mark: Mark,
overlap: Overlap,
render: Render,
viewlayout: ViewLayout
});
var Log = 'log';
var Pow = 'pow';
var Sqrt = 'sqrt';
var Band = 'band';
var Point = 'point';
var Linear$1 = 'linear';
var Ordinal = 'ordinal';
var Quantile = 'quantile';
var Quantize = 'quantize';
var Threshold = 'threshold';
var BinLinear = 'bin-linear';
var BinOrdinal = 'bin-ordinal';
var Sequential = 'sequential';
var invertRange = function(scale) {
return function(_) {
var lo = _[0],
hi = _[1],
t;
if (hi < lo) {
t = lo;
lo = hi;
hi = t;
}
return [
scale.invert(lo),
scale.invert(hi)
];
}
};
var invertRangeExtent = function(scale) {
return function(_) {
var range = scale.range(),
lo = _[0],
hi = _[1],
min = -1, max, t, i, n;
if (hi < lo) {
t = lo;
lo = hi;
hi = t;
}
for (i=0, n=range.length; i<n; ++i) {
if (range[i] >= lo && range[i] <= hi) {
if (min < 0) min = i;
max = i;
}
}
if (min < 0) return undefined;
lo = scale.invertExtent(range[min]);
hi = scale.invertExtent(range[max]);
return [
lo[0] === undefined ? lo[1] : lo[0],
hi[1] === undefined ? hi[0] : hi[1]
];
}
};
var bandSpace = function(count, paddingInner, paddingOuter) {
var space = count - paddingInner + paddingOuter * 2;
return count ? (space > 0 ? space : 1) : 0;
};
var array$2 = Array.prototype;
var map$3 = array$2.map;
var slice$2 = array$2.slice;
var implicit = {name: "implicit"};
function ordinal(range) {
var index = map(),
domain = [],
unknown = implicit;
range = range == null ? [] : slice$2.call(range);
function scale(d) {
var key = d + "", i = index.get(key);
if (!i) {
if (unknown !== implicit) return unknown;
index.set(key, i = domain.push(d));
}
return range[(i - 1) % range.length];
}
scale.domain = function(_) {
if (!arguments.length) return domain.slice();
domain = [], index = map();
var i = -1, n = _.length, d, key;
while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d));
return scale;
};
scale.range = function(_) {
return arguments.length ? (range = slice$2.call(_), scale) : range.slice();
};
scale.unknown = function(_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
scale.copy = function() {
return ordinal()
.domain(domain)
.range(range)
.unknown(unknown);
};
return scale;
}
var define = function(constructor, factory, prototype) {
constructor.prototype = factory.prototype = prototype;
prototype.constructor = constructor;
};
function extend$1(parent, definition) {
var prototype = Object.create(parent.prototype);
for (var key in definition) prototype[key] = definition[key];
return prototype;
}
function Color() {}
var darker = 0.7;
var brighter = 1 / darker;
var reI = "\\s*([+-]?\\d+)\\s*";
var reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*";
var reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*";
var reHex3 = /^#([0-9a-f]{3})$/;
var reHex6 = /^#([0-9a-f]{6})$/;
var reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$");
var reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$");
var reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$");
var reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$");
var reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$");
var reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
var named = {
aliceblue: 0xf0f8ff,
antiquewhite: 0xfaebd7,
aqua: 0x00ffff,
aquamarine: 0x7fffd4,
azure: 0xf0ffff,
beige: 0xf5f5dc,
bisque: 0xffe4c4,
black: 0x000000,
blanchedalmond: 0xffebcd,
blue: 0x0000ff,
blueviolet: 0x8a2be2,
brown: 0xa52a2a,
burlywood: 0xdeb887,
cadetblue: 0x5f9ea0,
chartreuse: 0x7fff00,
chocolate: 0xd2691e,
coral: 0xff7f50,
cornflowerblue: 0x6495ed,
cornsilk: 0xfff8dc,
crimson: 0xdc143c,
cyan: 0x00ffff,
darkblue: 0x00008b,
darkcyan: 0x008b8b,
darkgoldenrod: 0xb8860b,
darkgray: 0xa9a9a9,
darkgreen: 0x006400,
darkgrey: 0xa9a9a9,
darkkhaki: 0xbdb76b,
darkmagenta: 0x8b008b,
darkolivegreen: 0x556b2f,
darkorange: 0xff8c00,
darkorchid: 0x9932cc,
darkred: 0x8b0000,
darksalmon: 0xe9967a,
darkseagreen: 0x8fbc8f,
darkslateblue: 0x483d8b,
darkslategray: 0x2f4f4f,
darkslategrey: 0x2f4f4f,
darkturquoise: 0x00ced1,
darkviolet: 0x9400d3,
deeppink: 0xff1493,
deepskyblue: 0x00bfff,
dimgray: 0x696969,
dimgrey: 0x696969,
dodgerblue: 0x1e90ff,
firebrick: 0xb22222,
floralwhite: 0xfffaf0,
forestgreen: 0x228b22,
fuchsia: 0xff00ff,
gainsboro: 0xdcdcdc,
ghostwhite: 0xf8f8ff,
gold: 0xffd700,
goldenrod: 0xdaa520,
gray: 0x808080,
green: 0x008000,
greenyellow: 0xadff2f,
grey: 0x808080,
honeydew: 0xf0fff0,
hotpink: 0xff69b4,
indianred: 0xcd5c5c,
indigo: 0x4b0082,
ivory: 0xfffff0,
khaki: 0xf0e68c,
lavender: 0xe6e6fa,
lavenderblush: 0xfff0f5,
lawngreen: 0x7cfc00,
lemonchiffon: 0xfffacd,
lightblue: 0xadd8e6,
lightcoral: 0xf08080,
lightcyan: 0xe0ffff,
lightgoldenrodyellow: 0xfafad2,
lightgray: 0xd3d3d3,
lightgreen: 0x90ee90,
lightgrey: 0xd3d3d3,
lightpink: 0xffb6c1,
lightsalmon: 0xffa07a,
lightseagreen: 0x20b2aa,
lightskyblue: 0x87cefa,
lightslategray: 0x778899,
lightslategrey: 0x778899,
lightsteelblue: 0xb0c4de,
lightyellow: 0xffffe0,
lime: 0x00ff00,
limegreen: 0x32cd32,
linen: 0xfaf0e6,
magenta: 0xff00ff,
maroon: 0x800000,
mediumaquamarine: 0x66cdaa,
mediumblue: 0x0000cd,
mediumorchid: 0xba55d3,
mediumpurple: 0x9370db,
mediumseagreen: 0x3cb371,
mediumslateblue: 0x7b68ee,
mediumspringgreen: 0x00fa9a,
mediumturquoise: 0x48d1cc,
mediumvioletred: 0xc71585,
midnightblue: 0x191970,
mintcream: 0xf5fffa,
mistyrose: 0xffe4e1,
moccasin: 0xffe4b5,
navajowhite: 0xffdead,
navy: 0x000080,
oldlace: 0xfdf5e6,
olive: 0x808000,
olivedrab: 0x6b8e23,
orange: 0xffa500,
orangered: 0xff4500,
orchid: 0xda70d6,
palegoldenrod: 0xeee8aa,
palegreen: 0x98fb98,
paleturquoise: 0xafeeee,
palevioletred: 0xdb7093,
papayawhip: 0xffefd5,
peachpuff: 0xffdab9,
peru: 0xcd853f,
pink: 0xffc0cb,
plum: 0xdda0dd,
powderblue: 0xb0e0e6,
purple: 0x800080,
rebeccapurple: 0x663399,
red: 0xff0000,
rosybrown: 0xbc8f8f,
royalblue: 0x4169e1,
saddlebrown: 0x8b4513,
salmon: 0xfa8072,
sandybrown: 0xf4a460,
seagreen: 0x2e8b57,
seashell: 0xfff5ee,
sienna: 0xa0522d,
silver: 0xc0c0c0,
skyblue: 0x87ceeb,
slateblue: 0x6a5acd,
slategray: 0x708090,
slategrey: 0x708090,
snow: 0xfffafa,
springgreen: 0x00ff7f,
steelblue: 0x4682b4,
tan: 0xd2b48c,
teal: 0x008080,
thistle: 0xd8bfd8,
tomato: 0xff6347,
turquoise: 0x40e0d0,
violet: 0xee82ee,
wheat: 0xf5deb3,
white: 0xffffff,
whitesmoke: 0xf5f5f5,
yellow: 0xffff00,
yellowgreen: 0x9acd32
};
define(Color, color$1, {
displayable: function() {
return this.rgb().displayable();
},
toString: function() {
return this.rgb() + "";
}
});
function color$1(format) {
var m;
format = (format + "").trim().toLowerCase();
return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00
: (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000
: (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
: (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
: (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
: (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
: (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
: (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
: named.hasOwnProperty(format) ? rgbn(named[format])
: format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
: null;
}
function rgbn(n) {
return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
}
function rgba(r, g, b, a) {
if (a <= 0) r = g = b = NaN;
return new Rgb(r, g, b, a);
}
function rgbConvert(o) {
if (!(o instanceof Color)) o = color$1(o);
if (!o) return new Rgb;
o = o.rgb();
return new Rgb(o.r, o.g, o.b, o.opacity);
}
function rgb(r, g, b, opacity) {
return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
}
function Rgb(r, g, b, opacity) {
this.r = +r;
this.g = +g;
this.b = +b;
this.opacity = +opacity;
}
define(Rgb, rgb, extend$1(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
rgb: function() {
return this;
},
displayable: function() {
return (0 <= this.r && this.r <= 255)
&& (0 <= this.g && this.g <= 255)
&& (0 <= this.b && this.b <= 255)
&& (0 <= this.opacity && this.opacity <= 1);
},
toString: function() {
var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
return (a === 1 ? "rgb(" : "rgba(")
+ Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.b) || 0))
+ (a === 1 ? ")" : ", " + a + ")");
}
}));
function hsla(h, s, l, a) {
if (a <= 0) h = s = l = NaN;
else if (l <= 0 || l >= 1) h = s = NaN;
else if (s <= 0) h = NaN;
return new Hsl(h, s, l, a);
}
function hslConvert(o) {
if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
if (!(o instanceof Color)) o = color$1(o);
if (!o) return new Hsl;
if (o instanceof Hsl) return o;
o = o.rgb();
var r = o.r / 255,
g = o.g / 255,
b = o.b / 255,
min = Math.min(r, g, b),
max = Math.max(r, g, b),
h = NaN,
s = max - min,
l = (max + min) / 2;
if (s) {
if (r === max) h = (g - b) / s + (g < b) * 6;
else if (g === max) h = (b - r) / s + 2;
else h = (r - g) / s + 4;
s /= l < 0.5 ? max + min : 2 - max - min;
h *= 60;
} else {
s = l > 0 && l < 1 ? 0 : h;
}
return new Hsl(h, s, l, o.opacity);
}
function hsl(h, s, l, opacity) {
return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
}
function Hsl(h, s, l, opacity) {
this.h = +h;
this.s = +s;
this.l = +l;
this.opacity = +opacity;
}
define(Hsl, hsl, extend$1(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
rgb: function() {
var h = this.h % 360 + (this.h < 0) * 360,
s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
l = this.l,
m2 = l + (l < 0.5 ? l : 1 - l) * s,
m1 = 2 * l - m2;
return new Rgb(
hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
hsl2rgb(h, m1, m2),
hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
this.opacity
);
},
displayable: function() {
return (0 <= this.s && this.s <= 1 || isNaN(this.s))
&& (0 <= this.l && this.l <= 1)
&& (0 <= this.opacity && this.opacity <= 1);
}
}));
/* From FvD 13.37, CSS Color Module Level 3 */
function hsl2rgb(h, m1, m2) {
return (h < 60 ? m1 + (m2 - m1) * h / 60
: h < 180 ? m2
: h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
: m1) * 255;
}
var deg2rad = Math.PI / 180;
var rad2deg = 180 / Math.PI;
var Kn = 18;
var Xn = 0.950470;
var Yn = 1;
var Zn = 1.088830;
var t0$
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment