Socket
Socket
Sign inDemoInstall

vega-lite

Package Overview
Dependencies
Maintainers
2
Versions
470
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vega-lite - npm Package Compare versions

Comparing version 0.8.3 to 0.9.0

_config.yml

8

bower.json

@@ -25,8 +25,10 @@ {

"bower_components",
"test",
"tests"
"test"
],
"dependencies": {
"datalib": "^1.3.0"
"datalib": "^1.5.2"
},
"devDependencies": {
"vega": "^2.4.1"
}
}
{
"name": "vega-lite",
"author": "Jeffrey Heer, Dominik Moritz, Kanit \"Ham\" Wongsuphasawat",
"version": "0.8.3",
"version": "0.9.0",
"collaborators": [

@@ -11,3 +11,3 @@ "Kanit Wongsuphasawat <kanitw@gmail.com> (http://kanitw.yellowpigz.com)",

"description": "Vega-lite provides a higher-level grammar for visual analysis, comparable to ggplot or Tableau, that generates complete Vega specifications.",
"main": "src/vl.js",
"main": "vega-lite.js",
"directories": {

@@ -17,12 +17,14 @@ "test": "test"

"scripts": {
"build": "gulp build",
"cover": "gulp coverage",
"deploy": "npm run lint && npm run test && scripts/deploy.sh",
"feature": "gulp feature",
"lint": "gulp jshint",
"patch": "gulp patch",
"release": "gulp release",
"start": "gulp serve",
"test": "gulp test",
"watch": "gulp bundle watch-schema watch-test"
"build": "browserify src/vl.ts -p tsify -d -s vl | exorcist vega-lite.js.map > vega-lite.js",
"postbuild": "uglifyjs vega-lite.js -cm --source-map vega-lite.min.js.map > vega-lite.min.js && npm run schema",
"cover": "tsc && istanbul cover node_modules/.bin/_mocha -- --recursive",
"clean": "rm -f vega-lite.* vega-lite-schema.json & find src -name '*.js*' -type f -delete & find test -name '*.js*' -type f -delete",
"deploy": "npm run clean && npm run lint && npm run test && scripts/deploy.sh",
"lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts",
"start": "npm run watch:build & browser-sync start --server --files 'vega-lite.js' --index 'gallery.html'",
"schema": "tsc && node src/schema/schemagen.js > vega-lite-schema.json",
"test": "tsc && mocha --recursive --require source-map-support/register",
"watch:build": "watchify src/vl.ts -p tsify -v -d -s vl -o 'exorcist vega-lite.js.map > vega-lite.js'",
"watch:dev": "nodemon -e ts -x 'npm test && npm run lint'",
"watch:all": "nodemon -e ts -x 'npm test && npm run lint && npm run build'"
},

@@ -39,45 +41,31 @@ "repository": {

"devDependencies": {
"browser-sync": "^2.9.11",
"browserify": "^11.2.0",
"browserify-shim": "^3.8.10",
"browser-sync": "^2.10.0",
"browserify": "^12.0.1",
"browserify-shim": "^3.8.11",
"browserify-versionify": "^1.0.6",
"chai": "^3.4.0",
"commander": "^2.9.0",
"d3": "^3.5.6",
"deep-diff": "^0.3.3",
"gulp": "^3.9.0",
"gulp-bump": "^1.0.0",
"gulp-git": "^1.6.0",
"gulp-jshint": "^1.11.2",
"gulp-load-plugins": "^1.0.0",
"gulp-rename": "^1.2.2",
"gulp-run": "^1.6.11",
"gulp-sourcemaps": "^1.6.0",
"gulp-spawn-mocha": "^2.2.1",
"gulp-tag-version": "^1.3.0",
"gulp-uglify": "^1.4.2",
"gulp-util": "^3.0.7",
"jshint-stylish": "^2.0.1",
"lodash": "^3.10.1",
"mocha": "^2.3.3",
"require-dir": "^0.3.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "~3.4.0",
"z-schema": "^3.15.4",
"jstransform": "^11.0.3",
"through": "^2.3.8"
"chai": "^3.4.1",
"exorcist": "^0.4.0",
"mocha": "^2.3.4",
"nodemon": "^1.8.1",
"source-map-support": "^0.4.0",
"tsify": "^0.13.1",
"tslint": "^3.1.1",
"typescript": "^1.7.3",
"uglify-js": "^2.6.1",
"watchify": "^3.6.1",
"z-schema": "^3.16.0"
},
"dependencies": {
"colorbrewer": "0.0.2",
"d3-color": "^0.2.6",
"d3-format": "^0.3.3",
"d3-time-format": "0.1.3",
"datalib": "^1.4.6",
"yargs": "^3.29.0",
"vega": "^2.3.1"
"d3-color": "^0.3.1",
"d3-format": "^0.4.0",
"d3-time-format": "0.2.0",
"datalib": "^1.4.12",
"yargs": "^3.30.0",
"vega": "^2.4.1"
},
"browserify": {
"transform": [
"browserify-shim"
"browserify-shim",
"browserify-versionify"
]

@@ -84,0 +72,0 @@ },

@@ -7,16 +7,15 @@ # Vega-Lite

**Vega-Lite is still in alpha phase and we are working on improving the code and [documentation](docs/Documentation.md).
Note that our syntax might change slightly before we release 1.0.**
Vega-Lite provides a higher-level grammar for visual analysis, akin to ggplot or Tableau, that generates complete [Vega](https://vega.github.io/) specifications.
Vega-Lite provides a higher-level grammar for visual analysis, comparable to ggplot or Tableau, that generates complete [Vega](https://vega.github.io/) specifications.
Vega-Lite specifications consist of simple mappings of variables in a data set to visual encoding channels such as position (`x`,`y`), `size`, `color` and `shape`. These mappings are then translated into detailed visualization specifications in the form of Vega specification language. Vega-Lite produces default values for visualization components (e.g., scales, axes, and legends) in the output Vega specification using a rule-based approach, but users can explicit specify these properties to override default values.
Vega-Lite specifications consist of simple mappings of variables in a data set to visual encoding channels such as position (`x`,`y`), `size`, `color` and `shape`. These mappings are then translated into a full visualization specifications. These resulting visualizations can then be exported or further modified to customize the display.
__Try using Vega-Lite in the online [Vega Editor](http://vega.github.io/vega-editor/?mode=vega-lite)__.
If you are using Vega-Lite for your project(s), please let us know by emailing us at [Vega-Lite \[at\] cs.washington.edu](mailto:vega-lite@cs.washington.edu). Feedback is also welcome.
The complete schema for specifications as [JSON schema](http://json-schema.org/) is at [vega-lite-schema.json](https://vega.github.io/vega-lite/vega-lite-schema.json).
**Note: Vega-Lite is still in alpha phase and we are working on improving the code and [documentation](https://vega.github.io/vega-lite/docs/).
Our syntax might change slightly before we release 1.0.** See our wiki pages for [the development roadmap](https://github.com/vega/vega-lite/wiki/Roadmap) and [how you can contribute](https://github.com/vega/vega-lite/wiki/Contribute).
If you find a bug or have a feature request, please [create an issue](https://github.com/vega/vega-lite/issues/new).
__Try using Vega-lite in the online [Vega Editor](http://vega.github.io/vega-editor/?mode=vega-lite)__.
The complete schema for specifications as [JSON schema](http://json-schema.org/) is at [vega-lite-schema.json](https://vega.github.io/vega-lite/vega-lite-schema.json).
## Example specification

@@ -31,12 +30,12 @@

"data": {"url": "data/barley.json"},
"marktype": "point",
"mark": "point",
"encoding": {
"x": {"type": "Q","name": "yield","aggregate": "mean"},
"x": {"type": "quantitative", "field": "yield","aggregate": "mean"},
"y": {
"sort": {"name": "yield","aggregate": "mean","reverse": false},
"type": "O",
"name": "variety"
"sort": {"field": "yield", "aggregate": "mean", "reverse": false},
"type": "ordinal",
"field": "variety"
},
"row": {"type": "O","name": "site"},
"color": {"type": "O","name": "year"}
"row": {"type": "ordinal", "field": "site"},
"color": {"type": "ordinal", "field": "year"}
}

@@ -59,6 +58,6 @@ }

},
"marktype": "bar",
"mark": "bar",
"encoding": {
"x": {"type": "O","name": "a"},
"y": {"type": "Q","name": "b"}
"x": {"type": "ordinal", "field": "a"},
"y": {"type": "quantitative", "field": "b"}
}

@@ -79,13 +78,28 @@ }

Since Vega-Lite is written in Typescript, you should also install TypeScript
```sh
npm install -g typescript
```
We use the [atom](atom.io) editor with typescript support. If you don't want to see intermediate files (`.js`, `.js.map`), you can "Hide VCS Ignored Files" in the `tree-view` plugin.
### Commands
You can run `npm run build` to compile vega-lite or run `npm start` to open the live vega-lite editor.
You can run `npm run build` to compile Vega-Lite and regenerate `vega-lite-schema.json`. More commands are available in `npm run`.
You can `npm run watch` to start a watcher task that regenerate the `vega-lite-schema.json` file whenever `schema.js` changes, and lints and tests all JS files when any `.js` file in `test/` or `src/` changes.
#### Watch tasks
Note: These commands use [Gulp](http://gulpjs.com) internally; to run them directly (instead of through the `npm run` aliases), install gulp globally with
```sh
npm install -g gulp
```
During development, it can be convenient to rebuild automatically or run tests in the background.
You can `npm run watch:dev` to start a watcher task that **lints and runs tests** when any `.ts` file changes.
You can use `npm run watch:build` to start a watcher task that **re-compiles Vega-Lite** when `.ts` files related to VL change.
The previous two commands run very fast but don't run all tasks that you may want. If you are okay to use a slow command, you can use `npm run watch:all` to start a watcher task that when any `.ts` file changes:
- lints and runs tests
- re-compiles Vega-Lite
- regenerates `vega-lite-schema.json`
### Developing Vega-Lite and Datalib

@@ -97,3 +111,3 @@

```
```sh
# first link datalib global npm

@@ -100,0 +114,0 @@ cd path/to/datalib

@@ -1,259 +0,189 @@

'use strict';
require('../globals');
var util = require('../util'),
setter = util.setter,
getter = util.getter,
time = require('./time');
var axis = module.exports = {};
axis.def = function(name, encoding, layout, stats, opt) {
var isCol = name == COL,
isRow = name == ROW,
type = isCol ? 'x' : isRow ? 'y' : name;
// TODO: rename def to axisDef and avoid side effects where possible.
var def = {
type: type,
scale: name,
properties: {},
layer: encoding.encDef(name).axis.layer
};
var orient = axis.orient(encoding, name, stats);
if (orient) {
def.orient = orient;
}
// Add axis label custom scale (for bin / time)
def = axis.labels.scale(def, encoding, name);
def = axis.labels.format(def, encoding, name, stats);
def = axis.labels.angle(def, encoding, name);
// for x-axis, set ticks for Q or rotate scale for ordinal scale
if (name == X) {
if ((encoding.isDimension(X) || encoding.isType(X, T)) &&
!('angle' in getter(def, ['properties', 'labels']))) {
// TODO(kanitw): Jul 19, 2015 - #506 add condition for rotation
def = axis.labels.rotate(def);
} else { // Q
def.ticks = encoding.encDef(name).axis.ticks;
var util_1 = require('../util');
var type_1 = require('../type');
var channel_1 = require('../channel');
var time = require('./time');
function compileAxis(channel, model) {
var isCol = channel === channel_1.COLUMN, isRow = channel === channel_1.ROW, type = isCol ? 'x' : isRow ? 'y' : channel;
var def = {
type: type,
scale: channel
};
[
'format', 'grid', 'layer', 'orient', 'tickSize', 'ticks', 'title',
'offset', 'tickPadding', 'tickSize', 'tickSizeMajor', 'tickSizeMinor', 'tickSizeEnd',
'titleOffset', 'values', 'subdivide'
].forEach(function (property) {
var method;
var value = (method = exports[property]) ?
method(model, channel, def) :
model.fieldDef(channel).axis[property];
if (value !== undefined) {
def[property] = value;
}
});
var props = model.fieldDef(channel).axis.properties || {};
[
'axis', 'labels',
'grid', 'title', 'ticks', 'majorTicks', 'minorTicks'
].forEach(function (group) {
var value = properties[group] ?
properties[group](model, channel, props[group], def) :
props[group];
if (value !== undefined) {
def.properties = def.properties || {};
def.properties[group] = value;
}
});
return def;
}
exports.compileAxis = compileAxis;
function format(model, channel) {
var fieldDef = model.fieldDef(channel);
var format = fieldDef.axis.format;
if (format !== undefined) {
return format;
}
}
// TitleOffset depends on labels rotation
def.titleOffset = axis.titleOffset(encoding, layout, name);
//def.offset is used in axis.grid
if(isRow) def.offset = axis.titleOffset(encoding, layout, Y) + 20;
// FIXME(kanitw): Jul 19, 2015 - offset for column when x is put on top
def = axis.grid(def, encoding, name, layout);
def = axis.title(def, encoding, name, layout, opt);
if (isRow || isCol) {
def = axis.hideTicks(def);
}
return def;
};
axis.orient = function(encoding, name, stats) {
var orient = encoding.encDef(name).axis.orient;
if (orient) {
return orient;
} else if (name === COL) {
return 'top';
} else if (name === X && encoding.has(Y) && encoding.isOrdinalScale(Y) && encoding.cardinality(Y, stats) > 30) {
// x-axis for long y - put on top
return 'top';
}
return undefined;
};
axis.grid = function(def, encoding, name, layout) {
var cellPadding = layout.cellPadding,
isCol = name == COL,
isRow = name == ROW;
var _grid = encoding.axis(name).grid;
// If `grid` is unspecified, the default value is `true` for ROW and COL.
// For X and Y, the default value is `true` for (1) quantitative fields that are not binned and (2) time fields.
// Otherwise, the default value is `false`.
var grid = _grid === undefined ?
( name === ROW || name === COL ||
(encoding.isTypes(name, [Q, T]) && !encoding.encDef(name).bin)
) : _grid;
if (grid) {
def.grid = true;
if (isCol) {
// set grid property -- put the lines on the right the cell
var yOffset = encoding.config('cellGridOffset');
// TODO(#677): this should depend on orient
def.properties.grid = {
x: {
offset: layout.cellWidth * (1+ cellPadding/2.0),
// default value(s) -- vega doesn't do recursive merge
scale: 'col',
field: 'data'
},
y: {
value: -yOffset,
},
y2: {
field: {group: 'mark.group.height'},
offset: yOffset
},
stroke: { value: encoding.config('cellGridColor') },
strokeOpacity: { value: encoding.config('cellGridOpacity') }
};
} else if (isRow) {
var xOffset = encoding.config('cellGridOffset');
// TODO(#677): this should depend on orient
// set grid property -- put the lines on the top
def.properties.grid = {
y: {
offset: -layout.cellHeight * (cellPadding/2),
// default value(s) -- vega doesn't do recursive merge
scale: 'row',
field: 'data'
},
x: {
value: def.offset - xOffset
},
x2: {
field: {group: 'mark.group.width'},
offset: def.offset + xOffset,
// default value(s) -- vega doesn't do recursive merge
mult: 1
},
stroke: { value: encoding.config('cellGridColor') },
strokeOpacity: { value: encoding.config('cellGridOpacity') }
};
} else {
def.properties.grid = {
stroke: { value: encoding.config('gridColor') },
strokeOpacity: { value: encoding.config('gridOpacity') }
};
if (fieldDef.type === type_1.QUANTITATIVE) {
return model.numberFormat(channel);
}
}
return def;
};
axis.hideTicks = function(def) {
def.properties.ticks = {opacity: {value: 0}};
def.properties.majorTicks = {opacity: {value: 0}};
def.properties.axis = {opacity: {value: 0}};
return def;
};
axis.title = function (def, encoding, name, layout) {
var ax = encoding.encDef(name).axis;
if (ax.title) {
def.title = ax.title;
} else {
// if not defined, automatically determine axis title from field def
var fieldTitle = encoding.fieldTitle(name),
maxLength;
if (ax.titleMaxLength) {
maxLength = ax.titleMaxLength;
} else if (name===X) {
maxLength = layout.cellWidth / encoding.config('characterWidth');
} else if (name === Y) {
maxLength = layout.cellHeight / encoding.config('characterWidth');
else if (fieldDef.type === type_1.TEMPORAL) {
var timeUnit = fieldDef.timeUnit;
if (!timeUnit) {
return model.config('timeFormat');
}
else if (timeUnit === 'year') {
return 'd';
}
}
def.title = maxLength ? util.truncate(fieldTitle, maxLength) : fieldTitle;
}
if (name === ROW) {
def.properties.title = {
angle: {value: 0},
align: {value: 'right'},
baseline: {value: 'middle'},
dy: {value: (-layout.height/2) -20}
};
}
return def;
};
axis.labels = {};
/** add custom label for time type and bin */
axis.labels.scale = function(def, encoding, name) {
// time
var timeUnit = encoding.encDef(name).timeUnit;
if (encoding.isType(name, T) && timeUnit && (time.hasScale(timeUnit))) {
setter(def, ['properties','labels','text','scale'], 'time-'+ timeUnit);
}
// FIXME bin
return def;
};
/**
* Determine number format or truncate if maxLabel length is presented.
*/
axis.labels.format = function (def, encoding, name, stats) {
var fieldStats = stats[encoding.encDef(name).name];
if (encoding.axis(name).format) {
def.format = encoding.axis(name).format;
} else if (encoding.isType(name, Q) || fieldStats.type === 'number') {
def.format = encoding.numberFormat(fieldStats);
} else if (encoding.isType(name, T)) {
var timeUnit = encoding.encDef(name).timeUnit;
if (!timeUnit) {
def.format = encoding.config('timeFormat');
} else if (timeUnit === 'year') {
def.format = 'd';
return undefined;
}
exports.format = format;
function grid(model, channel) {
var fieldDef = model.fieldDef(channel);
var grid = fieldDef.axis.grid;
if (grid !== undefined) {
return grid;
}
} else if (encoding.isTypes(name, [N, O]) && encoding.axis(name).maxLabelLength) {
setter(def,
['properties','labels','text','template'],
'{{ datum.data | truncate:' +
encoding.axis(name).maxLabelLength + '}}'
);
}
return def;
};
axis.labels.angle = function(def, encoding, name) {
var angle = encoding.axis(name).labelAngle;
if (typeof angle === 'undefined') return def;
setter(def, ['properties', 'labels', 'angle', 'value'], angle);
return def;
};
axis.labels.rotate = function(def) {
var align = def.orient ==='top' ? 'left' : 'right';
setter(def, ['properties','labels', 'angle', 'value'], 270);
setter(def, ['properties','labels', 'align', 'value'], align);
setter(def, ['properties','labels', 'baseline', 'value'], 'middle');
return def;
};
axis.titleOffset = function (encoding, layout, name) {
// return specified value if specified
var value = encoding.axis(name).titleOffset;
if (value) return value;
switch (name) {
//FIXME make this adjustable
case ROW: return 0;
case COL: return 35;
}
return getter(layout, [name, 'axisTitleOffset']);
};
return !model.isOrdinalScale(channel) && !fieldDef.bin;
}
exports.grid = grid;
function layer(model, channel, def) {
var layer = model.fieldDef(channel).axis.layer;
if (layer !== undefined) {
return layer;
}
if (def.grid) {
return 'back';
}
return undefined;
}
exports.layer = layer;
;
function orient(model, channel) {
var orient = model.fieldDef(channel).axis.orient;
if (orient) {
return orient;
}
else if (channel === channel_1.COLUMN) {
return 'top';
}
else if (channel === channel_1.ROW) {
if (model.has(channel_1.Y) && model.fieldDef(channel_1.Y).axis.orient !== 'right') {
return 'right';
}
}
return undefined;
}
exports.orient = orient;
function ticks(model, channel) {
var ticks = model.fieldDef(channel).axis.ticks;
if (ticks !== undefined) {
return ticks;
}
if (channel === channel_1.X && !model.fieldDef(channel).bin) {
return 5;
}
return undefined;
}
exports.ticks = ticks;
function tickSize(model, channel) {
var tickSize = model.fieldDef(channel).axis.tickSize;
if (tickSize !== undefined) {
return tickSize;
}
if (channel === channel_1.ROW || channel === channel_1.COLUMN) {
return 0;
}
return undefined;
}
exports.tickSize = tickSize;
function title(model, channel) {
var axisSpec = model.fieldDef(channel).axis;
if (axisSpec.title !== undefined) {
return axisSpec.title;
}
var fieldTitle = model.fieldTitle(channel);
var layout = model.layout();
var maxLength;
if (axisSpec.titleMaxLength) {
maxLength = axisSpec.titleMaxLength;
}
else if (channel === channel_1.X && typeof layout.cellWidth === 'number') {
maxLength = layout.cellWidth / model.config('characterWidth');
}
else if (channel === channel_1.Y && typeof layout.cellHeight === 'number') {
maxLength = layout.cellHeight / model.config('characterWidth');
}
return maxLength ? util_1.truncate(fieldTitle, maxLength) : fieldTitle;
}
exports.title = title;
var properties;
(function (properties) {
function axis(model, channel, spec) {
if (channel === channel_1.ROW || channel === channel_1.COLUMN) {
return util_1.extend({
opacity: { value: 0 }
}, spec || {});
}
return spec || undefined;
}
properties.axis = axis;
function labels(model, channel, spec, def) {
var fieldDef = model.fieldDef(channel);
var filterName = time.labelTemplate(fieldDef.timeUnit, fieldDef.axis.shortTimeNames);
if (fieldDef.type === type_1.TEMPORAL && filterName) {
spec = util_1.extend({
text: { template: '{{datum.data | ' + filterName + '}}' }
}, spec || {});
}
if (util_1.contains([type_1.NOMINAL, type_1.ORDINAL], fieldDef.type) && fieldDef.axis.labelMaxLength) {
spec = util_1.extend({
text: {
template: '{{ datum.data | truncate:' + fieldDef.axis.labelMaxLength + '}}'
}
}, spec || {});
}
switch (channel) {
case channel_1.X:
if (model.isDimension(channel_1.X) || fieldDef.type === type_1.TEMPORAL) {
spec = util_1.extend({
angle: { value: 270 },
align: { value: def.orient === 'top' ? 'left' : 'right' },
baseline: { value: 'middle' }
}, spec || {});
}
break;
case channel_1.ROW:
if (def.orient === 'right') {
spec = util_1.extend({
angle: { value: 90 },
align: { value: 'center' },
baseline: { value: 'bottom' }
}, spec || {});
}
}
return spec || undefined;
}
properties.labels = labels;
})(properties || (properties = {}));
//# sourceMappingURL=axis.js.map

@@ -1,131 +0,76 @@

'use strict';
var summary = module.exports = require('datalib/src/stats').summary;
require('../globals');
/**
* Module for compiling Vega-lite spec into Vega spec.
*/
var compiler = module.exports = {};
var Encoding = require('../Encoding'),
axis = compiler.axis = require('./axis'),
legend = compiler.legend = require('./legend'),
marks = compiler.marks = require('./marks'),
scale = compiler.scale = require('./scale');
compiler.data = require('./data');
compiler.facet = require('./facet');
compiler.layout = require('./layout');
compiler.stack = require('./stack');
compiler.style = require('./style');
compiler.subfacet = require('./subfacet');
compiler.time = require('./time');
compiler.compile = function (spec, stats, theme) {
return compiler.compileEncoding(Encoding.fromSpec(spec, theme), stats);
};
compiler.shorthand = function (shorthand, stats, config, theme) {
return compiler.compileEncoding(Encoding.fromShorthand(shorthand, config, theme), stats);
};
/**
* Create a Vega specification from a Vega-lite Encoding object.
*/
compiler.compileEncoding = function (encoding, stats) {
// no need to pass stats if you pass in the data
if (!stats) {
if (encoding.hasValues()) {
stats = summary(encoding.data().values).reduce(function(s, p) {
s[p.field] = p;
return s;
}, {});
} else {
console.error('No stats provided and data is not embedded.');
}
}
var layout = compiler.layout(encoding, stats);
var spec = {
width: layout.width,
height: layout.height,
padding: 'auto',
data: compiler.data(encoding),
// global scales contains only time unit scales
scales: compiler.time.scales(encoding),
marks: [{
name: 'cell',
var Model_1 = require('./Model');
var axis_1 = require('./axis');
var data_1 = require('./data');
var facet_1 = require('./facet');
var legend_1 = require('./legend');
var marks_1 = require('./marks');
var scale_1 = require('./scale');
var util_1 = require('../util');
var data_2 = require('../data');
var channel_1 = require('../channel');
var Model_2 = require('./Model');
exports.Model = Model_2.Model;
function compile(spec, theme) {
var model = new Model_1.Model(spec, theme);
var layout = model.layout();
var rootGroup = util_1.extend({
name: spec.name ? spec.name + '_root' : 'root',
type: 'group',
}, spec.description ? { description: spec.description } : {}, {
from: { data: data_2.LAYOUT },
properties: {
enter: {
width: layout.cellWidth ?
{value: layout.cellWidth} :
{field: {group: 'width'}},
height: layout.cellHeight ?
{value: layout.cellHeight} :
{field: {group: 'height'}}
}
update: {
width: layout.width.field ?
{ field: layout.width.field } :
{ value: layout.width },
height: layout.height.field ?
{ field: layout.height.field } :
{ value: layout.height }
}
}
}]
};
var group = spec.marks[0];
// marks
var style = compiler.style(encoding, stats),
mdefs = group.marks = marks.def(encoding, layout, style, stats),
mdef = mdefs[mdefs.length - 1]; // TODO: remove this dirty hack by refactoring the whole flow
var stack = encoding.stack();
if (stack) {
// modify mdef.{from,properties}
compiler.stack(encoding, mdef, stack);
}
var lineType = marks[encoding.marktype()].line;
// handle subfacets
var details = encoding.details();
if (details.length > 0 && lineType) {
//subfacet to group area / line together in one group
compiler.subfacet(group, mdef, details);
}
// auto-sort line/area values
if (lineType && encoding.config('autoSortLine')) {
var f = (encoding.isMeasure(X) && encoding.isDimension(Y)) ? Y : X;
if (!mdef.from) {
mdef.from = {};
});
var marks = marks_1.compileMarks(model);
if (model.has(channel_1.ROW) || model.has(channel_1.COLUMN)) {
util_1.extend(rootGroup, facet_1.facetMixins(model, marks));
}
// TODO: why - ?
mdef.from.transform = [{type: 'sort', by: '-' + encoding.fieldRef(f)}];
}
// get a flattened list of all scale names that are used in the vl spec
var singleScaleNames = [].concat.apply([], mdefs.map(function(markProps) {
return scale.names(markProps.properties.update);
}));
// Small Multiples
if (encoding.has(ROW) || encoding.has(COL)) {
spec = compiler.facet(group, encoding, layout, spec, singleScaleNames, stats);
spec.legends = legend.defs(encoding, style);
} else {
group.scales = scale.defs(singleScaleNames, encoding, layout, stats);
group.axes = [];
if (encoding.has(X)) {
group.axes.push(axis.def(X, encoding, layout, stats));
else {
rootGroup.marks = marks.map(function (marks) {
marks.from = marks.from || {};
marks.from.data = model.dataTable();
return marks;
});
var scaleNames = model.map(function (_, channel) {
return channel;
});
rootGroup.scales = scale_1.compileScales(scaleNames, model);
var axes = (model.has(channel_1.X) ? [axis_1.compileAxis(channel_1.X, model)] : [])
.concat(model.has(channel_1.Y) ? [axis_1.compileAxis(channel_1.Y, model)] : []);
if (axes.length > 0) {
rootGroup.axes = axes;
}
}
if (encoding.has(Y)) {
group.axes.push(axis.def(Y, encoding, layout, stats));
var legends = legend_1.compileLegends(model);
if (legends.length > 0) {
rootGroup.legends = legends;
}
group.legends = legend.defs(encoding, style);
}
return spec;
};
var FIT = 1;
var output = util_1.extend(spec.name ? { name: spec.name } : {}, {
width: layout.width.field ? FIT : layout.width,
height: layout.height.field ? FIT : layout.height,
padding: 'auto'
}, ['viewport', 'background', 'scene'].reduce(function (topLevelConfig, property) {
var value = model.config(property);
if (value !== undefined) {
topLevelConfig[property] = value;
}
return topLevelConfig;
}, {}), {
data: data_1.compileData(model),
marks: [rootGroup]
});
return {
spec: output
};
}
exports.compile = compile;
//# sourceMappingURL=compiler.js.map

@@ -1,279 +0,329 @@

'use strict';
require('../globals');
module.exports = data;
var vlEncDef = require('../encdef'),
util = require('../util'),
time = require('./time');
/**
* Create Vega's data array from a given encoding.
*
* @param {Encoding} encoding
* @return {Array} Array of Vega data.
* This always includes a "raw" data table.
* If the encoding contains aggregate value, this will also create
* aggregate table as well.
*/
function data(encoding) {
var def = [data.raw(encoding)];
var aggregate = data.aggregate(encoding);
if (aggregate) {
def.push(data.aggregate(encoding));
}
// TODO add "having" filter here
// append non-positive filter at the end for the data table
data.filterNonPositive(def[def.length - 1], encoding);
// Stack
var stack = encoding.stack();
if (stack) {
def.push(data.stack(encoding, stack));
}
return def;
var vlFieldDef = require('../fielddef');
var util = require('../util');
var bin_1 = require('../bin');
var channel_1 = require('../channel');
var data_1 = require('../data');
var time = require('./time');
var type_1 = require('../type');
function compileData(model) {
var def = [source.def(model)];
var summaryDef = summary.def(model);
if (summaryDef) {
def.push(summaryDef);
}
filterNonPositiveForLog(def[def.length - 1], model);
var statsDef = layout.def(model);
if (statsDef) {
def.push(statsDef);
}
var stackDef = model.stack();
if (stackDef) {
def.push(stack.def(model, stackDef));
}
return def;
}
data.raw = function(encoding) {
var raw = {name: RAW};
// Data source (url or inline)
if (encoding.hasValues()) {
raw.values = encoding.data().values;
raw.format = {type: 'json'};
} else {
raw.url = encoding.data().url;
raw.format = {type: encoding.data().formatType};
}
// Set data's format.parse if needed
var parse = data.raw.formatParse(encoding);
if (parse) {
raw.format.parse = parse;
}
raw.transform = data.raw.transform(encoding);
return raw;
};
data.raw.formatParse = function(encoding) {
var parse;
encoding.forEach(function(encDef) {
if (encDef.type == T) {
parse = parse || {};
parse[encDef.name] = 'date';
} else if (encDef.type == Q) {
if (vlEncDef.isCount(encDef)) return;
parse = parse || {};
parse[encDef.name] = 'number';
exports.compileData = compileData;
var source;
(function (source_1) {
function def(model) {
var source = { name: data_1.SOURCE };
if (model.hasValues()) {
source.values = model.data().values;
source.format = { type: 'json' };
}
else {
source.url = model.data().url;
source.format = { type: model.data().formatType };
}
var parse = formatParse(model);
if (parse) {
source.format.parse = parse;
}
source.transform = transform(model);
return source;
}
});
return parse;
};
/**
* Generate Vega transforms for the raw data table. This can include
* transforms for time unit, binning and filtering.
*/
data.raw.transform = function(encoding) {
// null filter comes first so transforms are not performed on null values
// time and bin should come before filter so we can filter by time and bin
return data.raw.transform.nullFilter(encoding).concat(
data.raw.transform.formula(encoding),
data.raw.transform.time(encoding),
data.raw.transform.bin(encoding),
data.raw.transform.filter(encoding)
);
};
data.raw.transform.time = function(encoding) {
return encoding.reduce(function(transform, encDef, encType) {
if (encDef.type === T && encDef.timeUnit) {
var fieldRef = encoding.fieldRef(encType, {nofn: true, datum: true});
transform.push({
type: 'formula',
field: encoding.fieldRef(encType),
expr: time.formula(encDef.timeUnit, fieldRef)
});
source_1.def = def;
function formatParse(model) {
var parse;
model.forEach(function (fieldDef) {
if (fieldDef.type === type_1.TEMPORAL) {
parse = parse || {};
parse[fieldDef.field] = 'date';
}
else if (fieldDef.type === type_1.QUANTITATIVE) {
if (vlFieldDef.isCount(fieldDef))
return;
parse = parse || {};
parse[fieldDef.field] = 'number';
}
});
return parse;
}
return transform;
}, []);
};
data.raw.transform.bin = function(encoding) {
return encoding.reduce(function(transform, encDef, encType) {
if (encoding.bin(encType)) {
transform.push({
type: 'bin',
field: encDef.name,
output: {
start: encoding.fieldRef(encType, {bin_suffix: '_start'}),
end: encoding.fieldRef(encType, {bin_suffix: '_end'})
},
maxbins: encoding.bin(encType).maxbins
});
// temporary fix for adding missing `bin_mid` from the bin transform
transform.push({
type: 'formula',
field: encoding.fieldRef(encType, {bin_suffix: '_mid'}),
expr: '(' + encoding.fieldRef(encType, {datum:1, bin_suffix: '_start'}) + '+' + encoding.fieldRef(encType, {datum:1, bin_suffix: '_end'}) + ')/2'
});
function transform(model) {
return nullFilterTransform(model).concat(formulaTransform(model), timeTransform(model), binTransform(model), filterTransform(model));
}
return transform;
}, []);
};
/**
* @return {Array} An array that might contain a filter transform for filtering null value based on filterNul config
*/
data.raw.transform.nullFilter = function(encoding) {
var filteredFields = util.reduce(encoding.fields(),
function(filteredFields, fieldList, fieldName) {
if (fieldName === '*') return filteredFields; //count
// TODO(#597) revise how filterNull is structured.
if ((encoding.config('filterNull').Q && fieldList.containsType[Q]) ||
(encoding.config('filterNull').T && fieldList.containsType[T]) ||
(encoding.config('filterNull').O && fieldList.containsType[O]) ||
(encoding.config('filterNull').N && fieldList.containsType[N])) {
filteredFields.push(fieldName);
}
return filteredFields;
}, []);
return filteredFields.length > 0 ?
[{
type: 'filter',
test: filteredFields.map(function(fieldName) {
return 'datum.' + fieldName + '!==null';
}).join(' && ')
}] : [];
};
data.raw.transform.filter = function(encoding) {
var filter = encoding.data().filter;
return filter ? [{
type: 'filter',
test: filter
}] : [];
};
data.raw.transform.formula = function(encoding) {
var formulas = encoding.data().formulas;
if (formulas === undefined) {
return [];
}
return formulas.reduce(function(transform, formula) {
formula.type = 'formula';
transform.push(formula);
return transform;
}, []);
};
data.aggregate = function(encoding) {
/* dict set for dimensions */
var dims = {};
/* dictionary mapping field name => dict set of aggregation functions */
var meas = {};
var hasAggregate = false;
encoding.forEach(function(encDef, encType) {
if (encDef.aggregate) {
hasAggregate = true;
if (encDef.aggregate === 'count') {
meas['*'] = meas['*'] || {};
meas['*'].count = true;
} else {
meas[encDef.name] = meas[encDef.name] || {};
meas[encDef.name][encDef.aggregate] = true;
}
} else {
if (encDef.bin) {
// TODO(#694) only add dimension for the required ones.
dims[encoding.fieldRef(encType, {bin_suffix: '_start'})] = encoding.fieldRef(encType, {bin_suffix: '_start'});
dims[encoding.fieldRef(encType, {bin_suffix: '_mid'})] = encoding.fieldRef(encType, {bin_suffix: '_mid'});
dims[encoding.fieldRef(encType, {bin_suffix: '_end'})] = encoding.fieldRef(encType, {bin_suffix: '_end'});
} else {
dims[encDef.name] = encoding.fieldRef(encType);
}
source_1.transform = transform;
function timeTransform(model) {
return model.reduce(function (transform, fieldDef, channel) {
if (fieldDef.type === type_1.TEMPORAL && fieldDef.timeUnit) {
var field = model.field(channel, { nofn: true, datum: true });
transform.push({
type: 'formula',
field: model.field(channel),
expr: time.formula(fieldDef.timeUnit, field)
});
}
return transform;
}, []);
}
});
var groupby = util.vals(dims);
// short-format summarize object for Vega's aggregate transform
// https://github.com/vega/vega/wiki/Data-Transforms#-aggregate
var summarize = util.reduce(meas, function(summarize, fnDictSet, field) {
summarize[field] = util.keys(fnDictSet);
return summarize;
}, {});
if (hasAggregate) {
return {
name: AGGREGATE,
source: RAW,
transform: [{
type: 'aggregate',
groupby: groupby,
summarize: summarize
}]
};
}
return null;
};
/**
* Add stacked data source, for feeding the shared scale.
*/
data.stack = function(encoding, stack) {
var dim = stack.groupby;
var val = stack.value;
var facets = encoding.facets();
var stacked = {
name: STACKED,
source: encoding.dataTable(),
transform: [{
type: 'aggregate',
groupby: [encoding.fieldRef(dim)].concat(facets), // dim and other facets
summarize: [{ops: ['sum'], field: encoding.fieldRef(val)}]
}]
};
if (facets && facets.length > 0) {
stacked.transform.push({ //calculate max for each facet
type: 'aggregate',
groupby: facets,
summarize: [{
ops: ['max'],
// we want max of sum from above transform
field: encoding.fieldRef(val, {prefn: 'sum_'})
}]
source_1.timeTransform = timeTransform;
function binTransform(model) {
return model.reduce(function (transform, fieldDef, channel) {
var bin = model.bin(channel);
if (bin) {
transform.push({
type: 'bin',
field: fieldDef.field,
output: {
start: model.field(channel, { binSuffix: '_start' }),
mid: model.field(channel, { binSuffix: '_mid' }),
end: model.field(channel, { binSuffix: '_end' })
},
maxbins: typeof bin === 'boolean' ? bin_1.MAXBINS_DEFAULT : bin.maxbins
});
}
return transform;
}, []);
}
source_1.binTransform = binTransform;
function nullFilterTransform(model) {
var filterNull = model.config('filterNull');
var filteredFields = util.keys(model.reduce(function (filteredFields, fieldDef) {
if (fieldDef.field && fieldDef.field !== '*' && filterNull[fieldDef.type]) {
filteredFields[fieldDef.field] = true;
}
return filteredFields;
}, {}));
return filteredFields.length > 0 ?
[{
type: 'filter',
test: filteredFields.map(function (fieldName) {
return 'datum.' + fieldName + '!==null';
}).join(' && ')
}] : [];
}
source_1.nullFilterTransform = nullFilterTransform;
function filterTransform(model) {
var filter = model.data().filter;
return filter ? [{
type: 'filter',
test: filter
}] : [];
}
source_1.filterTransform = filterTransform;
function formulaTransform(model) {
var calculate = model.data().calculate;
if (calculate === undefined) {
return [];
}
return calculate.reduce(function (transform, formula) {
transform.push(util.extend({ type: 'formula' }, formula));
return transform;
}, []);
}
source_1.formulaTransform = formulaTransform;
})(source = exports.source || (exports.source = {}));
var layout;
(function (layout_1) {
function def(model) {
var summarize = [];
var formulas = [];
if (model.has(channel_1.X) && model.isOrdinalScale(channel_1.X)) {
var xScale = model.fieldDef(channel_1.X).scale;
var xHasDomain = xScale.domain instanceof Array;
if (!xHasDomain) {
summarize.push({
field: model.field(channel_1.X),
ops: ['distinct']
});
}
var xCardinality = xHasDomain ? xScale.domain.length :
model.field(channel_1.X, { datum: true, prefn: 'distinct_' });
formulas.push({
type: 'formula',
field: 'cellWidth',
expr: '(' + xCardinality + ' + ' + xScale.padding + ') * ' + xScale.bandWidth
});
}
if (model.has(channel_1.Y) && model.isOrdinalScale(channel_1.Y)) {
var yScale = model.fieldDef(channel_1.Y).scale;
var yHasDomain = yScale.domain instanceof Array;
if (!yHasDomain) {
summarize.push({
field: model.field(channel_1.Y),
ops: ['distinct']
});
}
var yCardinality = yHasDomain ? yScale.domain.length :
model.field(channel_1.Y, { datum: true, prefn: 'distinct_' });
formulas.push({
type: 'formula',
field: 'cellHeight',
expr: '(' + yCardinality + ' + ' + yScale.padding + ') * ' + yScale.bandWidth
});
}
var cellPadding = model.config('cell').padding;
var layout = model.layout();
if (model.has(channel_1.COLUMN)) {
var cellWidth = layout.cellWidth.field ?
'datum.' + layout.cellWidth.field :
layout.cellWidth;
var colScale = model.fieldDef(channel_1.COLUMN).scale;
var colHasDomain = colScale.domain instanceof Array;
if (!colHasDomain) {
summarize.push({
field: model.fieldDef(channel_1.COLUMN).field,
ops: ['distinct']
});
}
var colCardinality = colHasDomain ? colScale.domain.length :
model.field(channel_1.COLUMN, { datum: true, prefn: 'distinct_' });
formulas.push({
type: 'formula',
field: 'width',
expr: cellWidth + ' * ' + colCardinality + ' + ' +
'(' + colCardinality + ' - 1) * ' + cellPadding
});
}
if (model.has(channel_1.ROW)) {
var cellHeight = layout.cellHeight.field ?
'datum.' + layout.cellHeight.field :
layout.cellHeight;
var rowScale = model.fieldDef(channel_1.ROW).scale;
var rowHasDomain = rowScale.domain instanceof Array;
if (!rowHasDomain) {
summarize.push({
field: model.fieldDef(channel_1.ROW).field,
ops: ['distinct']
});
}
var rowCardinality = rowHasDomain ? rowScale.domain.length :
model.field(channel_1.ROW, { datum: true, prefn: 'distinct_' });
formulas.push({
type: 'formula',
field: 'height',
expr: cellHeight + ' * ' + rowCardinality + ' + ' +
'(' + rowCardinality + ' - 1) * ' + cellPadding
});
}
if (formulas.length > 0) {
return summarize.length > 0 ? {
name: data_1.LAYOUT,
source: model.dataTable(),
transform: [{
type: 'aggregate',
summarize: summarize
}].concat(formulas)
} : {
name: data_1.LAYOUT,
values: [{}],
transform: formulas
};
}
return null;
}
layout_1.def = def;
})(layout = exports.layout || (exports.layout = {}));
var summary;
(function (summary) {
function def(model) {
var dims = {};
var meas = {};
var hasAggregate = false;
model.forEach(function (fieldDef, channel) {
if (fieldDef.aggregate) {
hasAggregate = true;
if (fieldDef.aggregate === 'count') {
meas['*'] = meas['*'] || {};
meas['*'].count = true;
}
else {
meas[fieldDef.field] = meas[fieldDef.field] || {};
meas[fieldDef.field][fieldDef.aggregate] = true;
}
}
else {
if (fieldDef.bin) {
dims[model.field(channel, { binSuffix: '_start' })] = model.field(channel, { binSuffix: '_start' });
dims[model.field(channel, { binSuffix: '_mid' })] = model.field(channel, { binSuffix: '_mid' });
dims[model.field(channel, { binSuffix: '_end' })] = model.field(channel, { binSuffix: '_end' });
}
else {
dims[fieldDef.field] = model.field(channel);
}
}
});
var groupby = util.vals(dims);
var summarize = util.reduce(meas, function (summarize, fnDictSet, field) {
summarize[field] = util.keys(fnDictSet);
return summarize;
}, {});
if (hasAggregate) {
return {
name: data_1.SUMMARY,
source: data_1.SOURCE,
transform: [{
type: 'aggregate',
groupby: groupby,
summarize: summarize
}]
};
}
return null;
}
summary.def = def;
;
})(summary = exports.summary || (exports.summary = {}));
var stack;
(function (stack) {
function def(model, stackProps) {
var groupbyChannel = stackProps.groupbyChannel;
var fieldChannel = stackProps.fieldChannel;
var facetFields = (model.has(channel_1.COLUMN) ? [model.field(channel_1.COLUMN)] : [])
.concat((model.has(channel_1.ROW) ? [model.field(channel_1.ROW)] : []));
var stacked = {
name: data_1.STACKED,
source: model.dataTable(),
transform: [{
type: 'aggregate',
groupby: [model.field(groupbyChannel)].concat(facetFields),
summarize: [{ ops: ['sum'], field: model.field(fieldChannel) }]
}]
};
if (facetFields && facetFields.length > 0) {
stacked.transform.push({
type: 'aggregate',
groupby: facetFields,
summarize: [{
ops: ['max'],
field: model.field(fieldChannel, { prefn: 'sum_' })
}]
});
}
return stacked;
}
stack.def = def;
;
})(stack = exports.stack || (exports.stack = {}));
function filterNonPositiveForLog(dataTable, model) {
model.forEach(function (_, channel) {
if (model.fieldDef(channel).scale.type === 'log') {
dataTable.transform.push({
type: 'filter',
test: model.field(channel, { datum: true }) + ' > 0'
});
}
});
}
return stacked;
};
data.filterNonPositive = function(dataTable, encoding) {
encoding.forEach(function(encDef, encType) {
if (encoding.scale(encType).type === 'log') {
dataTable.transform.push({
type: 'filter',
test: encoding.fieldRef(encType, {datum: 1}) + ' > 0'
});
}
});
};
}
exports.filterNonPositiveForLog = filterNonPositiveForLog;
//# sourceMappingURL=data.js.map

@@ -1,145 +0,224 @@

'use strict';
require('../globals');
var util = require('../util');
var axis = require('./axis'),
scale = require('./scale');
module.exports = faceting;
function groupdef(name, opt) {
opt = opt || {};
var group = {
name: name || undefined,
type: 'group',
properties: {
enter: {
width: opt.width || {field: {group: 'width'}},
height: opt.height || {field: {group: 'height'}}
}
var channel_1 = require('../channel');
var axis_1 = require('./axis');
var scale_1 = require('./scale');
function facetMixins(model, marks) {
var layout = model.layout();
var cellWidth = !model.has(channel_1.COLUMN) ?
{ field: { group: 'width' } } :
layout.cellWidth.field ?
{ scale: 'column', band: true } :
{ value: layout.cellWidth };
var cellHeight = !model.has(channel_1.ROW) ?
{ field: { group: 'height' } } :
layout.cellHeight.field ?
{ scale: 'row', band: true } :
{ value: layout.cellHeight };
var facetGroupProperties = {
width: cellWidth,
height: cellHeight
};
var cellConfig = model.config('cell');
['fill', 'fillOpacity', 'stroke', 'strokeWidth',
'strokeOpacity', 'strokeDash', 'strokeDashOffset']
.forEach(function (property) {
var value = cellConfig[property];
if (value !== undefined) {
facetGroupProperties[property] = value;
}
});
var rootMarks = [], rootAxes = [], facetKeys = [], cellAxes = [];
var hasRow = model.has(channel_1.ROW), hasCol = model.has(channel_1.COLUMN);
if (hasRow) {
if (!model.isDimension(channel_1.ROW)) {
util.error('Row encoding should be ordinal.');
}
facetGroupProperties.y = {
scale: channel_1.ROW,
field: model.field(channel_1.ROW)
};
facetKeys.push(model.field(channel_1.ROW));
rootAxes.push(axis_1.compileAxis(channel_1.ROW, model));
if (model.has(channel_1.X)) {
rootMarks.push(getXAxesGroup(model, cellWidth, hasCol));
}
rootMarks.push(getRowRulesGroup(model, cellHeight));
}
};
if (opt.from) {
group.from = opt.from;
}
if (opt.x) {
group.properties.enter.x = opt.x;
}
if (opt.y) {
group.properties.enter.y = opt.y;
}
if (opt.axes) {
group.axes = opt.axes;
}
return group;
}
function faceting(group, encoding, layout, spec, singleScaleNames, stats) {
var enter = group.properties.enter;
var facetKeys = [], cellAxes = [], from, axesGrp;
var hasRow = encoding.has(ROW), hasCol = encoding.has(COL);
enter.fill = {value: encoding.config('cellBackgroundColor')};
//move "from" to cell level and add facet transform
group.from = {data: group.marks[0].from.data};
// Hack, this needs to be refactored
for (var i = 0; i < group.marks.length; i++) {
var mark = group.marks[i];
if (mark.from.transform) {
delete mark.from.data; //need to keep transform for subfacetting case
} else {
delete mark.from;
else {
if (model.has(channel_1.X)) {
cellAxes.push(axis_1.compileAxis(channel_1.X, model));
}
}
}
if (hasRow) {
if (!encoding.isDimension(ROW)) {
util.error('Row encoding should be ordinal.');
}
enter.y = {scale: ROW, field: encoding.fieldRef(ROW)};
enter.height = {'value': layout.cellHeight}; // HACK
facetKeys.push(encoding.fieldRef(ROW));
if (hasCol) {
from = util.duplicate(group.from);
from.transform = from.transform || [];
from.transform.unshift({type: 'facet', groupby: [encoding.fieldRef(COL)]});
if (!model.isDimension(channel_1.COLUMN)) {
util.error('Col encoding should be ordinal.');
}
facetGroupProperties.x = {
scale: channel_1.COLUMN,
field: model.field(channel_1.COLUMN)
};
facetKeys.push(model.field(channel_1.COLUMN));
rootAxes.push(axis_1.compileAxis(channel_1.COLUMN, model));
if (model.has(channel_1.Y)) {
rootMarks.push(getYAxesGroup(model, cellHeight, hasRow));
}
rootMarks.push(getColumnRulesGroup(model, cellWidth));
}
axesGrp = groupdef('x-axes', {
axes: encoding.has(X) ? [axis.def(X, encoding, layout, stats)] : undefined,
x: hasCol ? {scale: COL, field: encoding.fieldRef(COL)} : {value: 0},
width: hasCol && {'value': layout.cellWidth}, //HACK?
from: from
});
spec.marks.unshift(axesGrp); // need to prepend so it appears under the plots
(spec.axes = spec.axes || []);
spec.axes.push(axis.def(ROW, encoding, layout, stats));
} else { // doesn't have row
if (encoding.has(X)) {
//keep x axis in the cell
cellAxes.push(axis.def(X, encoding, layout, stats));
else {
if (model.has(channel_1.Y)) {
cellAxes.push(axis_1.compileAxis(channel_1.Y, model));
}
}
}
if (hasCol) {
if (!encoding.isDimension(COL)) {
util.error('Col encoding should be ordinal.');
var facetGroup = {
name: 'cell',
type: 'group',
from: {
data: model.dataTable(),
transform: [{ type: 'facet', groupby: facetKeys }]
},
properties: {
update: facetGroupProperties
},
marks: marks
};
if (cellAxes.length > 0) {
facetGroup.axes = cellAxes;
}
enter.x = {scale: COL, field: encoding.fieldRef(COL)};
enter.width = {'value': layout.cellWidth}; // HACK
facetKeys.push(encoding.fieldRef(COL));
rootMarks.push(facetGroup);
var scaleNames = model.map(function (_, channel) {
return channel;
});
return {
marks: rootMarks,
axes: rootAxes,
scales: scale_1.compileScales(scaleNames, model)
};
}
exports.facetMixins = facetMixins;
function getXAxesGroup(model, cellWidth, hasCol) {
var xAxesGroup = {
name: 'x-axes',
type: 'group',
properties: {
update: {
width: cellWidth,
height: { field: { group: 'height' } },
x: hasCol ? { scale: channel_1.COLUMN, field: model.field(channel_1.COLUMN) } : { value: 0 },
y: { value: -model.config('cell').padding / 2 }
}
},
axes: [axis_1.compileAxis(channel_1.X, model)]
};
if (hasCol) {
xAxesGroup.from = {
data: model.dataTable(),
transform: { type: 'facet', groupby: [model.field(channel_1.COLUMN)] }
};
}
return xAxesGroup;
}
function getYAxesGroup(model, cellHeight, hasRow) {
var yAxesGroup = {
name: 'y-axes',
type: 'group',
properties: {
update: {
width: { field: { group: 'width' } },
height: cellHeight,
x: { value: -model.config('cell').padding / 2 },
y: hasRow ? { scale: channel_1.ROW, field: model.field(channel_1.ROW) } : { value: 0 }
}
},
axes: [axis_1.compileAxis(channel_1.Y, model)]
};
if (hasRow) {
from = util.duplicate(group.from);
from.transform = from.transform || [];
from.transform.unshift({type: 'facet', groupby: [encoding.fieldRef(ROW)]});
yAxesGroup.from = {
data: model.dataTable(),
transform: { type: 'facet', groupby: [model.field(channel_1.ROW)] }
};
}
axesGrp = groupdef('y-axes', {
axes: encoding.has(Y) ? [axis.def(Y, encoding, layout, stats)] : undefined,
y: hasRow && {scale: ROW, field: encoding.fieldRef(ROW)},
x: hasRow && {value: 0},
height: hasRow && {'value': layout.cellHeight}, //HACK?
from: from
});
spec.marks.unshift(axesGrp); // need to prepend so it appears under the plots
(spec.axes = spec.axes || []);
spec.axes.push(axis.def(COL, encoding, layout, stats));
} else { // doesn't have col
if (encoding.has(Y)) {
cellAxes.push(axis.def(Y, encoding, layout, stats));
return yAxesGroup;
}
function getRowRulesGroup(model, cellHeight) {
var rowRulesOnTop = !model.has(channel_1.X) || model.fieldDef(channel_1.X).axis.orient !== 'top';
var offset = model.config('cell').padding / 2 - 1;
var rowRules = {
name: 'row-rules',
type: 'rule',
from: {
data: model.dataTable(),
transform: [{ type: 'facet', groupby: [model.field(channel_1.ROW)] }]
},
properties: {
update: {
y: {
scale: 'row',
field: model.field(channel_1.ROW),
offset: (rowRulesOnTop ? -1 : 1) * offset
},
x: { value: 0, offset: -model.config('cell').gridOffset },
x2: { field: { group: 'width' }, offset: model.config('cell').gridOffset },
stroke: { value: model.config('cell').gridColor },
strokeOpacity: { value: model.config('cell').gridOpacity }
}
}
};
if (rowRulesOnTop) {
return rowRules;
}
}
// assuming equal cellWidth here
// TODO: support heterogenous cellWidth (maybe by using multiple scales?)
spec.scales = (spec.scales || []).concat(scale.defs(
scale.names(enter).concat(singleScaleNames),
encoding,
layout,
stats,
true
)); // row/col scales + cell scales
if (cellAxes.length > 0) {
group.axes = cellAxes;
}
// add facet transform
var trans = (group.from.transform || (group.from.transform = []));
trans.unshift({type: 'facet', groupby: facetKeys});
return spec;
return {
name: 'row-rules-group',
type: 'group',
properties: {
update: {
y: cellHeight.value ?
cellHeight :
{ field: { parent: 'cellHeight' } },
width: { field: { group: 'width' } }
}
},
marks: [rowRules]
};
}
function getColumnRulesGroup(model, cellWidth) {
var colRulesOnLeft = !model.has(channel_1.Y) || model.fieldDef(channel_1.Y).axis.orient === 'right';
var offset = model.config('cell').padding / 2 - 1;
var columnRules = {
name: 'column-rules',
type: 'rule',
from: {
data: model.dataTable(),
transform: [{ type: 'facet', groupby: [model.field(channel_1.COLUMN)] }]
},
properties: {
update: {
x: {
scale: 'column',
field: model.field(channel_1.COLUMN),
offset: (colRulesOnLeft ? -1 : 1) * offset
},
y: { value: 0, offset: -model.config('cell').gridOffset },
y2: { field: { group: 'height' }, offset: model.config('cell').gridOffset },
stroke: { value: model.config('cell').gridColor },
strokeOpacity: { value: model.config('cell').gridOpacity }
}
}
};
if (colRulesOnLeft) {
return columnRules;
}
return {
name: 'column-rules-group',
type: 'group',
properties: {
update: {
x: cellWidth.value ?
cellWidth :
{ field: { parent: 'cellWidth' } },
height: { field: { group: 'height' } }
}
},
marks: [columnRules]
};
}
//# sourceMappingURL=facet.js.map

@@ -1,156 +0,50 @@

'use strict';
require('../globals');
var util = require('../util'),
setter = util.setter,
time = require('./time'),
d3_format = require('d3-format');
module.exports = vllayout;
function vllayout(encoding, stats) {
var layout = box(encoding, stats);
layout = offset(encoding, stats, layout);
return layout;
var channel_1 = require('../channel');
var mark_1 = require('../mark');
var data_1 = require('../data');
function compileLayout(model) {
var cellWidth = getCellWidth(model);
var cellHeight = getCellHeight(model);
return {
cellWidth: cellWidth,
cellHeight: cellHeight,
width: getWidth(model, cellWidth),
height: getHeight(model, cellHeight)
};
}
/*
HACK to set chart size
NOTE: this fails for plots driven by derived values (e.g., aggregates)
One solution is to update Vega to support auto-sizing
In the meantime, auto-padding (mostly) does the trick
*/
function box(encoding, stats) {
var hasRow = encoding.has(ROW),
hasCol = encoding.has(COL),
hasX = encoding.has(X),
hasY = encoding.has(Y),
marktype = encoding.marktype();
// FIXME/HACK we need to take filter into account
var xCardinality = hasX && encoding.isDimension(X) ? encoding.cardinality(X, stats) : 1,
yCardinality = hasY && encoding.isDimension(Y) ? encoding.cardinality(Y, stats) : 1;
var useSmallBand = xCardinality > encoding.config('largeBandMaxCardinality') ||
yCardinality > encoding.config('largeBandMaxCardinality');
var cellWidth, cellHeight, cellPadding = encoding.config('cellPadding');
// set cellWidth
if (hasX) {
if (encoding.isOrdinalScale(X)) {
// for ordinal, hasCol or not doesn't matter -- we scale based on cardinality
cellWidth = (xCardinality + encoding.encDef(X).band.padding) * encoding.bandSize(X, useSmallBand);
} else {
cellWidth = hasCol || hasRow ? encoding.encDef(COL).width : encoding.config('singleWidth');
exports.compileLayout = compileLayout;
function getCellWidth(model) {
if (model.has(channel_1.X)) {
if (model.isOrdinalScale(channel_1.X)) {
return { data: data_1.LAYOUT, field: 'cellWidth' };
}
return model.config('cell').width;
}
} else {
if (marktype === TEXT) {
cellWidth = encoding.config('textCellWidth');
} else {
cellWidth = encoding.bandSize(X);
if (model.mark() === mark_1.TEXT) {
return model.config('textCellWidth');
}
}
// set cellHeight
if (hasY) {
if (encoding.isOrdinalScale(Y)) {
// for ordinal, hasCol or not doesn't matter -- we scale based on cardinality
cellHeight = (yCardinality + encoding.encDef(Y).band.padding) * encoding.bandSize(Y, useSmallBand);
} else {
cellHeight = hasCol || hasRow ? encoding.encDef(ROW).height : encoding.config('singleHeight');
return model.fieldDef(channel_1.X).scale.bandWidth;
}
function getWidth(model, cellWidth) {
if (model.has(channel_1.COLUMN)) {
return { data: data_1.LAYOUT, field: 'width' };
}
} else {
cellHeight = encoding.bandSize(Y);
}
// Cell bands use rangeBands(). There are n-1 padding. Outerpadding = 0 for cells
var width = cellWidth, height = cellHeight;
if (hasCol) {
var colCardinality = encoding.cardinality(COL, stats);
width = cellWidth * ((1 + cellPadding) * (colCardinality - 1) + 1);
}
if (hasRow) {
var rowCardinality = encoding.cardinality(ROW, stats);
height = cellHeight * ((1 + cellPadding) * (rowCardinality - 1) + 1);
}
return {
// width and height of the whole cell
cellWidth: cellWidth,
cellHeight: cellHeight,
cellPadding: cellPadding,
// width and height of the chart
width: width,
height: height,
// information about x and y, such as band size
x: {useSmallBand: useSmallBand},
y: {useSmallBand: useSmallBand}
};
return cellWidth;
}
// FIXME fieldStats.max isn't always the longest
function getMaxNumberLength(encoding, et, fieldStats) {
var format = encoding.numberFormat(et, fieldStats);
return d3_format.format(format)(fieldStats.max).length;
}
// TODO(#600) revise this
function getMaxLength(encoding, stats, et) {
var encDef = encoding.encDef(et),
fieldStats = stats[encDef.name];
if (encDef.bin) {
// TODO once bin support range, need to update this
return getMaxNumberLength(encoding, et, fieldStats);
} if (encoding.isType(et, Q)) {
return getMaxNumberLength(encoding, et, fieldStats);
} else if (encoding.isType(et, T)) {
return time.maxLength(encoding.encDef(et).timeUnit, encoding);
} else if (encoding.isTypes(et, [N, O])) {
if(fieldStats.type === 'number') {
return getMaxNumberLength(encoding, et, fieldStats);
} else {
return Math.min(fieldStats.max, encoding.axis(et).maxLabelLength || Infinity);
function getCellHeight(model) {
if (model.has(channel_1.Y)) {
if (model.isOrdinalScale(channel_1.Y)) {
return { data: data_1.LAYOUT, field: 'cellHeight' };
}
else {
return model.config('cell').height;
}
}
}
return model.fieldDef(channel_1.Y).scale.bandWidth;
}
function offset(encoding, stats, layout) {
[X, Y].forEach(function (et) {
// TODO(kanitw): Jul 19, 2015 - create a set of visual test for extraOffset
var extraOffset = et === X ? 20 : 22,
maxLength;
if (encoding.isDimension(et) || encoding.isType(et, T)) {
maxLength = getMaxLength(encoding, stats, et);
} else if (
// TODO once we have #512 (allow using inferred type)
// Need to adjust condition here.
encoding.isType(et, Q) ||
encoding.encDef(et).aggregate === 'count'
) {
if (
et===Y
// || (et===X && false)
// FIXME determine when X would rotate, but should move this to axis.js first #506
) {
maxLength = getMaxLength(encoding, stats, et);
}
} else {
// nothing
function getHeight(model, cellHeight) {
if (model.has(channel_1.ROW)) {
return { data: data_1.LAYOUT, field: 'height' };
}
if (maxLength) {
setter(layout,[et, 'axisTitleOffset'], encoding.config('characterWidth') * maxLength + extraOffset);
} else {
// if no max length (no rotation case), use maxLength = 3
setter(layout,[et, 'axisTitleOffset'], encoding.config('characterWidth') * 3 + extraOffset);
}
});
return layout;
return cellHeight;
}
//# sourceMappingURL=layout.js.map

@@ -1,108 +0,117 @@

'use strict';
require('../globals');
var time = require('./time'),
util = require('../util'),
setter = util.setter,
getter = util.getter;
var legend = module.exports = {};
legend.defs = function(encoding, style) {
var defs = [];
if (encoding.has(COLOR) && encoding.encDef(COLOR).legend) {
defs.push(legend.def(COLOR, encoding, {
fill: COLOR
}, style));
}
if (encoding.has(SIZE) && encoding.encDef(SIZE).legend) {
defs.push(legend.def(SIZE, encoding, {
size: SIZE
}, style));
}
if (encoding.has(SHAPE) && encoding.encDef(SHAPE).legend) {
defs.push(legend.def(SHAPE, encoding, {
shape: SHAPE
}, style));
}
return defs;
};
legend.def = function(name, encoding, def, style) {
var timeUnit = encoding.encDef(name).timeUnit;
def.title = legend.title(name, encoding);
def.orient = encoding.encDef(name).legend.orient;
def = legend.style(name, encoding, def, style);
if (encoding.isType(name, T) &&
timeUnit &&
time.hasScale(timeUnit)
) {
setter(def, ['properties', 'labels', 'text', 'scale'], 'time-'+ timeUnit);
}
return def;
};
legend.style = function(name, e, def, style) {
var symbols = getter(def, ['properties', 'symbols']),
marktype = e.marktype();
switch (marktype) {
case 'bar':
case 'tick':
case 'text':
symbols.stroke = {value: 'transparent'};
symbols.shape = {value: 'square'};
break;
case 'circle':
case 'square':
symbols.shape = {value: marktype};
/* fall through */
case 'point':
// fill or stroke
if (e.encDef(SHAPE).filled) {
if (e.has(COLOR) && name === COLOR) {
symbols.fill = {scale: COLOR, field: 'data'};
} else {
symbols.fill = {value: e.value(COLOR)};
var util_1 = require('../util');
var channel_1 = require('../channel');
var time = require('./time');
var type_1 = require('../type');
var mark_1 = require('../mark');
function compileLegends(model) {
var defs = [];
if (model.has(channel_1.COLOR) && model.fieldDef(channel_1.COLOR).legend) {
defs.push(compileLegend(model, channel_1.COLOR, {
fill: channel_1.COLOR
}));
}
if (model.has(channel_1.SIZE) && model.fieldDef(channel_1.SIZE).legend) {
defs.push(compileLegend(model, channel_1.SIZE, {
size: channel_1.SIZE
}));
}
if (model.has(channel_1.SHAPE) && model.fieldDef(channel_1.SHAPE).legend) {
defs.push(compileLegend(model, channel_1.SHAPE, {
shape: channel_1.SHAPE
}));
}
return defs;
}
exports.compileLegends = compileLegends;
function compileLegend(model, channel, def) {
var legend = model.fieldDef(channel).legend;
def.title = title(model, channel);
['orient', 'format', 'values'].forEach(function (property) {
var value = legend[property];
if (value !== undefined) {
def[property] = value;
}
symbols.stroke = {value: 'transparent'};
} else {
if (e.has(COLOR) && name === COLOR) {
symbols.stroke = {scale: COLOR, field: 'data'};
} else {
symbols.stroke = {value: e.value(COLOR)};
});
var props = (typeof legend !== 'boolean' && legend.properties) || {};
['title', 'labels', 'symbols', 'legend'].forEach(function (group) {
var value = properties[group] ?
properties[group](model, channel, props[group]) :
props[group];
if (value !== undefined) {
def.properties = def.properties || {};
def.properties[group] = value;
}
symbols.fill = {value: 'transparent'};
symbols.strokeWidth = {value: e.config('strokeWidth')};
}
break;
case 'line':
case 'area':
// TODO use shape here after implementing #508
break;
}
var opacity = e.encDef(COLOR).opacity || style.opacity;
if (opacity) {
symbols.opacity = {value: opacity};
}
return def;
};
legend.title = function(name, encoding) {
var leg = encoding.encDef(name).legend;
if (leg.title) return leg.title;
return encoding.fieldTitle(name);
};
});
return def;
}
exports.compileLegend = compileLegend;
function title(model, channel) {
var legend = model.fieldDef(channel).legend;
if (typeof legend !== 'boolean' && legend.title) {
return legend.title;
}
return model.fieldTitle(channel);
}
exports.title = title;
var properties;
(function (properties) {
function labels(model, channel, spec) {
var fieldDef = model.fieldDef(channel);
var timeUnit = fieldDef.timeUnit;
if (fieldDef.type === type_1.TEMPORAL && timeUnit && time.labelTemplate(timeUnit)) {
return util_1.extend({
text: {
template: '{{datum.data | ' + time.labelTemplate(timeUnit) + '}}'
}
}, spec || {});
}
return spec;
}
properties.labels = labels;
function symbols(model, channel, spec) {
var symbols = {};
var mark = model.mark();
switch (mark) {
case mark_1.BAR:
case mark_1.TICK:
case mark_1.TEXT:
symbols.stroke = { value: 'transparent' };
symbols.shape = { value: 'square' };
break;
case mark_1.CIRCLE:
case mark_1.SQUARE:
symbols.shape = { value: mark };
case mark_1.POINT:
if (model.config('marks').filled) {
if (model.has(channel_1.COLOR) && channel === channel_1.COLOR) {
symbols.fill = { scale: channel_1.COLOR, field: 'data' };
}
else {
symbols.fill = { value: model.fieldDef(channel_1.COLOR).value };
}
symbols.stroke = { value: 'transparent' };
}
else {
if (model.has(channel_1.COLOR) && channel === channel_1.COLOR) {
symbols.stroke = { scale: channel_1.COLOR, field: 'data' };
}
else {
symbols.stroke = { value: model.fieldDef(channel_1.COLOR).value };
}
symbols.fill = { value: 'transparent' };
symbols.strokeWidth = { value: model.config('marks').strokeWidth };
}
break;
case mark_1.LINE:
case mark_1.AREA:
break;
}
var opacity = model.markOpacity();
if (opacity)
symbols.opacity = { value: opacity };
symbols = util_1.extend(symbols, spec || {});
return util_1.keys(symbols).length > 0 ? symbols : undefined;
}
properties.symbols = symbols;
})(properties || (properties = {}));
//# sourceMappingURL=legend.js.map

@@ -1,452 +0,584 @@

'use strict';
require('../globals');
var marks = module.exports = {};
marks.def = function(encoding, layout, style, stats) {
var defs = [],
mark = marks[encoding.marktype()],
from = encoding.dataTable();
// to add a background to text, we need to add it before the text
if (encoding.marktype() === TEXT && encoding.has(COLOR)) {
var bg = {
x: {value: 0},
y: {value: 0},
x2: {value: layout.cellWidth},
y2: {value: layout.cellHeight},
fill: {scale: COLOR, field: encoding.fieldRef(COLOR)}
};
defs.push({
type: 'rect',
from: {data: from},
properties: {enter: bg, update: bg}
});
}
// add the mark def for the main thing
var p = mark.prop(encoding, layout, style, stats);
defs.push({
type: mark.type,
from: {data: from},
properties: {enter: p, update: p}
});
return defs;
var channel_1 = require('../channel');
var mark_1 = require('../mark');
var type_1 = require('../type');
var stack_1 = require('./stack');
var MARKTYPES_MAP = {
bar: 'rect',
tick: 'rect',
point: 'symbol',
line: 'line',
area: 'area',
text: 'text',
circle: 'symbol',
square: 'symbol'
};
marks.bar = {
type: 'rect',
prop: bar_props,
supportedEncoding: {row: 1, col: 1, x: 1, y: 1, size: 1, color: 1}
};
marks.line = {
type: 'line',
line: true,
prop: line_props,
requiredEncoding: ['x', 'y'],
supportedEncoding: {row: 1, col: 1, x: 1, y: 1, color: 1, detail:1}
};
marks.area = {
type: 'area',
line: true,
requiredEncoding: ['x', 'y'],
prop: area_props,
supportedEncoding: {row: 1, col: 1, x: 1, y: 1, color: 1}
};
marks.tick = {
type: 'rect',
prop: tick_props,
supportedEncoding: {row: 1, col: 1, x: 1, y: 1, color: 1, detail: 1}
};
marks.circle = {
type: 'symbol',
prop: filled_point_props('circle'),
supportedEncoding: {row: 1, col: 1, x: 1, y: 1, size: 1, color: 1, detail: 1}
};
marks.square = {
type: 'symbol',
prop: filled_point_props('square'),
supportedEncoding: marks.circle.supportedEncoding
};
marks.point = {
type: 'symbol',
prop: point_props,
supportedEncoding: {row: 1, col: 1, x: 1, y: 1, size: 1, color: 1, shape: 1, detail: 1}
};
marks.text = {
type: 'text',
prop: text_props,
requiredEncoding: ['text'],
supportedEncoding: {row: 1, col: 1, size: 1, color: 1, text: 1}
};
function bar_props(e, layout, style) {
// jshint unused:false
var p = {};
// x's and width
if (e.encDef(X).bin) {
p.x = {scale: X, field: e.fieldRef(X, {bin_suffix: '_start'}), offset: 1};
p.x2 = {scale: X, field: e.fieldRef(X, {bin_suffix: '_end'})};
} else if (e.isMeasure(X)) {
p.x = {scale: X, field: e.fieldRef(X)};
if (!e.has(Y) || e.isDimension(Y)) {
p.x2 = {value: 0};
function compileMarks(model) {
var mark = model.mark();
if (mark === mark_1.LINE || mark === mark_1.AREA) {
var sortBy = mark === mark_1.LINE ? model.config('sortLineBy') : undefined;
if (!sortBy) {
var sortField = (model.isMeasure(channel_1.X) && model.isDimension(channel_1.Y)) ? channel_1.Y : channel_1.X;
sortBy = '-' + model.field(sortField);
}
var pathMarks = {
type: MARKTYPES_MAP[mark],
from: {
transform: [{ type: 'sort', by: sortBy }]
},
properties: {
update: properties[mark](model)
}
};
var details = detailFields(model);
if (details.length > 0) {
var facetTransform = { type: 'facet', groupby: details };
var transform = mark === mark_1.AREA && model.stack() ?
[stack_1.imputeTransform(model), stack_1.stackTransform(model), facetTransform] :
[facetTransform];
return [{
name: mark + '-facet',
type: 'group',
from: {
transform: transform
},
properties: {
update: {
width: { field: { group: 'width' } },
height: { field: { group: 'height' } }
}
},
marks: [pathMarks]
}];
}
else {
return [pathMarks];
}
}
} else {
if (e.has(X)) { // is ordinal
p.xc = {scale: X, field: e.fieldRef(X)};
} else {
p.x = {value: 0, offset: e.config('singleBarOffset')};
}
}
// width
if (!p.x2) {
if (!e.has(X) || e.isOrdinalScale(X)) { // no X or X is ordinal
if (e.has(SIZE)) {
p.width = {scale: SIZE, field: e.fieldRef(SIZE)};
} else {
p.width = {
value: e.bandSize(X, layout.x.useSmallBand),
offset: -1
else {
var marks = [];
if (mark === mark_1.TEXT && model.has(channel_1.COLOR)) {
marks.push({
type: 'rect',
properties: { update: properties.textBackground(model) }
});
}
var mainDef = {
type: MARKTYPES_MAP[mark],
properties: {
update: properties[mark](model)
}
};
}
} else { // X is Quant or Time Scale
p.width = {value: 2};
var stack = model.stack();
if (mark === mark_1.BAR && stack) {
mainDef.from = {
transform: [stack_1.stackTransform(model)]
};
}
marks.push(mainDef);
return marks;
}
}
// y's & height
if (e.encDef(Y).bin) {
p.y = {scale: Y, field: e.fieldRef(Y, {bin_suffix: '_start'})};
p.y2 = {scale: Y, field: e.fieldRef(Y, {bin_suffix: '_end'}), offset: 1};
} else if (e.isMeasure(Y)) {
p.y = {scale: Y, field: e.fieldRef(Y)};
p.y2 = {field: {group: 'height'}};
} else {
if (e.has(Y)) { // is ordinal
p.yc = {scale: Y, field: e.fieldRef(Y)};
} else {
p.y2 = {
field: {group: 'height'},
offset: -e.config('singleBarOffset')
};
}
if (e.has(SIZE)) {
p.height = {scale: SIZE, field: e.fieldRef(SIZE)};
} else {
p.height = {
value: e.bandSize(Y, layout.y.useSmallBand),
offset: -1
};
}
}
// fill
if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else {
p.fill = {value: e.value(COLOR)};
}
// opacity
var opacity = e.encDef(COLOR).opacity;
if (opacity) p.opacity = {value: opacity};
return p;
}
function point_props(e, layout, style) {
var p = {};
// x
if (e.has(X)) {
p.x = {scale: X, field: e.fieldRef(X, {bin_suffix: '_mid'})};
} else if (!e.has(X)) {
p.x = {value: e.bandSize(X, layout.x.useSmallBand) / 2};
}
// y
if (e.has(Y)) {
p.y = {scale: Y, field: e.fieldRef(Y, {bin_suffix: '_mid'})};
} else if (!e.has(Y)) {
p.y = {value: e.bandSize(Y, layout.y.useSmallBand) / 2};
}
// size
if (e.has(SIZE)) {
p.size = {scale: SIZE, field: e.fieldRef(SIZE)};
} else if (!e.has(SIZE)) {
p.size = {value: e.value(SIZE)};
}
// shape
if (e.has(SHAPE)) {
p.shape = {scale: SHAPE, field: e.fieldRef(SHAPE)};
} else if (!e.has(SHAPE)) {
p.shape = {value: e.value(SHAPE)};
}
// fill or stroke
if (e.encDef(SHAPE).filled) {
if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {
p.fill = {value: e.value(COLOR)};
}
} else {
if (e.has(COLOR)) {
p.stroke = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {
p.stroke = {value: e.value(COLOR)};
}
p.strokeWidth = {value: e.config('strokeWidth')};
}
// opacity
var opacity = e.encDef(COLOR).opacity || style.opacity;
if (opacity) p.opacity = {value: opacity};
return p;
exports.compileMarks = compileMarks;
function applyMarksConfig(marksProperties, marksConfig, propsList) {
propsList.forEach(function (property) {
var value = marksConfig[property];
if (value !== undefined) {
marksProperties[property] = { value: value };
}
});
}
function line_props(e,layout, style) {
// jshint unused:false
var p = {};
// x
if (e.has(X)) {
p.x = {scale: X, field: e.fieldRef(X, {bin_suffix: '_mid'})};
} else if (!e.has(X)) {
p.x = {value: 0};
}
// y
if (e.has(Y)) {
p.y = {scale: Y, field: e.fieldRef(Y, {bin_suffix: '_mid'})};
} else if (!e.has(Y)) {
p.y = {field: {group: 'height'}};
}
// stroke
if (e.has(COLOR)) {
p.stroke = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {
p.stroke = {value: e.value(COLOR)};
}
var opacity = e.encDef(COLOR).opacity;
if (opacity) p.opacity = {value: opacity};
p.strokeWidth = {value: e.config('strokeWidth')};
return p;
function detailFields(model) {
return [channel_1.COLOR, channel_1.DETAIL, channel_1.SHAPE].reduce(function (details, channel) {
if (model.has(channel) && !model.fieldDef(channel).aggregate) {
details.push(model.field(channel));
}
return details;
}, []);
}
// TODO(#694): optimize area's usage with bin
function area_props(e, layout, style) {
// jshint unused:false
var p = {};
// x
if (e.isMeasure(X)) {
p.x = {scale: X, field: e.fieldRef(X)};
if (e.isDimension(Y)) {
p.x2 = {scale: X, value: 0};
p.orient = {value: 'horizontal'};
var properties;
(function (properties) {
function bar(model) {
var stack = model.stack();
var p = {};
if (stack && channel_1.X === stack.fieldChannel) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X) + '_start'
};
p.x2 = {
scale: channel_1.X,
field: model.field(channel_1.X) + '_end'
};
}
else if (model.fieldDef(channel_1.X).bin) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X, { binSuffix: '_start' }),
offset: 1
};
p.x2 = {
scale: channel_1.X,
field: model.field(channel_1.X, { binSuffix: '_end' })
};
}
else if (model.isMeasure(channel_1.X)) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X)
};
if (!model.has(channel_1.Y) || model.isDimension(channel_1.Y)) {
p.x2 = { value: 0 };
}
}
else {
if (model.has(channel_1.X)) {
p.xc = {
scale: channel_1.X,
field: model.field(channel_1.X)
};
}
else {
p.x = { value: 0, offset: model.config('singleBarOffset') };
}
}
if (!p.x2) {
if (!model.has(channel_1.X) || model.isOrdinalScale(channel_1.X)) {
if (model.has(channel_1.SIZE)) {
p.width = {
scale: channel_1.SIZE,
field: model.field(channel_1.SIZE)
};
}
else {
p.width = {
value: model.fieldDef(channel_1.X).scale.bandWidth,
offset: -1
};
}
}
else {
p.width = { value: 2 };
}
}
if (stack && channel_1.Y === stack.fieldChannel) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y) + '_start'
};
p.y2 = {
scale: channel_1.Y,
field: model.field(channel_1.Y) + '_end'
};
}
else if (model.fieldDef(channel_1.Y).bin) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y, { binSuffix: '_start' })
};
p.y2 = {
scale: channel_1.Y,
field: model.field(channel_1.Y, { binSuffix: '_end' }),
offset: 1
};
}
else if (model.isMeasure(channel_1.Y)) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y)
};
p.y2 = { field: { group: 'height' } };
}
else {
if (model.has(channel_1.Y)) {
p.yc = {
scale: channel_1.Y,
field: model.field(channel_1.Y)
};
}
else {
p.y2 = {
field: { group: 'height' },
offset: -model.config('singleBarOffset')
};
}
if (model.has(channel_1.SIZE)) {
p.height = {
scale: channel_1.SIZE,
field: model.field(channel_1.SIZE)
};
}
else {
p.height = {
value: model.fieldDef(channel_1.Y).scale.bandWidth,
offset: -1
};
}
}
if (model.has(channel_1.COLOR)) {
p.fill = {
scale: channel_1.COLOR,
field: model.field(channel_1.COLOR)
};
}
else {
p.fill = { value: model.fieldDef(channel_1.COLOR).value };
}
var opacity = model.markOpacity();
if (opacity)
p.opacity = { value: opacity };
return p;
}
} else if (e.has(X)) {
p.x = {scale: X, field: e.fieldRef(X, {bin_suffix: '_mid'})};
} else {
p.x = {value: 0};
}
// y
if (e.isMeasure(Y)) {
p.y = {scale: Y, field: e.fieldRef(Y)};
p.y2 = {scale: Y, value: 0};
} else if (e.has(Y)) {
p.y = {scale: Y, field: e.fieldRef(Y, {bin_suffix: '_mid'})};
} else {
p.y = {field: {group: 'height'}};
}
// fill
if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {
p.fill = {value: e.value(COLOR)};
}
var opacity = e.encDef(COLOR).opacity;
if (opacity) p.opacity = {value: opacity};
return p;
}
function tick_props(e, layout, style) {
var p = {};
// x
if (e.has(X)) {
p.x = {scale: X, field: e.fieldRef(X, {bin_suffix: '_mid'})};
if (e.isDimension(X)) {
p.x.offset = -e.bandSize(X, layout.x.useSmallBand) / 3;
properties.bar = bar;
function point(model) {
var p = {};
var marksConfig = model.config('marks');
if (model.has(channel_1.X)) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X, { binSuffix: '_mid' })
};
}
else if (!model.has(channel_1.X)) {
p.x = { value: model.fieldDef(channel_1.X).scale.bandWidth / 2 };
}
if (model.has(channel_1.Y)) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y, { binSuffix: '_mid' })
};
}
else if (!model.has(channel_1.Y)) {
p.y = { value: model.fieldDef(channel_1.Y).scale.bandWidth / 2 };
}
if (model.has(channel_1.SIZE)) {
p.size = {
scale: channel_1.SIZE,
field: model.field(channel_1.SIZE)
};
}
else if (!model.has(channel_1.SIZE)) {
p.size = { value: model.fieldDef(channel_1.SIZE).value };
}
if (model.has(channel_1.SHAPE)) {
p.shape = {
scale: channel_1.SHAPE,
field: model.field(channel_1.SHAPE)
};
}
else if (!model.has(channel_1.SHAPE)) {
p.shape = { value: model.fieldDef(channel_1.SHAPE).value };
}
if (marksConfig.filled) {
if (model.has(channel_1.COLOR)) {
p.fill = {
scale: channel_1.COLOR,
field: model.field(channel_1.COLOR)
};
}
else if (!model.has(channel_1.COLOR)) {
p.fill = { value: model.fieldDef(channel_1.COLOR).value };
}
}
else {
if (model.has(channel_1.COLOR)) {
p.stroke = {
scale: channel_1.COLOR,
field: model.field(channel_1.COLOR)
};
}
else if (!model.has(channel_1.COLOR)) {
p.stroke = { value: model.fieldDef(channel_1.COLOR).value };
}
p.strokeWidth = { value: model.config('marks').strokeWidth };
}
var opacity = model.markOpacity();
if (opacity)
p.opacity = { value: opacity };
return p;
}
} else if (!e.has(X)) {
p.x = {value: 0};
}
// y
if (e.has(Y)) {
p.y = {scale: Y, field: e.fieldRef(Y, {bin_suffix: '_mid'})};
if (e.isDimension(Y)) {
p.y.offset = -e.bandSize(Y, layout.y.useSmallBand) / 3;
properties.point = point;
function line(model) {
var p = {};
if (model.has(channel_1.X)) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X, { binSuffix: '_mid' })
};
}
else if (!model.has(channel_1.X)) {
p.x = { value: 0 };
}
if (model.has(channel_1.Y)) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y, { binSuffix: '_mid' })
};
}
else if (!model.has(channel_1.Y)) {
p.y = { field: { group: 'height' } };
}
if (model.has(channel_1.COLOR)) {
p.stroke = {
scale: channel_1.COLOR,
field: model.field(channel_1.COLOR)
};
}
else if (!model.has(channel_1.COLOR)) {
p.stroke = { value: model.fieldDef(channel_1.COLOR).value };
}
var opacity = model.markOpacity();
if (opacity)
p.opacity = { value: opacity };
p.strokeWidth = { value: model.config('marks').strokeWidth };
applyMarksConfig(p, model.config('marks'), ['interpolate', 'tension']);
return p;
}
} else if (!e.has(Y)) {
p.y = {value: 0};
}
// width
if (!e.has(X) || e.isDimension(X)) {
// TODO(#694): optimize tick's width for bin
p.width = {value: e.bandSize(X, layout.y.useSmallBand) / 1.5};
} else {
p.width = {value: 1};
}
// height
if (!e.has(Y) || e.isDimension(Y)) {
// TODO(#694): optimize tick's height for bin
p.height = {value: e.bandSize(Y, layout.y.useSmallBand) / 1.5};
} else {
p.height = {value: 1};
}
// fill
if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else {
p.fill = {value: e.value(COLOR)};
}
var opacity = e.encDef(COLOR).opacity || style.opacity;
if(opacity) p.opacity = {value: opacity};
return p;
}
function filled_point_props(shape) {
return function(e, layout, style) {
var p = {};
// x
if (e.has(X)) {
p.x = {scale: X, field: e.fieldRef(X, {bin_suffix: '_mid'})};
} else if (!e.has(X)) {
p.x = {value: e.bandSize(X, layout.x.useSmallBand) / 2};
properties.line = line;
function area(model) {
var stack = model.stack();
var p = {};
if (stack && channel_1.X === stack.fieldChannel) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X) + '_start'
};
p.x2 = {
scale: channel_1.X,
field: model.field(channel_1.X) + '_end'
};
}
else if (model.isMeasure(channel_1.X)) {
p.x = { scale: channel_1.X, field: model.field(channel_1.X) };
if (model.isDimension(channel_1.Y)) {
p.x2 = {
scale: channel_1.X,
value: 0
};
p.orient = { value: 'horizontal' };
}
}
else if (model.has(channel_1.X)) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X, { binSuffix: '_mid' })
};
}
else {
p.x = { value: 0 };
}
if (stack && channel_1.Y === stack.fieldChannel) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y) + '_start'
};
p.y2 = {
scale: channel_1.Y,
field: model.field(channel_1.Y) + '_end'
};
}
else if (model.isMeasure(channel_1.Y)) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y)
};
p.y2 = {
scale: channel_1.Y,
value: 0
};
}
else if (model.has(channel_1.Y)) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y, { binSuffix: '_mid' })
};
}
else {
p.y = { field: { group: 'height' } };
}
if (model.has(channel_1.COLOR)) {
p.fill = {
scale: channel_1.COLOR,
field: model.field(channel_1.COLOR)
};
}
else if (!model.has(channel_1.COLOR)) {
p.fill = { value: model.fieldDef(channel_1.COLOR).value };
}
var opacity = model.markOpacity();
if (opacity)
p.opacity = { value: opacity };
applyMarksConfig(p, model.config('marks'), ['interpolate', 'tension']);
return p;
}
// y
if (e.has(Y)) {
p.y = {scale: Y, field: e.fieldRef(Y, {bin_suffix: '_mid'})};
} else if (!e.has(Y)) {
p.y = {value: e.bandSize(Y, layout.y.useSmallBand) / 2};
properties.area = area;
function tick(model) {
var p = {};
if (model.has(channel_1.X)) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X, { binSuffix: '_mid' })
};
if (model.isDimension(channel_1.X)) {
p.x.offset = -model.fieldDef(channel_1.X).scale.bandWidth / 3;
}
}
else if (!model.has(channel_1.X)) {
p.x = { value: 0 };
}
if (model.has(channel_1.Y)) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y, { binSuffix: '_mid' })
};
if (model.isDimension(channel_1.Y)) {
p.y.offset = -model.fieldDef(channel_1.Y).scale.bandWidth / 3;
}
}
else if (!model.has(channel_1.Y)) {
p.y = { value: 0 };
}
if (!model.has(channel_1.X) || model.isDimension(channel_1.X)) {
p.width = { value: model.fieldDef(channel_1.X).scale.bandWidth / 1.5 };
}
else {
p.width = { value: 1 };
}
if (!model.has(channel_1.Y) || model.isDimension(channel_1.Y)) {
p.height = { value: model.fieldDef(channel_1.Y).scale.bandWidth / 1.5 };
}
else {
p.height = { value: 1 };
}
if (model.has(channel_1.COLOR)) {
p.fill = {
scale: channel_1.COLOR,
field: model.field(channel_1.COLOR)
};
}
else {
p.fill = { value: model.fieldDef(channel_1.COLOR).value };
}
var opacity = model.markOpacity();
if (opacity)
p.opacity = { value: opacity };
return p;
}
// size
if (e.has(SIZE)) {
p.size = {scale: SIZE, field: e.fieldRef(SIZE)};
} else if (!e.has(X)) {
p.size = {value: e.value(SIZE)};
properties.tick = tick;
function filled_point_props(shape) {
return function (model) {
var p = {};
if (model.has(channel_1.X)) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X, { binSuffix: '_mid' })
};
}
else if (!model.has(channel_1.X)) {
p.x = { value: model.fieldDef(channel_1.X).scale.bandWidth / 2 };
}
if (model.has(channel_1.Y)) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y, { binSuffix: '_mid' })
};
}
else if (!model.has(channel_1.Y)) {
p.y = { value: model.fieldDef(channel_1.Y).scale.bandWidth / 2 };
}
if (model.has(channel_1.SIZE)) {
p.size = {
scale: channel_1.SIZE,
field: model.field(channel_1.SIZE)
};
}
else if (!model.has(channel_1.X)) {
p.size = { value: model.fieldDef(channel_1.SIZE).value };
}
p.shape = { value: shape };
if (model.has(channel_1.COLOR)) {
p.fill = {
scale: channel_1.COLOR,
field: model.field(channel_1.COLOR)
};
}
else if (!model.has(channel_1.COLOR)) {
p.fill = { value: model.fieldDef(channel_1.COLOR).value };
}
var opacity = model.markOpacity();
if (opacity)
p.opacity = { value: opacity };
return p;
};
}
// shape
p.shape = {value: shape};
// fill
if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {
p.fill = {value: e.value(COLOR)};
properties.circle = filled_point_props('circle');
properties.square = filled_point_props('square');
function textBackground(model) {
return {
x: { value: 0 },
y: { value: 0 },
width: { field: { group: 'width' } },
height: { field: { group: 'height' } },
fill: { scale: channel_1.COLOR, field: model.field(channel_1.COLOR) }
};
}
var opacity = e.encDef(COLOR).opacity || style.opacity;
if(opacity) p.opacity = {value: opacity};
return p;
};
}
function text_props(e, layout, style, stats) {
var p = {},
encDef = e.encDef(TEXT);
// x
if (e.has(X)) {
p.x = {scale: X, field: e.fieldRef(X, {bin_suffix: '_mid'})};
} else if (!e.has(X)) {
if (e.has(TEXT) && e.isType(TEXT, Q)) {
p.x = {value: layout.cellWidth-5};
} else {
p.x = {value: e.bandSize(X, layout.x.useSmallBand) / 2};
properties.textBackground = textBackground;
function text(model) {
var p = {};
var fieldDef = model.fieldDef(channel_1.TEXT);
var marksConfig = model.config('marks');
if (model.has(channel_1.X)) {
p.x = {
scale: channel_1.X,
field: model.field(channel_1.X, { binSuffix: '_mid' })
};
}
else if (!model.has(channel_1.X)) {
if (model.has(channel_1.TEXT) && model.fieldDef(channel_1.TEXT).type === type_1.QUANTITATIVE) {
p.x = { field: { group: 'width' }, offset: -5 };
}
else {
p.x = { value: model.fieldDef(channel_1.X).scale.bandWidth / 2 };
}
}
if (model.has(channel_1.Y)) {
p.y = {
scale: channel_1.Y,
field: model.field(channel_1.Y, { binSuffix: '_mid' })
};
}
else if (!model.has(channel_1.Y)) {
p.y = { value: model.fieldDef(channel_1.Y).scale.bandWidth / 2 };
}
if (model.has(channel_1.SIZE)) {
p.fontSize = {
scale: channel_1.SIZE,
field: model.field(channel_1.SIZE)
};
}
else if (!model.has(channel_1.SIZE)) {
p.fontSize = { value: marksConfig.fontSize };
}
var opacity = model.markOpacity();
if (opacity)
p.opacity = { value: opacity };
if (model.has(channel_1.TEXT)) {
if (model.fieldDef(channel_1.TEXT).type === type_1.QUANTITATIVE) {
var numberFormat = marksConfig.format !== undefined ?
marksConfig.format : model.numberFormat(channel_1.TEXT);
p.text = { template: '{{' + model.field(channel_1.TEXT, { datum: true }) +
' | number:\'' + numberFormat + '\'}}' };
}
else {
p.text = { field: model.field(channel_1.TEXT) };
}
}
else {
p.text = { value: fieldDef.value };
}
applyMarksConfig(p, marksConfig, ['angle', 'align', 'baseline', 'dx', 'dy', 'fill', 'font', 'fontWeight',
'fontStyle', 'radius', 'theta']);
return p;
}
}
// y
if (e.has(Y)) {
p.y = {scale: Y, field: e.fieldRef(Y, {bin_suffix: '_mid'})};
} else if (!e.has(Y)) {
p.y = {value: e.bandSize(Y, layout.y.useSmallBand) / 2};
}
// size
if (e.has(SIZE)) {
p.fontSize = {scale: SIZE, field: e.fieldRef(SIZE)};
} else if (!e.has(SIZE)) {
p.fontSize = {value: encDef.font.size};
}
// fill
// color should be set to background
p.fill = {value: encDef.color};
var opacity = e.encDef(COLOR).opacity || style.opacity;
if(opacity) p.opacity = {value: opacity};
// text
if (e.has(TEXT)) {
if (e.isType(TEXT, Q)) {
var fieldStats = stats[e.encDef(TEXT).name],
numberFormat = encDef.format || e.numberFormat(fieldStats);
p.text = {template: '{{' + e.fieldRef(TEXT, {datum: true}) + ' | number:\'' +
numberFormat +'\'}}'};
p.align = {value: encDef.align};
} else {
p.text = {field: e.fieldRef(TEXT)};
}
} else {
p.text = {value: encDef.placeholder};
}
p.font = {value: encDef.font.family};
p.fontWeight = {value: encDef.font.weight};
p.fontStyle = {value: encDef.font.style};
p.baseline = {value: encDef.baseline};
return p;
}
properties.text = text;
})(properties = exports.properties || (exports.properties = {}));
//# sourceMappingURL=marks.js.map

@@ -1,359 +0,273 @@

'use strict';
require('../globals');
var util = require('../util'),
time = require('./time'),
colorbrewer = require('colorbrewer'),
interpolate = require('d3-color').interpolateHsl;
var scale = module.exports = {};
scale.names = function(props) {
return util.keys(util.keys(props).reduce(function(a, x) {
if (props[x] && props[x].scale) a[props[x].scale] = 1;
return a;
}, {}));
};
scale.defs = function(names, encoding, layout, stats, facet) {
return names.reduce(function(a, name) {
var scaleDef = {};
scaleDef.name = name;
scaleDef.type = scale.type(name, encoding);
scaleDef.domain = scale.domain(encoding, name, scaleDef.type, facet);
// Add optional properties
var reverse = scale.reverse(encoding, name);
if (reverse) {
scaleDef.reverse = reverse;
var util_1 = require('../util');
var aggregate_1 = require('../aggregate');
var channel_1 = require('../channel');
var data_1 = require('../data');
var time = require('./time');
var type_1 = require('../type');
var mark_1 = require('../mark');
function compileScales(names, model) {
return names.reduce(function (a, channel) {
var scaleDef = {
name: channel,
type: type(channel, model),
};
scaleDef.domain = domain(model, channel, scaleDef.type);
util_1.extend(scaleDef, rangeMixins(model, channel, scaleDef.type));
[
'reverse', 'round',
'clamp', 'nice',
'exponent', 'zero',
'bandWidth', 'outerPadding', 'padding', 'points'
].forEach(function (property) {
var value = exports[property](model, channel, scaleDef.type);
if (value !== undefined) {
scaleDef[property] = value;
}
});
return (a.push(scaleDef), a);
}, []);
}
exports.compileScales = compileScales;
function type(channel, model) {
var fieldDef = model.fieldDef(channel);
switch (fieldDef.type) {
case type_1.NOMINAL:
return 'ordinal';
case type_1.ORDINAL:
var range = fieldDef.scale.range;
return channel === channel_1.COLOR && (typeof range !== 'string') ? 'linear' : 'ordinal';
case type_1.TEMPORAL:
return time.scale.type(fieldDef.timeUnit, channel);
case type_1.QUANTITATIVE:
if (model.bin(channel)) {
return channel === channel_1.ROW || channel === channel_1.COLUMN || channel === channel_1.SHAPE ? 'ordinal' : 'linear';
}
return fieldDef.scale.type;
}
var zero = scale.zero(encoding, name);
if (zero !== undefined) {
scaleDef.zero = zero;
}
exports.type = type;
function domain(model, channel, type) {
var fieldDef = model.fieldDef(channel);
if (fieldDef.scale.domain) {
return fieldDef.scale.domain;
}
// TODO split scale.range into methods for each properties
scaleDef = scale.range(scaleDef, encoding, layout, stats);
return (a.push(scaleDef), a);
}, []);
};
scale.type = function(name, encoding) {
switch (encoding.type(name)) {
case N: //fall through
case O: return 'ordinal';
case T:
var timeUnit = encoding.encDef(name).timeUnit;
return timeUnit ? time.scale.type(timeUnit, name) : 'time';
case Q:
if (encoding.bin(name)) {
return 'linear';
}
return encoding.scale(name).type;
}
};
scale.domain = function (encoding, name, type, facet) {
var encDef = encoding.encDef(name);
// special case for temporal scale
if (encoding.isType(name, T)) {
var range = time.scale.domain(encDef.timeUnit, name);
if (range) return range;
}
// For stack, use STACKED data.
var stack = encoding.stack();
if (stack && name === stack.value) {
return {
data: STACKED,
field: encoding.fieldRef(name, {
// If faceted, scale is determined by the max of sum in each facet.
prefn: (facet ? 'max_' : '') + 'sum_'
})
};
}
var useRawDomain = scale._useRawDomain(encoding, name);
var sort = scale.sort(encoding, name, type);
if (useRawDomain) { // useRawDomain - only Q/T
return {
data: RAW,
field: encoding.fieldRef(name, {noAggregate:true})
};
} else if (encDef.bin) { // bin -- need to merge both bin_start and bin_end
return {
data: encoding.dataTable(),
field: [
encoding.fieldRef(name, {bin_suffix:'_start'}),
encoding.fieldRef(name, {bin_suffix:'_end'})
]
};
} else if (sort) { // have sort -- only for ordinal
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: sort.op ? RAW : encoding.dataTable(),
field: encoding.fieldRef(name),
sort: sort
};
} else {
return {
data: encoding.dataTable(),
field: encoding.fieldRef(name)
};
}
};
scale.sort = function(encoding, name, type) {
var sort = encoding.encDef(name).sort;
if (sort === 'ascending' || sort === 'descending') {
return true;
}
// Sorted based on an aggregate calculation over a specified sort field (only for ordinal scale)
if (type === 'ordinal' && util.isObject(sort)) {
return {
op: sort.op,
field: sort.field
};
}
return undefined;
};
scale.reverse = function(encoding, name) {
var sort = encoding.encDef(name).sort;
return sort && (sort === 'descending' || (sort.order === 'descending'));
};
/**
* Determine if useRawDomain should be activated for this scale.
* @return {Boolean} Returns true if all of the following conditons applies:
* 1. `useRawDomain` is enabled either through scale or config
* 2. Aggregation function is not `count` or `sum`
* 3. The scale is quantitative or time scale.
*/
scale._useRawDomain = function (encoding, name) {
var encDef = encoding.encDef(name);
// scale value
var scaleUseRawDomain = encoding.scale(name).useRawDomain;
// Determine if useRawDomain is enabled. If scale value is specified, use scale value.
// Otherwise, use config value.
var useRawDomainEnabled = scaleUseRawDomain !== undefined ?
scaleUseRawDomain : encoding.config('useRawDomain');
var notCountOrSum = !encDef.aggregate ||
(encDef.aggregate !=='count' && encDef.aggregate !== 'sum');
return useRawDomainEnabled &&
notCountOrSum && (
// Q always uses quantitative scale except when it's binned and thus uses ordinal scale.
(
encoding.isType(name, Q) &&
!encDef.bin // TODO(#614): this must be changed once bin is reimplemented
) ||
// TODO: revise this
// T uses non-ordinal scale when there's no unit or when the unit is not ordinal.
(
encoding.isType(name, T) &&
(!encDef.timeUnit || !time.isOrdinalFn(encDef.timeUnit))
)
);
};
// FIXME revise if we should produce undefined for shorter spec (and just use vega's default value.)
// However, let's ignore it for now as it is unclear what is Vega's default value.
scale.zero = function(encoding, name) {
var spec = encoding.scale(name);
var encDef = encoding.encDef(name);
var timeUnit = encDef.timeUnit;
if (spec.zero) {
return spec.zero; // return explicit value if defined
}
if (encoding.isType(name, T) && (!timeUnit || timeUnit === 'year')) { // FIXME revise this
// Returns false (undefined) by default for time scale
return false;
}
if (encDef.bin) {
// Returns false (undefined) by default of bin
return false;
}
// if not bin / temporal, returns true by default
return name === X || name === Y || name === SIZE;
};
scale.range = function (scaleDef, encoding, layout, stats) {
var encDef = encoding.encDef(scaleDef.name);
var timeUnit = encDef.timeUnit;
switch (scaleDef.name) {
case X:
scaleDef.range = layout.cellWidth ? [0, layout.cellWidth] : 'width';
if (scaleDef.type === 'ordinal') {
scaleDef.bandWidth = encoding.bandSize(X, layout.x.useSmallBand);
}
scaleDef.round = true;
if (scaleDef.type === 'time') {
scaleDef.nice = timeUnit || encoding.config('timeScaleNice');
}else {
scaleDef.nice = true;
}
break;
case Y:
if (scaleDef.type === 'ordinal') {
scaleDef.range = layout.cellHeight ?
(encDef.bin ? [layout.cellHeight, 0] : [0, layout.cellHeight]) :
'height';
scaleDef.bandWidth = encoding.bandSize(Y, layout.y.useSmallBand);
} else {
scaleDef.range = layout.cellHeight ? [layout.cellHeight, 0] : 'height';
}
scaleDef.round = true;
if (scaleDef.type === 'time') {
scaleDef.nice = timeUnit || encoding.config('timeScaleNice');
}else {
scaleDef.nice = true;
}
break;
case ROW: // support only ordinal
scaleDef.bandWidth = layout.cellHeight;
scaleDef.round = true;
scaleDef.nice = true;
break;
case COL: // support only ordinal
scaleDef.bandWidth = layout.cellWidth;
scaleDef.round = true;
scaleDef.nice = true;
break;
case SIZE:
if (encoding.is('bar')) {
// FIXME this is definitely incorrect
// but let's fix it later since bar size is a bad encoding anyway
scaleDef.range = [3, Math.max(encoding.bandSize(X), encoding.bandSize(Y))];
} else if (encoding.is(TEXT)) {
scaleDef.range = [8, 40];
} else { //point
var bandSize = Math.min(encoding.bandSize(X), encoding.bandSize(Y)) - 1;
scaleDef.range = [10, 0.8 * bandSize*bandSize];
}
scaleDef.round = true;
break;
case SHAPE:
scaleDef.range = 'shapes';
break;
case COLOR:
scaleDef.range = scale.color(scaleDef, encoding, stats);
break;
default:
throw new Error('Unknown encoding name: '+ scaleDef.name);
}
// FIXME(kanitw): Jul 29, 2015 - consolidate this with above
switch (scaleDef.name) {
case ROW:
case COL:
scaleDef.padding = encoding.config('cellPadding');
scaleDef.outerPadding = 0;
break;
case X:
case Y:
if (scaleDef.type === 'ordinal') { //&& !s.bandWidth
scaleDef.points = true;
scaleDef.padding = encoding.encDef(scaleDef.name).band.padding;
}
}
return scaleDef;
};
scale.color = function(s, encoding, stats) {
var colorScale = encoding.scale(COLOR),
range = colorScale.range,
cardinality = encoding.cardinality(COLOR, stats),
type = encoding.type(COLOR);
if (range === undefined) {
var ordinalPalette = colorScale.ordinalPalette,
quantitativeRange = colorScale.quantitativeRange;
if (s.type === 'ordinal') {
if (type === N) {
// use categorical color scale
if (cardinality <= 10) {
range = colorScale.c10palette;
} else {
range = colorScale.c20palette;
if (fieldDef.type === type_1.TEMPORAL) {
var range = time.scale.domain(fieldDef.timeUnit, channel);
if (range)
return range;
}
var stack = model.stack();
if (stack && channel === stack.fieldChannel) {
var facet = model.has(channel_1.ROW) || model.has(channel_1.COLUMN);
return {
data: data_1.STACKED,
field: model.field(channel, {
prefn: (facet ? 'max_' : '') + 'sum_'
})
};
}
var useRawDomain = _useRawDomain(model, channel);
var sort = domainSort(model, channel, type);
if (useRawDomain) {
return {
data: data_1.SOURCE,
field: model.field(channel, { noAggregate: true })
};
}
else if (fieldDef.bin) {
return {
data: model.dataTable(),
field: type === 'ordinal' ?
model.field(channel, { binSuffix: '_start' }) :
[
model.field(channel, { binSuffix: '_start' }),
model.field(channel, { binSuffix: '_end' })
]
};
}
else if (sort) {
return {
data: sort.op ? data_1.SOURCE : model.dataTable(),
field: model.field(channel),
sort: sort
};
}
else {
return {
data: model.dataTable(),
field: model.field(channel)
};
}
}
exports.domain = domain;
function domainSort(model, channel, type) {
var sort = model.fieldDef(channel).sort;
if (sort === 'ascending' || sort === 'descending') {
return true;
}
if (type === 'ordinal' && typeof sort !== 'string') {
return {
op: sort.op,
field: sort.field
};
}
return undefined;
}
exports.domainSort = domainSort;
function reverse(model, channel) {
var sort = model.fieldDef(channel).sort;
return sort && (typeof sort === 'string' ?
sort === 'descending' :
sort.order === 'descending') ? true : undefined;
}
exports.reverse = reverse;
function _useRawDomain(model, channel) {
var fieldDef = model.fieldDef(channel);
return fieldDef.scale.useRawDomain &&
fieldDef.aggregate &&
aggregate_1.SHARED_DOMAIN_OPS.indexOf(fieldDef.aggregate) >= 0 &&
((fieldDef.type === type_1.QUANTITATIVE && !fieldDef.bin) ||
(fieldDef.type === type_1.TEMPORAL &&
(!fieldDef.timeUnit || time.scale.type(fieldDef.timeUnit, channel) === 'linear')));
}
exports._useRawDomain = _useRawDomain;
function bandWidth(model, channel, scaleType) {
if (scaleType === 'ordinal') {
return model.fieldDef(channel).scale.bandWidth;
}
return undefined;
}
exports.bandWidth = bandWidth;
function clamp(model, channel) {
return model.fieldDef(channel).scale.clamp;
}
exports.clamp = clamp;
function exponent(model, channel) {
return model.fieldDef(channel).scale.exponent;
}
exports.exponent = exponent;
function nice(model, channel, scaleType) {
if (model.fieldDef(channel).scale.nice !== undefined) {
return model.fieldDef(channel).scale.nice;
}
switch (channel) {
case channel_1.X:
case channel_1.Y:
if (scaleType === 'time' || scaleType === 'ordinal') {
return undefined;
}
return true;
case channel_1.ROW:
case channel_1.COLUMN:
return true;
}
return undefined;
}
exports.nice = nice;
function outerPadding(model, channel, scaleType) {
if (scaleType === 'ordinal') {
if (model.fieldDef(channel).scale.outerPadding !== undefined) {
return model.fieldDef(channel).scale.outerPadding;
}
return scale.color.palette(range, cardinality, type);
} else {
if (ordinalPalette) {
return scale.color.palette(ordinalPalette, cardinality, type);
}
return undefined;
}
exports.outerPadding = outerPadding;
function padding(model, channel, scaleType) {
if (scaleType === 'ordinal') {
return model.fieldDef(channel).scale.padding;
}
return undefined;
}
exports.padding = padding;
function points(model, channel, scaleType) {
if (scaleType === 'ordinal') {
if (model.fieldDef(channel).scale.points !== undefined) {
return model.fieldDef(channel).scale.points;
}
return scale.color.interpolate(quantitativeRange[0], quantitativeRange[1], cardinality);
}
} else { //time or quantitative
return [quantitativeRange[0], quantitativeRange[1]];
switch (channel) {
case channel_1.X:
case channel_1.Y:
return true;
}
}
}
};
scale.color.palette = function(range, cardinality, type) {
// FIXME(kanitw): Jul 29, 2015 - check range is string
switch (range) {
case 'category10k':
// tableau's category 10, ordered by perceptual kernel study results
// https://github.com/uwdata/perceptual-kernels
return ['#2ca02c', '#e377c2', '#7f7f7f', '#17becf', '#8c564b', '#d62728', '#bcbd22', '#9467bd', '#ff7f0e', '#1f77b4'];
// d3/tableau category10/20/20b/20c
case 'category10':
return ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'];
case 'category20':
return ['#1f77b4', '#aec7e8', '#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', '#d62728', '#ff9896', '#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2', '#f7b6d2', '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5'];
case 'category20b':
return ['#393b79', '#5254a3', '#6b6ecf', '#9c9ede', '#637939', '#8ca252', '#b5cf6b', '#cedb9c', '#8c6d31', '#bd9e39', '#e7ba52', '#e7cb94', '#843c39', '#ad494a', '#d6616b', '#e7969c', '#7b4173', '#a55194', '#ce6dbd', '#de9ed6'];
case 'category20c':
return ['#3182bd', '#6baed6', '#9ecae1', '#c6dbef', '#e6550d', '#fd8d3c', '#fdae6b', '#fdd0a2', '#31a354', '#74c476', '#a1d99b', '#c7e9c0', '#756bb1', '#9e9ac8', '#bcbddc', '#dadaeb', '#636363', '#969696', '#bdbdbd', '#d9d9d9'];
}
// TODO add our own set of custom ordinal color palette
if (range in colorbrewer) {
var palette = colorbrewer[range];
// if cardinality pre-defined, use it.
if (cardinality in palette) return palette[cardinality];
// if not, use the highest cardinality one for nominal
if (type === N) {
return palette[Math.max.apply(null, util.keys(palette))];
return undefined;
}
exports.points = points;
function rangeMixins(model, channel, scaleType) {
var fieldDef = model.fieldDef(channel);
if (fieldDef.scale.range) {
return { range: fieldDef.scale.range };
}
// otherwise, interpolate
var ps = cardinality < 3 ? 3 : Math.max.apply(null, util.keys(palette)),
from = 0 , to = ps - 1;
// FIXME add config for from / to
return scale.color.interpolate(palette[ps][from], palette[ps][to], cardinality);
}
return range;
};
scale.color.interpolate = function (start, end, cardinality) {
var interpolator = interpolate(start, end);
return util.range(cardinality).map(function(i) { return interpolator(i*1.0/(cardinality-1)); });
};
switch (channel) {
case channel_1.X:
return { rangeMin: 0, rangeMax: model.layout().cellWidth };
case channel_1.Y:
if (scaleType === 'ordinal') {
return { rangeMin: 0, rangeMax: model.layout().cellHeight };
}
return { rangeMin: model.layout().cellHeight, rangeMax: 0 };
case channel_1.SIZE:
if (model.is(mark_1.BAR)) {
return {
range: [3, Math.max(model.fieldDef(channel_1.X).scale.bandWidth, model.fieldDef(channel_1.Y).scale.bandWidth)]
};
}
else if (model.is(mark_1.TEXT)) {
return { range: [8, 40] };
}
var bandWidth = Math.min(model.fieldDef(channel_1.X).scale.bandWidth, model.fieldDef(channel_1.Y).scale.bandWidth) - 1;
return { range: [10, 0.8 * bandWidth * bandWidth] };
case channel_1.SHAPE:
return { range: 'shapes' };
case channel_1.COLOR:
if (scaleType === 'ordinal') {
return { range: 'category10' };
}
else {
return { range: ['#AFC6A3', '#09622A'] };
}
case channel_1.ROW:
return { range: 'height' };
case channel_1.COLUMN:
return { range: 'width' };
}
return {};
}
exports.rangeMixins = rangeMixins;
function round(model, channel) {
if (model.fieldDef(channel).scale.round !== undefined) {
return model.fieldDef(channel).scale.round;
}
switch (channel) {
case channel_1.X:
case channel_1.Y:
case channel_1.ROW:
case channel_1.COLUMN:
case channel_1.SIZE:
return true;
}
return undefined;
}
exports.round = round;
function zero(model, channel) {
var fieldDef = model.fieldDef(channel);
var timeUnit = fieldDef.timeUnit;
if (fieldDef.scale.zero !== undefined) {
return fieldDef.scale.zero;
}
if (fieldDef.type === type_1.TEMPORAL) {
if (timeUnit === 'year') {
return false;
}
return undefined;
}
if (fieldDef.bin) {
return false;
}
return channel === channel_1.X || channel === channel_1.Y ?
undefined :
false;
}
exports.zero = zero;
//# sourceMappingURL=scale.js.map

@@ -1,58 +0,40 @@

'use strict';
require('../globals');
module.exports = stacking;
function stacking(encoding, mdef, stack) {
var groupby = stack.groupby;
var field = stack.value;
var valName = encoding.fieldRef(field);
var startField = valName + '_start';
var endField = valName + '_end';
var transforms = [];
if (encoding.marktype() === 'area') {
// Add impute transform to ensure we have all values for each series
transforms.push({
type: 'impute',
field: encoding.fieldRef(field),
groupby: [encoding.fieldRef(stack.stack)],
orderby: [encoding.fieldRef(groupby)],
method: 'value',
value: 0
});
}
// add stack transform to mark
var stackTransform = {
type: 'stack',
groupby: [encoding.fieldRef(groupby)],
field: encoding.fieldRef(field),
sortby: [(stack.properties.reverse ? '-' : '') + encoding.fieldRef(stack.stack)],
output: {start: startField, end: endField}
};
if (stack.properties.offset) {
stackTransform.offset = stack.properties.offset;
}
transforms.push(stackTransform)
mdef.from.transform = transforms;
// TODO(#276): This is super hack-ish -- consolidate into modular mark properties?
mdef.properties.update[field] = mdef.properties.enter[field] = {
scale: field,
field: startField
};
mdef.properties.update[field + '2'] = mdef.properties.enter[field + '2'] = {
scale: field,
field: endField
};
return field; //return stack encoding
var util_1 = require('../util');
function imputeTransform(model) {
var stack = model.stack();
return {
type: 'impute',
field: model.field(stack.fieldChannel),
groupby: [model.field(stack.stackChannel)],
orderby: [model.field(stack.groupbyChannel)],
method: 'value',
value: 0
};
}
exports.imputeTransform = imputeTransform;
function stackTransform(model) {
var stack = model.stack();
var sortby = stack.config.sort === 'descending' ?
'-' + model.field(stack.stackChannel) :
stack.config.sort === 'ascending' ?
model.field(stack.stackChannel) :
util_1.isObject(stack.config.sort) ?
stack.config.sort :
'-' + model.field(stack.stackChannel);
var valName = model.field(stack.fieldChannel);
var transform = {
type: 'stack',
groupby: [model.field(stack.groupbyChannel)],
field: model.field(stack.fieldChannel),
sortby: sortby,
output: {
start: valName + '_start',
end: valName + '_end'
}
};
if (stack.config.offset) {
transform.offset = stack.config.offset;
}
return transform;
}
exports.stackTransform = stackTransform;
//# sourceMappingURL=stack.js.map

@@ -1,163 +0,77 @@

'use strict';
var util = require('../util'),
d3_time_format = require('d3-time-format');
var time = module.exports = {};
// 'Wednesday September 17 04:00:00 2014'
// Wednesday is the longest date
// September is the longest month (8 in javascript as it is zero-indexed).
var LONG_DATE = new Date(Date.UTC(2014, 8, 17));
time.cardinality = function(encDef, stats, filterNull, type) {
var timeUnit = encDef.timeUnit;
switch (timeUnit) {
case 'seconds': return 60;
case 'minutes': return 60;
case 'hours': return 24;
case 'day': return 7;
case 'date': return 31;
case 'month': return 12;
case 'year':
var stat = stats[encDef.name],
yearstat = stats['year_' + encDef.name];
if (!yearstat) { return null; }
return yearstat.distinct -
(stat.missing > 0 && filterNull[type] ? 1 : 0);
}
return null;
};
time.formula = function(timeUnit, fieldRef) {
// TODO(kanitw): add formula to other time format
var fn = 'utc' + timeUnit;
return fn + '(' + fieldRef + ')';
};
time.maxLength = function(timeUnit, encoding) {
switch (timeUnit) {
case 'seconds':
case 'minutes':
case 'hours':
case 'date':
return 2;
case 'month':
case 'day':
var range = time.range(timeUnit, encoding);
if (range) {
// return the longest name in the range
return Math.max.apply(null, range.map(function(r) {return r.length;}));
}
return 2;
case 'year':
return 4; //'1998'
}
// TODO(#600) revise this
// no time unit
var timeFormat = encoding.config('timeFormat');
return d3_time_format.utcFormat(timeFormat)(LONG_DATE).length;
};
time.range = function(timeUnit, encoding) {
var labelLength = encoding.config('timeScaleLabelLength'),
scaleLabel;
switch (timeUnit) {
case 'day':
scaleLabel = encoding.config('dayScaleLabel');
break;
case 'month':
scaleLabel = encoding.config('monthScaleLabel');
break;
}
if (scaleLabel) {
return labelLength ? scaleLabel.map(
function(s) { return s.substr(0, labelLength);}
) : scaleLabel;
}
return;
};
/**
* @param {Object} encoding
* @return {Array} scales for time unit names
*/
time.scales = function(encoding) {
var scales = encoding.reduce(function(scales, encDef) {
var timeUnit = encDef.timeUnit;
if (encDef.type === T && timeUnit && !scales[timeUnit]) {
var scale = time.scale.def(encDef.timeUnit, encoding);
if (scale) scales[timeUnit] = scale;
var util = require('../util');
var channel_1 = require('../channel');
function cardinality(fieldDef, stats, filterNull, type) {
var timeUnit = fieldDef.timeUnit;
switch (timeUnit) {
case 'seconds': return 60;
case 'minutes': return 60;
case 'hours': return 24;
case 'day': return 7;
case 'date': return 31;
case 'month': return 12;
case 'year':
var stat = stats[fieldDef.field], yearstat = stats['year_' + fieldDef.field];
if (!yearstat) {
return null;
}
return yearstat.distinct -
(stat.missing > 0 && filterNull[type] ? 1 : 0);
}
return scales;
}, {});
return util.vals(scales);
};
time.scale = {};
/** append custom time scales for axis label */
time.scale.def = function(timeUnit, encoding) {
var range = time.range(timeUnit, encoding);
if (range) {
return {
name: 'time-'+timeUnit,
type: 'ordinal',
domain: time.scale.domain(timeUnit),
range: range
};
}
return null;
};
time.isOrdinalFn = function(timeUnit) {
switch (timeUnit) {
case 'seconds':
case 'minutes':
case 'hours':
case 'day':
case 'date':
case 'month':
return true;
}
return false;
};
time.scale.type = function(timeUnit, name) {
if (name === COLOR) {
return 'linear'; // time has order, so use interpolated ordinal color scale.
}
// FIXME revise this -- should 'year' be linear too?
return time.isOrdinalFn(timeUnit) || name === COL || name === ROW ? 'ordinal' : 'linear';
};
time.scale.domain = function(timeUnit, name) {
var isColor = name === COLOR;
switch (timeUnit) {
case 'seconds':
case 'minutes': return isColor ? [0,59] : util.range(0, 60);
case 'hours': return isColor ? [0,23] : util.range(0, 24);
case 'day': return isColor ? [0,6] : util.range(0, 7);
case 'date': return isColor ? [1,31] : util.range(1, 32);
case 'month': return isColor ? [0,11] : util.range(0, 12);
}
return null;
};
/** whether a particular time function has custom scale for labels implemented in time.scale */
time.hasScale = function(timeUnit) {
switch (timeUnit) {
case 'day':
case 'month':
return true;
}
return false;
};
return null;
}
exports.cardinality = cardinality;
function formula(timeUnit, field) {
var fn = 'utc' + timeUnit;
return fn + '(' + field + ')';
}
exports.formula = formula;
var scale;
(function (scale) {
function type(timeUnit, channel) {
if (channel === channel_1.COLOR) {
return 'linear';
}
if (channel === channel_1.COLUMN || channel === channel_1.ROW) {
return 'ordinal';
}
switch (timeUnit) {
case 'hours':
case 'day':
case 'date':
case 'month':
return 'ordinal';
case 'year':
case 'second':
case 'minute':
return 'linear';
}
return 'time';
}
scale.type = type;
function domain(timeUnit, channel) {
var isColor = channel === channel_1.COLOR;
switch (timeUnit) {
case 'seconds':
case 'minutes': return isColor ? [0, 59] : util.range(0, 60);
case 'hours': return isColor ? [0, 23] : util.range(0, 24);
case 'day': return isColor ? [0, 6] : util.range(0, 7);
case 'date': return isColor ? [1, 31] : util.range(1, 32);
case 'month': return isColor ? [0, 11] : util.range(0, 12);
}
return null;
}
scale.domain = domain;
})(scale = exports.scale || (exports.scale = {}));
function labelTemplate(timeUnit, abbreviated) {
if (abbreviated === void 0) { abbreviated = false; }
var postfix = abbreviated ? '-abbrev' : '';
switch (timeUnit) {
case 'day':
return 'day' + postfix;
case 'month':
return 'month' + postfix;
}
return null;
}
exports.labelTemplate = labelTemplate;
//# sourceMappingURL=time.js.map

@@ -1,30 +0,13 @@

'use strict';
require('./globals');
var stats = require('datalib/src/stats');
var vldata = module.exports = {};
/** Mapping from datalib's inferred type to Vega-lite's type */
vldata.types = {
'boolean': N,
'number': Q,
'integer': Q,
'date': T,
'string': N
var type_1 = require('./type');
exports.SUMMARY = 'summary';
exports.SOURCE = 'source';
exports.STACKED = 'stacked';
exports.LAYOUT = 'layout';
exports.types = {
'boolean': type_1.NOMINAL,
'number': type_1.QUANTITATIVE,
'integer': type_1.QUANTITATIVE,
'date': type_1.TEMPORAL,
'string': type_1.NOMINAL
};
vldata.stats = function(data) {
var summary = stats.summary(data);
return summary.reduce(function(s, profile) {
s[profile.field] = profile;
return s;
}, {
'*': {
max: data.length,
min: 0
}
});
};
//# sourceMappingURL=data.js.map

@@ -1,840 +0,32 @@

// Package of defining Vega-lite Specification's json schema
'use strict';
require('../globals');
var schema = module.exports = {},
util = require('../util'),
toMap = util.toMap,
colorbrewer = require('colorbrewer');
var VALID_AGG_OPS = require('vega/src/transforms/Aggregate').VALID_OPS;
// TODO(#620) refer to vega schema
// var vgStackSchema = require('vega/src/transforms/Stack').schema;
schema.util = require('./schemautil');
schema.marktype = {
type: 'string',
enum: ['point', 'tick', 'bar', 'line', 'area', 'circle', 'square', 'text']
};
schema.aggregate = {
type: 'string',
enum: VALID_AGG_OPS,
supportedEnums: {
Q: VALID_AGG_OPS,
O: ['median','min','max'],
N: [],
T: ['mean', 'median', 'min', 'max'],
'': ['count']
},
supportedTypes: toMap([Q, N, O, T, ''])
};
schema.getSupportedRole = function(encType) {
return schema.schema.properties.encoding.properties[encType].supportedRole;
};
schema.timeUnits = ['year', 'month', 'day', 'date', 'hours', 'minutes', 'seconds'];
schema.defaultTimeFn = 'month';
schema.timeUnit = {
type: 'string',
enum: schema.timeUnits,
supportedTypes: toMap([T])
};
schema.scale_type = {
type: 'string',
// TODO(kanitw) read vega's schema here, add description
enum: ['linear', 'log', 'pow', 'sqrt', 'quantile'],
default: 'linear',
supportedTypes: toMap([Q])
};
schema.field = {
type: 'object',
properties: {
name: {
type: 'string'
}
}
};
var clone = util.duplicate;
var merge = schema.util.merge;
schema.MAXBINS_DEFAULT = 15;
var bin = {
type: ['boolean', 'object'],
default: false,
properties: {
maxbins: {
type: 'integer',
default: schema.MAXBINS_DEFAULT,
minimum: 2,
description: 'Maximum number of bins.'
}
},
supportedTypes: toMap([Q]) // TODO: add O after finishing #81
};
var typicalField = merge(clone(schema.field), {
type: 'object',
properties: {
type: {
type: 'string',
enum: [N, O, Q, T]
},
aggregate: schema.aggregate,
timeUnit: schema.timeUnit,
bin: bin,
scale: {
type: 'object',
properties: {
/* Common Scale Properties */
type: schema.scale_type,
/* Quantitative Scale Properties */
nice: {
type: 'string',
enum: ['second', 'minute', 'hour', 'day', 'week', 'month', 'year'],
supportedTypes: toMap([T])
var schemaUtil = require('./schemautil');
var mark_schema_1 = require('./mark.schema');
var config_schema_1 = require('./config.schema');
var data_schema_1 = require('./data.schema');
var encoding_schema_1 = require('./encoding.schema');
var fielddef_schema_1 = require('./fielddef.schema');
exports.aggregate = fielddef_schema_1.aggregate;
exports.util = schemaUtil;
exports.schema = {
$schema: 'http://json-schema.org/draft-04/schema#',
description: 'Schema for Vega-lite specification',
type: 'object',
required: ['mark', 'encoding'],
properties: {
name: {
type: 'string'
},
zero: {
type: 'boolean',
description: 'Include zero',
default: undefined,
supportedTypes: toMap([Q, T])
description: {
type: 'string'
},
/* Vega-lite only Properties */
useRawDomain: {
type: 'boolean',
default: undefined,
description: 'Use the raw data range as scale domain instead of ' +
'aggregated data for aggregate axis. ' +
'This option does not work with sum or count aggregate' +
'as they might have a substantially larger scale range.' +
'By default, use value from config.useRawDomain.'
}
}
data: data_schema_1.data,
mark: mark_schema_1.mark,
encoding: encoding_schema_1.encoding,
config: config_schema_1.config
}
}
});
var onlyOrdinalField = merge(clone(schema.field), {
type: 'object',
supportedRole: {
dimension: true
},
properties: {
type: {
type: 'string',
enum: [N, O, Q, T] // ordinal-only field supports Q when bin is applied and T when time unit is applied.
},
timeUnit: schema.timeUnit,
bin: bin,
aggregate: {
type: 'string',
enum: ['count'],
supportedTypes: toMap([N, O]) // FIXME this looks weird to me
}
}
});
var axisMixin = {
type: 'object',
supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true},
properties: {
axis: {
type: 'object',
properties: {
/* Vega Axis Properties */
format: {
type: 'string',
default: undefined, // auto
description: 'The formatting pattern for axis labels. '+
'If not undefined, this will be determined by ' +
'small/largeNumberFormat and the max value ' +
'of the field.'
},
grid: {
type: 'boolean',
default: undefined,
description: 'A flag indicate if gridlines should be created in addition to ticks. If `grid` is unspecified, the default value is `true` for ROW and COL. For X and Y, the default value is `true` for quantitative and time fields and `false` otherwise.'
},
layer: {
type: 'string',
default: 'back',
description: 'A string indicating if the axis (and any gridlines) should be placed above or below the data marks. One of "front" (default) or "back".'
},
orient: {
type: 'string',
default: undefined,
enum: ['top', 'right', 'left', 'bottom'],
description: 'The orientation of the axis. One of top, bottom, left or right. The orientation can be used to further specialize the axis type (e.g., a y axis oriented for the right edge of the chart).'
},
ticks: {
type: 'integer',
default: 5,
minimum: 0,
description: 'A desired number of ticks, for axes visualizing quantitative scales. The resulting number may be different so that values are "nice" (multiples of 2, 5, 10) and lie within the underlying scale\'s range.'
},
/* Vega Axis Properties that are automatically populated by Vega-lite */
title: {
type: 'string',
default: undefined,
description: 'A title for the axis. (Shows field name and its function by default.)'
},
/* Vega-lite only */
maxLabelLength: {
type: 'integer',
default: 25,
minimum: 0,
description: 'Truncate labels that are too long.'
},
labelAngle: {
type: 'integer',
default: undefined, // auto
minimum: 0,
maximum: 360,
description: 'Angle by which to rotate labels. Set to 0 to force horizontal.'
},
titleMaxLength: {
type: 'integer',
default: undefined,
minimum: 0,
description: 'Max length for axis title if the title is automatically generated from the field\'s description'
},
titleOffset: {
type: 'integer',
default: undefined, // auto
description: 'A title offset value for the axis.'
},
}
}
}
};
var sortMixin = {
type: 'object',
properties: {
sort: {
default: 'ascending',
supportedTypes: toMap([N, O]),
oneOf: [
{
type: 'string',
enum: ['ascending', 'descending', 'unsorted']
},
{ // sort by aggregation of another field
type: 'object',
required: ['field', 'op'],
properties: {
field: {
type: 'string',
description: 'The field name to aggregate over.'
},
op: {
type: 'string',
enum: VALID_AGG_OPS,
description: 'The field name to aggregate over.'
},
order: {
type: 'string',
enum: ['ascending', 'descending']
}
}
}
]
}
}
};
var bandMixin = {
type: 'object',
properties: {
band: {
type: 'object',
properties: {
size: {
type: 'integer',
minimum: 0,
default: undefined
},
padding: {
type: 'integer',
minimum: 0,
default: 1
}
}
}
}
};
var legendMixin = {
type: 'object',
properties: {
legend: {
type: 'object',
description: 'Properties of a legend.',
properties: {
title: {
type: 'string',
default: undefined,
description: 'A title for the legend. (Shows field name and its function by default.)'
},
orient: {
type: 'string',
default: 'right',
description: 'The orientation of the legend. One of "left" or "right". This determines how the legend is positioned within the scene. The default is "right".'
}
}
}
}
};
var textMixin = {
type: 'object',
supportedMarktypes: {'text': true},
properties: {
align: {
type: 'string',
default: 'right'
},
baseline: {
type: 'string',
default: 'middle'
},
color: {
type: 'string',
role: 'color',
default: '#000000'
},
margin: {
type: 'integer',
default: 4,
minimum: 0
},
placeholder: {
type: 'string',
default: 'Abc'
},
font: {
type: 'object',
properties: {
weight: {
type: 'string',
enum: ['normal', 'bold'],
default: 'normal'
},
size: {
type: 'integer',
default: 10,
minimum: 0
},
family: {
type: 'string',
default: 'Helvetica Neue'
},
style: {
type: 'string',
default: 'normal',
enum: ['normal', 'italic']
}
}
},
format: {
type: 'string',
default: undefined, // auto
description: 'The formatting pattern for text value. '+
'If not undefined, this will be determined by ' +
'small/largeNumberFormat and the max value ' +
'of the field.'
},
}
};
var sizeMixin = {
type: 'object',
supportedMarktypes: {point: true, bar: true, circle: true, square: true, text: true},
properties: {
value: {
type: 'integer',
default: 30,
minimum: 0,
description: 'Size of marks.'
}
}
};
var colorMixin = {
type: 'object',
supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true, 'text': true},
properties: {
value: {
type: 'string',
role: 'color',
default: '#4682b4',
description: 'Color to be used for marks.'
},
opacity: {
type: 'number',
default: undefined, // auto
minimum: 0,
maximum: 1
},
scale: {
type: 'object',
properties: {
range: {
type: ['string', 'array'],
default: undefined,
description:
'Color palette, if undefined vega-lite will use data property' +
'to pick one from c10palette, c20palette, or ordinalPalette.'
//FIXME
},
c10palette: {
type: 'string',
default: 'category10',
enum: [
// Tableau
'category10', 'category10k',
// Color Brewer
'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3'
]
},
c20palette: {
type: 'string',
default: 'category20',
enum: ['category20', 'category20b', 'category20c']
},
ordinalPalette: {
type: 'string',
default: undefined,
description: 'Color palette to encode ordinal variables.',
enum: util.keys(colorbrewer)
},
quantitativeRange: {
type: 'array',
default: ['#AFC6A3', '#09622A'], // tableau greens
// default: ['#ccece6', '#00441b'], // BuGn.9 [2-8]
description: 'Color range to encode quantitative variables.',
minItems: 2,
maxItems: 2,
items: {
type: 'string',
role: 'color'
}
}
}
}
}
};
var stackMixin = {
type: 'object',
properties: {
stack: {
type: ['boolean', 'object'],
default: true,
description: 'Enable stacking (for bar and area marks only).',
properties: {
reverse: {
type: 'boolean',
default: false,
description: 'Whether to reverse the stack\'s sortby.'
},
offset: {
type: 'string',
default: undefined,
enum: ['zero', 'center', 'normalize']
// TODO(#620) refer to Vega spec once it doesn't throw error
// enum: vgStackSchema.properties.offset.oneOf[0].enum
}
}
}
}
};
var shapeMixin = {
type: 'object',
supportedMarktypes: {point: true, circle: true, square: true},
properties: {
value: {
type: 'string',
enum: ['circle', 'square', 'cross', 'diamond', 'triangle-up', 'triangle-down'],
default: 'circle',
description: 'Mark to be used.'
},
filled: {
type: 'boolean',
default: false,
description: 'Whether the shape\'s color should be used as fill color instead of stroke color.'
}
}
};
var detailMixin = {
type: 'object',
supportedMarktypes: {point: true, tick: true, line: true, circle: true, square: true}
};
var rowMixin = {
properties: {
height: {
type: 'number',
minimum: 0,
default: 150
}
}
};
var colMixin = {
properties: {
width: {
type: 'number',
minimum: 0,
default: 150
},
axis: {
properties: {
maxLabelLength: {
type: 'integer',
default: 12,
minimum: 0,
description: 'Truncate labels that are too long.'
}
}
}
}
};
var facetMixin = {
type: 'object',
supportedMarktypes: {point: true, tick: true, bar: true, line: true, area: true, circle: true, square: true, text: true},
properties: {
padding: {
type: 'number',
minimum: 0,
maximum: 1,
default: 0.1
}
}
};
var requiredNameType = {
required: ['name', 'type']
};
var multiRoleField = merge(clone(typicalField), {
supportedRole: {
measure: true,
dimension: true
}
});
var quantitativeField = merge(clone(typicalField), {
supportedRole: {
measure: true,
dimension: 'ordinal-only' // using size to encoding category lead to order interpretation
}
});
var onlyQuantitativeField = merge(clone(typicalField), {
supportedRole: {
measure: true
}
});
var x = merge(clone(multiRoleField), axisMixin, bandMixin, requiredNameType, sortMixin);
var y = clone(x);
var facet = merge(clone(onlyOrdinalField), requiredNameType, facetMixin, sortMixin);
var row = merge(clone(facet), axisMixin, rowMixin);
var col = merge(clone(facet), axisMixin, colMixin);
var size = merge(clone(quantitativeField), legendMixin, sizeMixin, sortMixin);
var color = merge(clone(multiRoleField), legendMixin, colorMixin, stackMixin, sortMixin);
var shape = merge(clone(onlyOrdinalField), legendMixin, shapeMixin, sortMixin);
var detail = merge(clone(onlyOrdinalField), detailMixin, stackMixin, sortMixin);
// we only put aggregated measure in pivot table
var text = merge(clone(onlyQuantitativeField), textMixin, sortMixin);
// TODO add label
var data = {
type: 'object',
properties: {
// data source
formatType: {
type: 'string',
enum: ['json', 'csv'],
default: 'json'
},
url: {
type: 'string',
default: undefined
},
values: {
type: 'array',
default: undefined,
description: 'Pass array of objects instead of a url to a file.',
items: {
type: 'object',
additionalProperties: true
}
},
// we generate a vega filter transform
filter: {
type: 'string',
default: undefined,
description: 'A string containing the filter Vega expression. Use `datum` to refer to the current data object.'
},
// we generate a vega formula transform
formulas: {
type: 'array',
default: undefined,
description: 'Array of formula transforms. Formulas are applied before filter.',
items: {
type: 'object',
properties: {
field: {
type: 'string',
description: 'The property name in which to store the computed formula value.'
},
expr: {
type: 'string',
description: 'A string containing an expression for the formula. Use the variable `datum` to to refer to the current data object.'
}
}
}
}
}
};
var config = {
type: 'object',
properties: {
// template
width: {
type: 'integer',
default: undefined
},
height: {
type: 'integer',
default: undefined
},
viewport: {
type: 'array',
items: {
type: 'integer'
},
default: undefined
},
gridColor: {
type: 'string',
role: 'color',
default: '#000000'
},
gridOpacity: {
type: 'number',
minimum: 0,
maximum: 1,
default: 0.08
},
// filter null
// TODO(#597) revise this config
filterNull: {
type: 'object',
properties: {
N: {type:'boolean', default: false},
O: {type:'boolean', default: false},
Q: {type:'boolean', default: true},
T: {type:'boolean', default: true}
}
},
autoSortLine: {
type: 'boolean',
default: true
},
// single plot
singleHeight: {
// will be overwritten by bandWidth * (cardinality + padding)
type: 'integer',
default: 200,
minimum: 0
},
singleWidth: {
// will be overwritten by bandWidth * (cardinality + padding)
type: 'integer',
default: 200,
minimum: 0
},
// band size
largeBandSize: {
type: 'integer',
default: 21,
minimum: 0
},
smallBandSize: {
//small multiples or single plot with high cardinality
type: 'integer',
default: 12,
minimum: 0
},
largeBandMaxCardinality: {
type: 'integer',
default: 10
},
// small multiples
cellPadding: {
type: 'number',
default: 0.1
},
cellGridColor: {
type: 'string',
role: 'color',
default: '#000000'
},
cellGridOpacity: {
type: 'number',
minimum: 0,
maximum: 1,
default: 0.25
},
cellGridOffset: {
type: 'number',
default: 6 // equal to tickSize
},
cellBackgroundColor: {
type: 'string',
role: 'color',
default: 'rgba(0,0,0,0)'
},
textCellWidth: {
type: 'integer',
default: 90,
minimum: 0
},
// marks
strokeWidth: {
type: 'integer',
default: 2,
minimum: 0
},
singleBarOffset: {
type: 'integer',
default: 5,
minimum: 0
},
// scales
timeScaleLabelLength: {
type: 'integer',
default: 3,
minimum: 0,
description: 'Max length for values in dayScaleLabel and monthScaleLabel. Zero means using full names in dayScaleLabel/monthScaleLabel.'
},
dayScaleLabel: {
type: 'array',
items: {
type: 'string'
},
default: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
description: 'Axis labels for day of week, starting from Sunday.' +
'(Consistent with Javascript -- See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDay.'
},
monthScaleLabel: {
type: 'array',
items: {
type: 'string'
},
default: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
description: 'Axis labels for month.'
},
// other
characterWidth: {
type: 'integer',
default: 6
},
maxSmallNumber: {
type: 'number',
default: 10000,
description: 'maximum number that a field will be considered smallNumber.'+
'Used for axis labelling.'
},
smallNumberFormat: {
type: 'string',
default: '',
description: 'D3 Number format for axis labels and text tables '+
'for number <= maxSmallNumber. Used for axis labelling.'
},
largeNumberFormat: {
type: 'string',
default: '.3s',
description: 'D3 Number format for axis labels and text tables ' +
'for number > maxSmallNumber.'
},
timeFormat: {
type: 'string',
default: '%Y-%m-%d',
description: 'Date format for axis labels.'
},
useRawDomain: {
type: 'boolean',
default: false,
description: 'Use the raw data range as scale domain instead of ' +
'aggregated data for aggregate axis. ' +
'This option does not work with sum or count aggregate' +
'as they might have a substantially larger scale range.' +
'By default, use value from config.useRawDomain.'
}
}
};
/** @type Object Schema of a vega-lite specification */
schema.schema = {
$schema: 'http://json-schema.org/draft-04/schema#',
description: 'Schema for Vega-lite specification',
type: 'object',
required: ['marktype', 'encoding', 'data'],
properties: {
data: data,
marktype: schema.marktype,
encoding: {
type: 'object',
properties: {
x: x,
y: y,
row: row,
col: col,
size: size,
color: color,
shape: shape,
text: text,
detail: detail
}
},
config: config
}
};
schema.encTypes = util.keys(schema.schema.properties.encoding.properties);
/** Instantiate a verbose vl spec from the schema */
schema.instantiate = function() {
return schema.util.instantiate(schema.schema);
};
function instantiate() {
return schemaUtil.instantiate(exports.schema);
}
exports.instantiate = instantiate;
;
//# sourceMappingURL=schema.js.map

@@ -1,6 +0,3 @@

'use strict';
var schema = require('./schema').schema,
json3 = require('../../lib/json3-compactstringify.js');
process.stdout.write(json3.stringify(schema, null, 1, 80) + '\n');
var schema_1 = require('./schema');
process.stdout.write(JSON.stringify(schema_1.schema, null, 4) + '\n');
//# sourceMappingURL=schemagen.js.map

@@ -1,87 +0,110 @@

'use strict';
var schemautil = module.exports = {},
util = require('../util');
var isEmpty = function(obj) {
return Object.keys(obj).length === 0;
};
schemautil.extend = function(instance, schema) {
return schemautil.merge(schemautil.instantiate(schema), instance);
};
// instantiate a schema
schemautil.instantiate = function(schema) {
var val;
if (schema === undefined) {
var util = require('../util');
function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
;
function extend(instance, schema) {
return merge(instantiate(schema), instance);
}
exports.extend = extend;
;
function instantiate(schema) {
var val;
if (schema === undefined) {
return undefined;
}
else if ('default' in schema) {
val = schema.default;
return util.isObject(val) ? util.duplicate(val) : val;
}
else if (schema.type === 'object') {
var instance = {};
for (var name in schema.properties) {
val = instantiate(schema.properties[name]);
if (val !== undefined) {
instance[name] = val;
}
}
return instance;
}
else if (schema.type === 'array') {
return undefined;
}
return undefined;
} else if ('default' in schema) {
val = schema.default;
return util.isObject(val) ? util.duplicate(val) : val;
} else if (schema.type === 'object') {
var instance = {};
for (var name in schema.properties) {
val = schemautil.instantiate(schema.properties[name]);
if (val !== undefined) {
instance[name] = val;
}
}
exports.instantiate = instantiate;
;
function subtract(instance, defaults) {
var changes = {};
for (var prop in instance) {
var def = defaults[prop];
var ins = instance[prop];
if (!defaults || def !== ins) {
if (typeof ins === 'object' && !util.isArray(ins) && def) {
var c = subtract(ins, def);
if (!isEmpty(c)) {
changes[prop] = c;
}
}
else if (util.isArray(ins)) {
if (util.isArray(def)) {
if (ins.length === def.length) {
var equal = true;
for (var i = 0; i < ins.length; i++) {
if (ins[i] !== def[i]) {
equal = false;
break;
}
}
if (equal) {
continue;
}
}
}
changes[prop] = ins;
}
else {
changes[prop] = ins;
}
}
}
return instance;
} else if (schema.type === 'array') {
return [];
}
return undefined;
};
// remove all defaults from an instance
schemautil.subtract = function(instance, defaults) {
var changes = {};
for (var prop in instance) {
var def = defaults[prop];
var ins = instance[prop];
// Note: does not properly subtract arrays
if (!defaults || def !== ins) {
if (typeof ins === 'object' && !util.isArray(ins) && def) {
var c = schemautil.subtract(ins, def);
if (!isEmpty(c))
changes[prop] = c;
} else if (!util.isArray(ins) || ins.length > 0) {
changes[prop] = ins;
}
return changes;
}
exports.subtract = subtract;
;
function merge(dest) {
var src = [];
for (var _i = 1; _i < arguments.length; _i++) {
src[_i - 1] = arguments[_i];
}
}
return changes;
};
schemautil.merge = function(/*dest*, src0, src1, ...*/){
var dest = arguments[0];
for (var i=1 ; i<arguments.length; i++) {
dest = merge(dest, arguments[i]);
}
return dest;
};
// recursively merges src into dest
function merge(dest, src) {
if (typeof src !== 'object' || src === null) {
for (var i = 0; i < src.length; i++) {
dest = merge_(dest, src[i]);
}
return dest;
}
for (var p in src) {
if (!src.hasOwnProperty(p)) {
continue;
}
exports.merge = merge;
;
function merge_(dest, src) {
if (typeof src !== 'object' || src === null) {
return dest;
}
if (src[p] === undefined) {
continue;
for (var p in src) {
if (!src.hasOwnProperty(p)) {
continue;
}
if (src[p] === undefined) {
continue;
}
if (typeof src[p] !== 'object' || src[p] === null) {
dest[p] = src[p];
}
else if (typeof dest[p] !== 'object' || dest[p] === null) {
dest[p] = merge(src[p].constructor === Array ? [] : {}, src[p]);
}
else {
merge(dest[p], src[p]);
}
}
if (typeof src[p] !== 'object' || src[p] === null) {
dest[p] = src[p];
} else if (typeof dest[p] !== 'object' || dest[p] === null) {
dest[p] = merge(src[p].constructor === Array ? [] : {}, src[p]);
} else {
merge(dest[p], src[p]);
}
}
return dest;
}
return dest;
}
//# sourceMappingURL=schemautil.js.map

@@ -1,102 +0,78 @@

'use strict';
var util = module.exports = require('datalib/src/util');
util.extend(util, require('datalib/src/generate'));
util.extend(util, require('datalib/src/stats'));
util.extend(util, require('./logger')('[VL Error]'));
util.bin = require('datalib/src/bins/bins');
util.isin = function(item, array) {
return array.indexOf(item) !== -1;
};
util.forEach = function(obj, f, thisArg) {
if (obj.forEach) {
obj.forEach.call(thisArg, f);
} else {
for (var k in obj) {
f.call(thisArg, obj[k], k , obj);
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require('datalib/src/util'));
__export(require('datalib/src/generate'));
__export(require('datalib/src/stats'));
function contains(array, item) {
return array.indexOf(item) > -1;
}
exports.contains = contains;
function forEach(obj, f, thisArg) {
if (obj.forEach) {
obj.forEach.call(thisArg, f);
}
}
};
util.reduce = function(obj, f, init, thisArg) {
if (obj.reduce) {
return obj.reduce.call(thisArg, f, init);
} else {
for (var k in obj) {
init = f.call(thisArg, init, obj[k], k, obj);
else {
for (var k in obj) {
f.call(thisArg, obj[k], k, obj);
}
}
return init;
}
};
util.map = function(obj, f, thisArg) {
if (obj.map) {
return obj.map.call(thisArg, f);
} else {
var output = [];
for (var k in obj) {
output.push( f.call(thisArg, obj[k], k, obj));
}
exports.forEach = forEach;
function reduce(obj, f, init, thisArg) {
if (obj.reduce) {
return obj.reduce.call(thisArg, f, init);
}
}
};
util.any = function(arr, f) {
var i = 0, k;
for (k in arr) {
if (f(arr[k], k, i++)) return true;
}
return false;
};
util.all = function(arr, f) {
var i = 0, k;
for (k in arr) {
if (!f(arr[k], k, i++)) return false;
}
return true;
};
util.getbins = function(stats, maxbins) {
return util.bin({
min: stats.min,
max: stats.max,
maxbins: maxbins
});
};
/**
* x[p[0]]...[p[n]] = val
* @param noaugment determine whether new object should be added f
* or non-existing properties along the path
*/
util.setter = function(x, p, val, noaugment) {
for (var i=0; i<p.length-1; ++i) {
if (!noaugment && !(p[i] in x)){
x = x[p[i]] = {};
} else {
x = x[p[i]];
else {
for (var k in obj) {
init = f.call(thisArg, init, obj[k], k, obj);
}
return init;
}
}
x[p[i]] = val;
};
/**
* returns x[p[0]]...[p[n]]
* @param augment determine whether new object should be added f
* or non-existing properties along the path
*/
util.getter = function(x, p, noaugment) {
for (var i=0; i<p.length; ++i) {
if (!noaugment && !(p[i] in x)){
x = x[p[i]] = {};
} else {
x = x[p[i]];
}
exports.reduce = reduce;
function map(obj, f, thisArg) {
if (obj.map) {
return obj.map.call(thisArg, f);
}
}
return x;
};
else {
var output = [];
for (var k in obj) {
output.push(f.call(thisArg, obj[k], k, obj));
}
return output;
}
}
exports.map = map;
function any(arr, f) {
var i = 0, k;
for (k in arr) {
if (f(arr[k], k, i++))
return true;
}
return false;
}
exports.any = any;
function all(arr, f) {
var i = 0, k;
for (k in arr) {
if (!f(arr[k], k, i++))
return false;
}
return true;
}
exports.all = all;
var dlBin = require('datalib/src/bins/bins');
function getbins(stats, maxbins) {
return dlBin({
min: stats.min,
max: stats.max,
maxbins: maxbins
});
}
exports.getbins = getbins;
function error(message) {
console.error('[VL Error]', message);
}
exports.error = error;
//# sourceMappingURL=util.js.map

@@ -1,22 +0,29 @@

'use strict';
require('./globals');
var util = require('./util'),
consts = require('./consts');
var vl = {};
util.extend(vl, consts, util);
vl.Encoding = require('./Encoding');
vl.compiler = require('./compiler/compiler');
vl.compile = vl.compiler.compile;
vl.data = require('./data');
vl.enc = require('./enc');
vl.encDef = require('./encdef');
vl.schema = require('./schema/schema');
vl.toShorthand = vl.Encoding.shorthand;
vl.format = require('d3-format').format;
module.exports = vl;
var vlBin = require('./bin');
var vlChannel = require('./channel');
var vlData = require('./data');
var vlEncoding = require('./encoding');
var vlFieldDef = require('./fielddef');
var vlCompiler = require('./compiler/compiler');
var vlSchema = require('./schema/schema');
var vlShorthand = require('./shorthand');
var vlSpec = require('./spec');
var vlTimeUnit = require('./timeunit');
var vlType = require('./type');
var vlValidate = require('./validate');
var vlUtil = require('./util');
exports.bin = vlBin;
exports.channel = vlChannel;
exports.compiler = vlCompiler;
exports.compile = vlCompiler.compile;
exports.data = vlData;
exports.encoding = vlEncoding;
exports.fieldDef = vlFieldDef;
exports.schema = vlSchema;
exports.shorthand = vlShorthand;
exports.spec = vlSpec;
exports.timeUnit = vlTimeUnit;
exports.type = vlType;
exports.util = vlUtil;
exports.validate = vlValidate;
exports.version = '__VERSION__';
//# sourceMappingURL=vl.js.map

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc