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 1.0.8 to 1.0.9

.editorconfig

64

CONTRIBUTING.md

@@ -7,47 +7,5 @@ # Contributing

## Creating an Issue
- For small fixes, please feel free to submit a pull request. No worry about creating an issue first.
Before creating an issue, please browse through the
[issue list](https://github.com/vega/vega-lite/issues) to avoid duplicates.
A good issue shouldn't leave others needing to chase you up for more information.
Here are properties of a good issue:
- __Use clear and descriptive title__ for the issue
- __Describe how to reproduce the issue__ If possible,
please provide an example Vega-Lite specification for reproducing the issue.
- __Provide screenshots/animated GIFs or describe the behavior you observed
after following the steps__ and point out what exactly is the problem with that behavior.
[Skitch](https://evernote.com/skitch) is a useful tool for capturing screenshots.
Github's issue tracker also supports drag-and-drop image upload.
- __Explain which behavior you expected to see instead and why.__
## Submitting a Pull Request
You can contribute to our codebase by submitting
[a pull request](https://help.github.com/articles/using-pull-requests/).
Here are some guides:
- Search GitHub for a related pull request. You don't want to duplicate effort.
- Before submitting a pull request:
- All lint and test should pass.
- Run `npm run lint` and `npm run test`.
- Update the documentation under `site/docs/` to reflect the changes.
- Make sure you have merged `master` into your branch. If you are not a git command line ninja, we recommend [SourceTree](https://www.sourcetreeapp.com/).
- Provide a concise description for the pull request so that we can copy the description and simply paste in [our release note](https://github.com/vega/vega-lite/releases). When writing description for a pull request or a commit, please:
- Use imperative mood and present tense ([Why?](http://stackoverflow.com/questions/13861318/why-is-it-considered-good-practice-to-describe-git-commits-in-the-present-tense)).
- Mention relevant issues using github's # syntax. (e.g., `#1` for mentioning issue #1)
- Focus on _what_ and _why_ rather than _how_
- See more [tips about git commit](http://chris.beams.io/posts/git-commit/).
- For small fixes, please feel free to submit a pull request
with appropriate test cases or example specs the demonstrate the use case.
No worry about creating an issue first.
- For major changes, please discuss with us via [our mailing list] and Github first,
- For major changes, please discuss with us via [our mailing list](https://groups.google.com/forum/#!forum/vega-js) and Github first,
so we can better coordinate our efforts, prevent duplication of work,

@@ -59,3 +17,3 @@ and help you to craft the change so that it is successfully accepted into the project.

- You can refer to related issue by adding #<issue-no> to the pull request's description.
See our [issue](.github/ISSUE_TEMPLATE.md) and [pull request](.github/PULL_REQUEST_TEMPLATE.md) templates for more information.

@@ -76,3 +34,3 @@ ### Looking for a Task to Contribute

The images that are shown on the homepage and in the gallery have to be generated with `npm run images`.
To run the script, you need to install [gnu parallel](https://www.gnu.org/software/parallel/). (For Mac, you can simply do `brew install parallel`.)
To run the script, you need to install [gnu parallel](https://www.gnu.org/software/parallel/). (For Mac, you can simply do `brew install parallel`.)

@@ -122,3 +80,3 @@ Since we only publish the Github Pages when we release a new version,

- `examples/docs/` contains examples that are used in the documetnation, but should not be shown in the gallery.
- `examples/vl-examples.json` lists all examples under `examples/`. Similarly, `examples/docs/vl-docs-examples.json` lists all examples under `examples/docs`.
- `examples/vl-examples.json` lists all examples under `examples/`. Similarly, `examples/docs/vl-docs-examples.json` lists all examples under `examples/docs`.

@@ -137,3 +95,3 @@ - `lib/` contains JSON schema's `schema.json`

- `test/` - Code for unit testing. `test`'s structure reflects `src`'s' directory structure.
- `test/` - Code for unit testing. `test`'s structure reflects `src`'s' directory structure.
For example, `test/compile/` test files inside `src/compile/`.

@@ -148,3 +106,3 @@ - Note that we prepend `/* tslint:disable:quotemark */` to all files under `test/compile`

This section lists commands that are commonly used during development. See `package.json` for other commands.
This section lists commands that are commonly used during development. See `package.json` for other commands.

@@ -157,3 +115,3 @@ ### Build

`npm run lint` and `npm run test` run ts-lint and all unit-tests respectively. These two commands are automatically run by `npm start` and `npm run watch`.
`npm run lint` and `npm run test` run ts-lint and all unit-tests respectively. These two commands are automatically run by `npm start` and `npm run watch`.

@@ -199,3 +157,3 @@ ### Test Coverage

This will compile all examples again and output the diff for changed examples in the console.
All compiled specs will be in `examples/_output`. For changed examples,
All compiled specs will be in `examples/_output`. For changed examples,
SVG files will be created in `examples/_diff` for comparison.

@@ -219,3 +177,3 @@ You can open those files to inspect visual changes, or run a diff command

- `atom-typescript` - This provides us IDE-like features for TS inside Atom including renaming, go to definition, find all references.
- `linter` and `linter-tslint` – These shows tslint errors inside the editor. This is quite important since our Travis run includes linting too. Therefore, if your branch has a linting error, Travis test will fail too.
- `linter` and `linter-tslint` – These shows tslint errors inside the editor. This is quite important since our Travis run includes linting too. Therefore, if your branch has a linting error, Travis test will fail too.

@@ -249,3 +207,3 @@ __Tips:__ If you don't want to see intermediate files (`.js`, `.js.map`), you can "Hide VCS Ignored Files" in the `tree-view` plugin.

Vega-Lite enables a number of open-source applications including user interface tools ([PoleStar](https://github.com/uwdata/polestar) and [Voyager](https://github.com/uwdata/voyager)) and visualization recommender ([Compass](https://github.com/uwdata/compass)). Look at their contribute pages if you are interested!
Vega-Lite enables a number of open-source applications including user interface tools ([PoleStar](https://github.com/uwdata/polestar) and [Voyager](https://github.com/uwdata/voyager)) and visualization recommender ([Compass](https://github.com/uwdata/compass)). Look at their contribute pages if you are interested!

@@ -252,0 +210,0 @@ - [PoleStar: Contribute](https://github.com/uwdata/polestar/wiki/Contribute)

@@ -1,1 +0,1 @@

["area","area_vertical","bar","bar_1d","bar_aggregate","bar_aggregate_size","bar_aggregate_vertical","bar_filter_calc","bar_grouped","bar_grouped_horizontal","bar_layered_transparent","bar_yearmonth","circle","histogram","line","line_color","line_detail","line_monotone","line_month","line_slope","line_step","point_1d","point_color","point_dot_timeunit_color","point_filled","point_ordinal_color","scatter","scatter_aggregate_detail","scatter_binned","scatter_binned_color","scatter_binned_size","scatter_bubble","scatter_color","scatter_color_custom","scatter_color_order","scatter_color_ordinal","scatter_color_ordinal_custom","scatter_color_quantitative","scatter_color_shape_constant","scatter_colored_with_shape","scatter_connected","scatter_log","square","stacked_area","stacked_area_normalize","stacked_area_ordinal","stacked_area_stream","stacked_bar_1d","stacked_bar_h","stacked_bar_h_order","stacked_bar_normalize","stacked_bar_population","stacked_bar_v","stacked_bar_weather","text_scatter_colored","text_table_heatmap","tick_dot","tick_dot_thickness","tick_strip","trellis_bar","trellis_bar_histogram","trellis_barley","trellis_row_column","trellis_scatter","trellis_scatter_binned_row","trellis_stacked_bar"]
["area","area_vertical","bar","bar_1d","bar_aggregate","bar_aggregate_size","bar_aggregate_vertical","bar_filter_calc","bar_grouped","bar_grouped_horizontal","bar_layered_transparent","bar_yearmonth","bubble_health_income","circle","github_punchcard","histogram","layer_bar_line","layer_bar_line_union","layer_histogram","layer_line_color_rule","line","line_color","line_detail","line_monotone","line_month","line_slope","line_step","minimal","point_1d","point_color","point_dot_timeunit_color","point_filled","point_ordinal_color","scatter","scatter_aggregate_detail","scatter_binned","scatter_binned_color","scatter_binned_size","scatter_bubble","scatter_color","scatter_color_custom","scatter_color_order","scatter_color_ordinal","scatter_color_ordinal_custom","scatter_color_quantitative","scatter_color_shape_constant","scatter_colored_with_shape","scatter_connected","scatter_log","scatter_opacity","square","stacked_area","stacked_area_normalize","stacked_area_ordinal","stacked_area_stream","stacked_bar_1d","stacked_bar_h","stacked_bar_h_order","stacked_bar_normalize","stacked_bar_population","stacked_bar_size","stacked_bar_sum_opacity","stacked_bar_v","stacked_bar_weather","text_scatter_colored","text_table_heatmap","tick_dot","tick_dot_thickness","tick_strip","trellis_anscombe","trellis_bar","trellis_bar_histogram","trellis_barley","trellis_row_column","trellis_scatter","trellis_scatter_binned_row","trellis_stacked_bar"]

@@ -14,16 +14,19 @@ "use strict";

var vgSchema = require('../node_modules/vega/vega-schema.json');
function validateAgainstSchemas(vlspec) {
var isVlValid = validator.validate(vlspec, vlSchema);
if (!isVlValid) {
var errors = validator.getLastErrors();
function validateVL(spec) {
var valid = validator.validate(spec, vlSchema);
var errors = validator.getLastErrors();
if (!valid) {
console.log(inspect(errors, { depth: 10, colors: true }));
}
chai_1.assert(isVlValid);
var vegaSpec = vl.compile(vlspec).spec;
var isVgValid = validator.validate(vegaSpec, vgSchema);
if (!isVgValid) {
var errors = validator.getLastErrors();
chai_1.assert(valid, errors && errors.map(function (err) { return err.message; }).join(', '));
}
function validateVega(spec) {
var vegaSpec = vl.compile(spec).spec;
var valid = validator.validate(vegaSpec, vgSchema);
var errors = validator.getLastErrors();
if (!valid) {
console.log(vegaSpec.marks[0].marks[0].properties);
console.log(inspect(errors, { depth: 10, colors: true }));
}
chai_1.assert(isVgValid);
chai_1.assert(valid, errors && errors.map(function (err) { return err.message; }).join(', '));
}

@@ -36,5 +39,10 @@ describe('Examples', function () {

}
it('should be valid and produce valid vega for: ' + example, function () {
var data = JSON.parse(fs.readFileSync('examples/specs/' + example));
validateAgainstSchemas(data);
var jsonSpec = JSON.parse(fs.readFileSync('examples/specs/' + example));
describe(example, function () {
it('should be valid vega-lite', function () {
validateVL(jsonSpec);
});
it('should produce valid vega', function () {
validateVega(jsonSpec);
});
});

@@ -41,0 +49,0 @@ });

@@ -17,18 +17,21 @@ import {assert} from 'chai';

function validateAgainstSchemas(vlspec) {
const isVlValid = validator.validate(vlspec, vlSchema);
if (!isVlValid) {
const errors = validator.getLastErrors();
function validateVL(spec) {
const valid = validator.validate(spec, vlSchema);
const errors = validator.getLastErrors();
if (!valid) {
console.log(inspect(errors, { depth: 10, colors: true }));
}
assert(isVlValid);
assert(valid, errors && errors.map((err) => {return err.message; }).join(', '));
}
const vegaSpec = vl.compile(vlspec).spec;
function validateVega(spec) {
const vegaSpec = vl.compile(spec).spec;
const isVgValid = validator.validate(vegaSpec, vgSchema);
if (!isVgValid) {
const errors = validator.getLastErrors();
const valid = validator.validate(vegaSpec, vgSchema);
const errors = validator.getLastErrors();
if (!valid) {
console.log(vegaSpec.marks[0].marks[0].properties);
console.log(inspect(errors, { depth: 10, colors: true }));
}
assert(isVgValid);
assert(valid, errors && errors.map((err) => {return err.message; }).join(', '));
}

@@ -41,8 +44,14 @@

if (path.extname(example) !== '.json') { return; }
const jsonSpec = JSON.parse(fs.readFileSync('examples/specs/' + example));
it('should be valid and produce valid vega for: ' + example, function() {
const data = JSON.parse(fs.readFileSync('examples/specs/' + example));
validateAgainstSchemas(data);
describe(example, function() {
it('should be valid vega-lite', function() {
validateVL(jsonSpec);
});
it('should produce valid vega', function() {
validateVega(jsonSpec);
});
});
});
});

@@ -62,7 +62,7 @@ {

{
"name": "scatter_log",
"title": "Scatter Plot with Log Scale",
"name": "bubble_health_income",
"title": "Gapminder Bubble Plot",
"galleryParameters": {
"backgroundSize": "160%",
"backgroundPosition": "15% 20%"
"backgroundSize": "230%",
"backgroundPosition": "40% 20%"
}

@@ -124,2 +124,6 @@ },

}
},
{
"name": "github_punchcard",
"title": "Github punch card visualization"
}

@@ -169,2 +173,8 @@ ],

"Trellis": [
{ "name": "trellis_anscombe",
"title": "Anscombe's Quartet",
"galleryParameters": {
"backgroundSize": "130%"
}
},
{

@@ -171,0 +181,0 @@ "name": "trellis_bar",

{
"name": "vega-lite",
"author": "Jeffrey Heer, Dominik Moritz, Kanit \"Ham\" Wongsuphasawat",
"version": "1.0.8",
"version": "1.0.9",
"collaborators": [
"Kanit Wongsuphasawat <kanitw@gmail.com> (http://kanitw.yellowpigz.com)",
"Dominik Moritz <domoritz@cs.washington.edu> (http://domoritz.de)",
"Dominik Moritz <domoritz@cs.washington.edu> (http://www.domoritz.de)",
"Jeffrey Heer <jheer@uw.edu> (http://jheer.org)"

@@ -25,2 +25,3 @@ ],

"build:images": "npm run data && scripts/generate-images.sh",
"build:toc": "bundle exec jekyll build --incremental -q && scripts/generate-toc",
"cover": "npm run pretest && istanbul cover node_modules/.bin/_mocha -- --recursive",

@@ -31,8 +32,8 @@ "clean": "rm -f vega-lite.* vega-lite-schema.json & find src -name '*.js*' -type f -delete & find test -name '*.js*' -type f -delete & find site -name '*.js*' -type f -delete & rm -rf examples/_diff examples/_original examples/_output examples/images && rm -rf data",

"deploy:gh": "scripts/deploy-gh.sh",
"lint": "tslint -c tslint.json src/*.ts test/*.ts src/**/*.ts test/**/*.ts",
"lint": "tslint -c tslint.json 'src/**/*.ts' 'test/**/*.ts'",
"prestart": "npm run build && npm run data && scripts/index-examples",
"start": "npm run watch & browser-sync start --server --files 'vega-lite.js' --index 'test-gallery.html'",
"poststart": "rm examples/all-examples.json",
"schema": "typescript-json-schema --required true src/spec.ts Spec > vega-lite-schema.json",
"presite": "tsc && npm run build && bower install && npm run data",
"schema": "typescript-json-schema --required true src/spec.ts ExtendedSpec > vega-lite-schema.json",
"presite": "tsc && npm run build && bower install && npm run data && npm run build:toc",
"site": "bundle exec jekyll serve --incremental",

@@ -57,26 +58,29 @@ "pretest": "tsc && npm run data",

"devDependencies": {
"browser-sync": "^2.11.1",
"browserify": "^13.0.0",
"browser-sync": "^2.12.8",
"browserify": "^13.0.1",
"browserify-shim": "^3.8.12",
"browserify-versionify": "^1.0.6",
"chai": "^3.5.0",
"cheerio": "^0.20.0",
"exorcist": "^0.4.0",
"istanbul": "^0.4.2",
"istanbul": "^0.4.3",
"json-diff": "^0.3.1",
"mocha": "^2.4.5",
"nodemon": "^1.9.0",
"nodemon": "^1.9.2",
"source-map-support": "^0.4.0",
"tsify": "^0.13.2",
"tslint": "^3.4.0",
"typescript": "^1.8.2",
"typescript-json-schema": "^0.0.8",
"tsify": "^0.15.5",
"tslint": "^3.10.2",
"typescript": "^1.8.10",
"typescript-json-schema": "^0.1.1",
"uglify-js": "^2.6.2",
"vega": "^2.5.1",
"vega": "^2.5.2",
"vega-datasets": "vega/vega-datasets#gh-pages",
"watchify": "^3.7.0",
"z-schema": "^3.16.1"
"yaml-front-matter": "^3.4.0",
"z-schema": "^3.17.0"
},
"dependencies": {
"datalib": "^1.6.1",
"yargs": "^4.1.0"
"datalib": "^1.6.3",
"json-stable-stringify": "^1.0.1",
"yargs": "^4.7.1"
},

@@ -83,0 +87,0 @@ "browserify": {

@@ -9,5 +9,5 @@ # Vega-Lite

You can find more details, documentation, and tutorials on the [Vega-Lite website](https://vega.github.io/vega-lite/).
You can find more details, [documentation](https://vega.github.io/vega-lite/docs/), [examples](https://vega.github.io/vega-lite/examples/), [usage instructions](https://vega.github.io/vega-lite/usage/embed.html), and [tutorials](https://vega.github.io/vega-lite/tutorials/getting_started.html) on the [Vega-Lite website](https://vega.github.io/vega-lite/).
Try using Vega-Lite in the online [Vega Editor](http://vega.github.io/vega-editor/?mode=vega-lite).
Try using Vega-Lite in the online [Vega Editor](https://vega.github.io/vega-editor/?mode=vega-lite).

@@ -18,2 +18,2 @@ Contributions are also welcome. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for contribution and development guidelines.

The development of Vega-Lite is led by [Kanit "Ham" Wongsuphasawat](https://twitter.com/kanitw), [Dominik Moritz](https://twitter.com/domoritz), and [Jeffrey Heer](https://twitter.com/jeffrey_heer) of the [University Washington Interactive Data Lab](http://idl.cs.washington.edu), with significant help from [Arvind Satyanarayan](https://twitter.com/arvindsatya1). Please see the [contributors page](https://github.com/vega/vega-lite/graphs/contributors) for the full list of contributors.
The development of Vega-Lite is led by [Kanit "Ham" Wongsuphasawat](https://twitter.com/kanitw), [Dominik Moritz](https://twitter.com/domoritz), and [Jeffrey Heer](https://twitter.com/jeffrey_heer) of the [University Washington Interactive Data Lab](https://idl.cs.washington.edu), with significant help from [Arvind Satyanarayan](https://twitter.com/arvindsatya1). Please see the [contributors page](https://github.com/vega/vega-lite/graphs/contributors) for the full list of contributors.

@@ -24,2 +24,8 @@

// ---------- Axis ----------
/**
* Color of axis line.
*/
axisColor?: string;
// ---------- Grid ----------

@@ -31,2 +37,22 @@ /**

/**
* Color of gridlines.
*/
gridColor?: string;
/**
* The offset (in pixels) into which to begin drawing with the grid dash array.
*/
gridDash?: number[];
/**
* The stroke opacity of grid (value between [0,1])
*/
gridOpacity?: number;
/**
* The grid width, in pixels.
*/
gridWidth?: number;
// ---------- Labels ----------

@@ -69,3 +95,24 @@ /**

ticks?: number;
/**
* The color of the axis's tick.
*/
tickColor?: string;
/**
* The color of the tick label, can be in hex color code or regular color name.
*/
tickLabelColor?: string;
/**
* The font of the tick label.
*/
tickLabelFont?: string;
/**
* The font size of label, in pixels.
*/
tickLabelFontSize?: number;
/**
* The padding, in pixels, between ticks and text labels.

@@ -95,4 +142,29 @@ */

/**
* The width, in pixels, of ticks.
*/
tickWidth?: number;
// ---------- Title ----------
/**
* Color of the title, can be in hex color code or regular color name.
*/
titleColor?: string;
/**
* Font of the title.
*/
titleFont?: string;
/**
* Size of the title.
*/
titleFontSize?: number;
/**
* Weight of the title.
*/
titleFontWeight?: string;
/**
* A title offset value for the axis.

@@ -99,0 +171,0 @@ */

@@ -16,2 +16,3 @@ "use strict";

Channel[Channel["ORDER"] = 'order'] = "ORDER";
Channel[Channel["OPACITY"] = 'opacity'] = "OPACITY";
})(exports.Channel || (exports.Channel = {}));

@@ -31,3 +32,8 @@ var Channel = exports.Channel;

exports.ORDER = Channel.ORDER;
exports.CHANNELS = [exports.X, exports.Y, exports.ROW, exports.COLUMN, exports.SIZE, exports.SHAPE, exports.COLOR, exports.PATH, exports.ORDER, exports.TEXT, exports.DETAIL, exports.LABEL];
exports.OPACITY = Channel.OPACITY;
exports.CHANNELS = [exports.X, exports.Y, exports.ROW, exports.COLUMN, exports.SIZE, exports.SHAPE, exports.COLOR, exports.PATH, exports.ORDER, exports.OPACITY, exports.TEXT, exports.DETAIL, exports.LABEL];
exports.UNIT_CHANNELS = util_1.without(exports.CHANNELS, [exports.ROW, exports.COLUMN]);
exports.UNIT_SCALE_CHANNELS = util_1.without(exports.UNIT_CHANNELS, [exports.PATH, exports.ORDER, exports.DETAIL, exports.TEXT, exports.LABEL]);
exports.NONSPATIAL_CHANNELS = util_1.without(exports.UNIT_CHANNELS, [exports.X, exports.Y]);
exports.NONSPATIAL_SCALE_CHANNELS = util_1.without(exports.UNIT_SCALE_CHANNELS, [exports.X, exports.Y]);
;

@@ -45,6 +51,7 @@ function supportMark(channel, mark) {

case exports.ORDER:
case exports.OPACITY:
case exports.ROW:
case exports.COLUMN:
return {
point: true, tick: true, circle: true, square: true,
point: true, tick: true, rule: true, circle: true, square: true,
bar: true, line: true, area: true, text: true

@@ -54,3 +61,3 @@ };

return {
point: true, tick: true, circle: true, square: true,
point: true, tick: true, rule: true, circle: true, square: true,
bar: true, text: true

@@ -74,2 +81,3 @@ };

case exports.COLOR:
case exports.OPACITY:
case exports.LABEL:

@@ -76,0 +84,0 @@ return {

@@ -7,3 +7,3 @@ /*

import {Mark} from './mark';
import {contains} from './util';
import {contains, without} from './util';

@@ -22,3 +22,4 @@ export enum Channel {

PATH = 'path' as any,
ORDER = 'order' as any
ORDER = 'order' as any,
OPACITY = 'opacity' as any
}

@@ -38,8 +39,15 @@

export const ORDER = Channel.ORDER;
export const OPACITY = Channel.OPACITY;
export const CHANNELS = [X, Y, ROW, COLUMN, SIZE, SHAPE, COLOR, PATH, ORDER, TEXT, DETAIL, LABEL];
export const CHANNELS = [X, Y, ROW, COLUMN, SIZE, SHAPE, COLOR, PATH, ORDER, OPACITY, TEXT, DETAIL, LABEL];
export const UNIT_CHANNELS = without(CHANNELS, [ROW, COLUMN]);
export const UNIT_SCALE_CHANNELS = without(UNIT_CHANNELS, [PATH, ORDER, DETAIL, TEXT, LABEL]);
export const NONSPATIAL_CHANNELS = without(UNIT_CHANNELS, [X, Y]);
export const NONSPATIAL_SCALE_CHANNELS = without(UNIT_SCALE_CHANNELS, [X, Y]);
export interface SupportedMark {
point?: boolean;
tick?: boolean;
rule?: boolean;
circle?: boolean;

@@ -75,6 +83,7 @@ square?: boolean;

case ORDER:
case OPACITY:
case ROW:
case COLUMN:
return { // all marks
point: true, tick: true, circle: true, square: true,
point: true, tick: true, rule: true, circle: true, square: true,
bar: true, line: true, area: true, text: true

@@ -84,3 +93,3 @@ };

return {
point: true, tick: true, circle: true, square: true,
point: true, tick: true, rule: true, circle: true, square: true,
bar: true, text: true

@@ -113,2 +122,3 @@ };

case COLOR:
case OPACITY:
case LABEL:

@@ -115,0 +125,0 @@ return {

@@ -8,3 +8,12 @@ "use strict";

var common_1 = require('./common');
function compileInnerAxis(channel, model) {
function parseAxisComponent(model, axisChannels) {
return axisChannels.reduce(function (axis, channel) {
if (model.axis(channel)) {
axis[channel] = parseAxis(channel, model);
}
return axis;
}, {});
}
exports.parseAxisComponent = parseAxisComponent;
function parseInnerAxis(channel, model) {
var isCol = channel === channel_1.COLUMN, isRow = channel === channel_1.ROW, type = isCol ? 'x' : isRow ? 'y' : channel;

@@ -37,4 +46,4 @@ var def = {

}
exports.compileInnerAxis = compileInnerAxis;
function compileAxis(channel, model) {
exports.parseInnerAxis = parseInnerAxis;
function parseAxis(channel, model) {
var isCol = channel === channel_1.COLUMN, isRow = channel === channel_1.ROW, type = isCol ? 'x' : isRow ? 'y' : channel;

@@ -48,5 +57,4 @@ var axis = model.axis(channel);

[
'grid', 'layer', 'offset', 'orient', 'tickSize', 'ticks', 'title',
'tickPadding', 'tickSize', 'tickSizeMajor', 'tickSizeMinor', 'tickSizeEnd',
'titleOffset', 'values', 'subdivide'
'grid', 'layer', 'offset', 'orient', 'tickSize', 'ticks', 'tickSizeEnd', 'title', 'titleOffset',
'tickPadding', 'tickSize', 'tickSizeMajor', 'tickSizeMinor', 'values', 'subdivide'
].forEach(function (property) {

@@ -69,3 +77,3 @@ var method;

props[group];
if (value !== undefined) {
if (value !== undefined && util_1.keys(value).length > 0) {
def.properties = def.properties || {};

@@ -77,3 +85,3 @@ def.properties[group] = value;

}
exports.compileAxis = compileAxis;
exports.parseAxis = parseAxis;
function offset(model, channel) {

@@ -95,3 +103,3 @@ return model.axis(channel).offset;

}
return gridShow(model, channel) && ((channel === channel_1.Y || channel === channel_1.X) && !(model.has(channel_1.COLUMN) || model.has(channel_1.ROW)));
return gridShow(model, channel) && ((channel === channel_1.Y || channel === channel_1.X) && !(model.parent() && model.parent().isFacet()));
}

@@ -119,7 +127,2 @@ exports.grid = grid;

}
else if (channel === channel_1.ROW) {
if (model.has(channel_1.Y) && model.axis(channel_1.Y).orient !== axis_1.AxisOrient.RIGHT) {
return axis_1.AxisOrient.RIGHT;
}
}
return undefined;

@@ -147,2 +150,10 @@ }

exports.tickSize = tickSize;
function tickSizeEnd(model, channel) {
var tickSizeEnd = model.axis(channel).tickSizeEnd;
if (tickSizeEnd !== undefined) {
return tickSizeEnd;
}
return undefined;
}
exports.tickSizeEnd = tickSizeEnd;
function title(model, channel) {

@@ -159,6 +170,8 @@ var axis = model.axis(channel);

else if (channel === channel_1.X && !model.isOrdinalScale(channel_1.X)) {
maxLength = model.cellWidth() / model.axis(channel_1.X).characterWidth;
var unitModel = model;
maxLength = unitModel.config().cell.width / model.axis(channel_1.X).characterWidth;
}
else if (channel === channel_1.Y && !model.isOrdinalScale(channel_1.Y)) {
maxLength = model.cellHeight() / model.axis(channel_1.Y).characterWidth;
var unitModel = model;
maxLength = unitModel.config().cell.height / model.axis(channel_1.Y).characterWidth;
}

@@ -168,7 +181,17 @@ return maxLength ? util_1.truncate(fieldTitle, maxLength) : fieldTitle;

exports.title = title;
function titleOffset(model, channel) {
var titleOffset = model.axis(channel).titleOffset;
if (titleOffset !== undefined) {
return titleOffset;
}
return undefined;
}
exports.titleOffset = titleOffset;
var properties;
(function (properties) {
function axis(model, channel, axisPropsSpec, def) {
function axis(model, channel, axisPropsSpec) {
var axis = model.axis(channel);
return util_1.extend(axis.axisWidth !== undefined ?
return util_1.extend(axis.axisColor !== undefined ?
{ stroke: { value: axis.axisColor } } :
{}, axis.axisWidth !== undefined ?
{ strokeWidth: { value: axis.axisWidth } } :

@@ -178,2 +201,7 @@ {}, axisPropsSpec || {});

properties.axis = axis;
function grid(model, channel, gridPropsSpec) {
var axis = model.axis(channel);
return util_1.extend(axis.gridColor !== undefined ? { stroke: { value: axis.gridColor } } : {}, axis.gridOpacity !== undefined ? { strokeOpacity: { value: axis.gridOpacity } } : {}, axis.gridWidth !== undefined ? { strokeWidth: { value: axis.gridWidth } } : {}, axis.gridDash !== undefined ? { strokeDashOffset: { value: axis.gridDash } } : {}, gridPropsSpec || {});
}
properties.grid = grid;
function labels(model, channel, labelsSpec, def) {

@@ -201,5 +229,2 @@ var fieldDef = model.fieldDef(channel);

}
else if (channel === channel_1.ROW && model.has(channel_1.X)) {
labelsSpec.angle = { value: def.orient === 'left' ? 270 : 90 };
}
}

@@ -236,6 +261,25 @@ if (axis.labelAlign !== undefined) {

}
return labelsSpec || undefined;
if (axis.tickLabelColor !== undefined) {
labelsSpec.stroke = { value: axis.tickLabelColor };
}
if (axis.tickLabelFont !== undefined) {
labelsSpec.font = { value: axis.tickLabelFont };
}
if (axis.tickLabelFontSize !== undefined) {
labelsSpec.fontSize = { value: axis.tickLabelFontSize };
}
return util_1.keys(labelsSpec).length === 0 ? undefined : labelsSpec;
}
properties.labels = labels;
function ticks(model, channel, ticksPropsSpec) {
var axis = model.axis(channel);
return util_1.extend(axis.tickColor !== undefined ? { stroke: { value: axis.tickColor } } : {}, axis.tickWidth !== undefined ? { strokeWidth: { value: axis.tickWidth } } : {}, ticksPropsSpec || {});
}
properties.ticks = ticks;
function title(model, channel, titlePropsSpec) {
var axis = model.axis(channel);
return util_1.extend(axis.titleColor !== undefined ? { stroke: { value: axis.titleColor } } : {}, axis.titleFont !== undefined ? { font: { value: axis.titleFont } } : {}, axis.titleFontSize !== undefined ? { fontSize: { value: axis.titleFontSize } } : {}, axis.titleFontWeight !== undefined ? { fontWeight: { value: axis.titleFontWeight } } : {}, titlePropsSpec || {});
}
properties.title = title;
})(properties = exports.properties || (exports.properties = {}));
//# sourceMappingURL=axis.js.map

@@ -5,6 +5,8 @@ import {AxisOrient} from '../axis';

import {NOMINAL, ORDINAL, TEMPORAL} from '../type';
import {contains, extend, truncate} from '../util';
import {contains, keys, extend, truncate, Dict} from '../util';
import {VgAxis} from '../vega.schema';
import {formatMixins} from './common';
import {Model} from './Model';
import {Model} from './model';
import {UnitModel} from './unit';

@@ -14,6 +16,15 @@ // https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#11-ambient-declarations

export function parseAxisComponent(model: Model, axisChannels: Channel[]): Dict<VgAxis> {
return axisChannels.reduce(function(axis, channel) {
if (model.axis(channel)) {
axis[channel] = parseAxis(channel, model);
}
return axis;
}, {} as Dict<VgAxis>);
}
/**
* Make an inner axis for showing grid for shared axis.
*/
export function compileInnerAxis(channel: Channel, model: Model) {
export function parseInnerAxis(channel: Channel, model: Model): VgAxis {
const isCol = channel === COLUMN,

@@ -33,3 +44,3 @@ isRow = channel === ROW,

labels: {
text: {value:''}
text: {value: ''}
},

@@ -59,3 +70,3 @@ axis: {

export function compileAxis(channel: Channel, model: Model) {
export function parseAxis(channel: Channel, model: Model): VgAxis {
const isCol = channel === COLUMN,

@@ -79,6 +90,5 @@ isRow = channel === ROW,

// a) properties with special rules (so it has axis[property] methods) -- call rule functions
'grid', 'layer', 'offset', 'orient', 'tickSize', 'ticks', 'title',
'grid', 'layer', 'offset', 'orient', 'tickSize', 'ticks', 'tickSizeEnd', 'title', 'titleOffset',
// b) properties without rules, only produce default values in the schema, or explicit value if specified
'tickPadding', 'tickSize', 'tickSizeMajor', 'tickSizeMinor', 'tickSizeEnd',
'titleOffset', 'values', 'subdivide'
'tickPadding', 'tickSize', 'tickSizeMajor', 'tickSizeMinor', 'values', 'subdivide'
].forEach(function(property) {

@@ -106,3 +116,3 @@ let method: (model: Model, channel: Channel, def:any)=>any;

props[group];
if (value !== undefined) {
if (value !== undefined && keys(value).length > 0) {
def.properties = def.properties || {};

@@ -143,3 +153,3 @@ def.properties[group] = value;

// the axis is a shared / union axis.
(channel === Y || channel === X) && !(model.has(COLUMN) || model.has(ROW))
(channel === Y || channel === X) && !(model.parent() && model.parent().isFacet())
);

@@ -167,6 +177,2 @@ }

return AxisOrient.TOP;
} else if (channel === ROW) {
if (model.has(Y) && model.axis(Y).orient !== AxisOrient.RIGHT) {
return AxisOrient.RIGHT;
}
}

@@ -199,3 +205,11 @@ return undefined;

export function tickSizeEnd(model: Model, channel: Channel) {
const tickSizeEnd = model.axis(channel).tickSizeEnd;
if (tickSizeEnd !== undefined) {
return tickSizeEnd;
}
return undefined;
}
export function title(model: Model, channel: Channel) {

@@ -214,8 +228,11 @@ const axis = model.axis(channel);

} else if (channel === X && !model.isOrdinalScale(X)) {
const unitModel: UnitModel = model as any; // only unit model has channel x
// For non-ordinal scale, we know cell size at compile time, we can guess max length
maxLength = model.cellWidth() / model.axis(X).characterWidth;
maxLength = unitModel.config().cell.width / model.axis(X).characterWidth;
} else if (channel === Y && !model.isOrdinalScale(Y)) {
const unitModel: UnitModel = model as any; // only unit model has channel y
// For non-ordinal scale, we know cell size at compile time, we can guess max length
maxLength = model.cellHeight() / model.axis(Y).characterWidth;
maxLength = unitModel.config().cell.height / model.axis(Y).characterWidth;
}
// FIXME: we should use template to truncate instead

@@ -225,7 +242,18 @@ return maxLength ? truncate(fieldTitle, maxLength) : fieldTitle;

export function titleOffset(model: Model, channel: Channel) {
const titleOffset = model.axis(channel).titleOffset;
if (titleOffset !== undefined) {
return titleOffset;
}
return undefined;
}
export namespace properties {
export function axis(model: Model, channel: Channel, axisPropsSpec, def) {
export function axis(model: Model, channel: Channel, axisPropsSpec) {
const axis = model.axis(channel);
return extend(
axis.axisColor !== undefined ?
{ stroke: {value: axis.axisColor} } :
{},
axis.axisWidth !== undefined ?

@@ -238,2 +266,14 @@ { strokeWidth: {value: axis.axisWidth} } :

export function grid(model: Model, channel: Channel, gridPropsSpec) {
const axis = model.axis(channel);
return extend(
axis.gridColor !== undefined ? { stroke: {value: axis.gridColor}} : {},
axis.gridOpacity !== undefined ? {strokeOpacity: {value: axis.gridOpacity} } : {},
axis.gridWidth !== undefined ? {strokeWidth : {value: axis.gridWidth} } : {},
axis.gridDash !== undefined ? {strokeDashOffset : {value: axis.gridDash} } : {},
gridPropsSpec || {}
);
}
export function labels(model: Model, channel: Channel, labelsSpec, def) {

@@ -265,4 +305,2 @@ const fieldDef = model.fieldDef(channel);

labelsSpec.angle = {value: 270};
} else if (channel === ROW && model.has(X)) {
labelsSpec.angle = {value: def.orient === 'left' ? 270 : 90};
}

@@ -303,4 +341,39 @@ }

return labelsSpec || undefined;
if (axis.tickLabelColor !== undefined) {
labelsSpec.stroke = {value: axis.tickLabelColor};
}
if (axis.tickLabelFont !== undefined) {
labelsSpec.font = {value: axis.tickLabelFont};
}
if (axis.tickLabelFontSize !== undefined) {
labelsSpec.fontSize = {value: axis.tickLabelFontSize};
}
return keys(labelsSpec).length === 0 ? undefined : labelsSpec;
}
export function ticks(model: Model, channel: Channel, ticksPropsSpec) {
const axis = model.axis(channel);
return extend(
axis.tickColor !== undefined ? {stroke : {value: axis.tickColor} } : {},
axis.tickWidth !== undefined ? {strokeWidth: {value: axis.tickWidth} } : {},
ticksPropsSpec || {}
);
}
export function title(model: Model, channel: Channel, titlePropsSpec) {
const axis = model.axis(channel);
return extend(
axis.titleColor !== undefined ? {stroke : {value: axis.titleColor} } : {},
axis.titleFont !== undefined ? {font: {value: axis.titleFont}} : {},
axis.titleFontSize !== undefined ? {fontSize: {value: axis.titleFontSize}} : {},
axis.titleFontWeight !== undefined ? {fontWeight: {value: axis.titleFontWeight}} : {},
titlePropsSpec || {}
);
}
}

@@ -6,27 +6,63 @@ "use strict";

var type_1 = require('../type');
var time_1 = require('./time');
var util_1 = require('../util');
exports.FILL_STROKE_CONFIG = ['fill', 'fillOpacity',
'stroke', 'strokeWidth', 'strokeDash', 'strokeDashOffset', 'strokeOpacity',
var facet_1 = require('./facet');
var layer_1 = require('./layer');
var timeunit_1 = require('../timeunit');
var unit_1 = require('./unit');
var spec_1 = require('../spec');
function buildModel(spec, parent, parentGivenName) {
if (spec_1.isFacetSpec(spec)) {
return new facet_1.FacetModel(spec, parent, parentGivenName);
}
if (spec_1.isLayerSpec(spec)) {
return new layer_1.LayerModel(spec, parent, parentGivenName);
}
if (spec_1.isUnitSpec(spec)) {
return new unit_1.UnitModel(spec, parent, parentGivenName);
}
console.error('Invalid spec.');
return null;
}
exports.buildModel = buildModel;
exports.STROKE_CONFIG = ['stroke', 'strokeWidth',
'strokeDash', 'strokeDashOffset', 'strokeOpacity', 'opacity'];
exports.FILL_CONFIG = ['fill', 'fillOpacity',
'opacity'];
exports.FILL_STROKE_CONFIG = util_1.union(exports.STROKE_CONFIG, exports.FILL_CONFIG);
function applyColorAndOpacity(p, model) {
var filled = model.config().mark.filled;
var fieldDef = model.fieldDef(channel_1.COLOR);
applyMarkConfig(p, model, exports.FILL_STROKE_CONFIG);
var value;
var colorFieldDef = model.fieldDef(channel_1.COLOR);
var opacityFieldDef = model.fieldDef(channel_1.OPACITY);
if (filled) {
applyMarkConfig(p, model, exports.FILL_CONFIG);
}
else {
applyMarkConfig(p, model, exports.STROKE_CONFIG);
}
var colorValue;
var opacityValue;
if (model.has(channel_1.COLOR)) {
value = {
colorValue = {
scale: model.scaleName(channel_1.COLOR),
field: model.field(channel_1.COLOR, fieldDef.type === type_1.ORDINAL ? { prefn: 'rank_' } : {})
field: model.field(channel_1.COLOR, colorFieldDef.type === type_1.ORDINAL ? { prefn: 'rank_' } : {})
};
}
else if (fieldDef && fieldDef.value) {
value = { value: fieldDef.value };
else if (colorFieldDef && colorFieldDef.value) {
colorValue = { value: colorFieldDef.value };
}
if (value !== undefined) {
if (model.has(channel_1.OPACITY)) {
opacityValue = {
scale: model.scaleName(channel_1.OPACITY),
field: model.field(channel_1.OPACITY, opacityFieldDef.type === type_1.ORDINAL ? { prefn: 'rank_' } : {})
};
}
else if (opacityFieldDef && opacityFieldDef.value) {
opacityValue = { value: opacityFieldDef.value };
}
if (colorValue !== undefined) {
if (filled) {
p.fill = value;
p.fill = colorValue;
}
else {
p.stroke = value;
p.stroke = colorValue;
}

@@ -38,2 +74,5 @@ }

}
if (opacityValue !== undefined) {
p.opacity = opacityValue;
}
}

@@ -48,6 +87,7 @@ exports.applyColorAndOpacity = applyColorAndOpacity;

});
return properties;
}
exports.applyConfig = applyConfig;
function applyMarkConfig(marksProperties, model, propsList) {
applyConfig(marksProperties, model.config().mark, propsList);
return applyConfig(marksProperties, model.config().mark, propsList);
}

@@ -96,2 +136,3 @@ exports.applyMarkConfig = applyMarkConfig;

case channel_1.COLOR:
case channel_1.OPACITY:
case channel_1.SHAPE:

@@ -112,5 +153,5 @@ case channel_1.SIZE:

var fieldDef = model.fieldDef(channel);
return time_1.format(fieldDef.timeUnit, isAbbreviated(model, channel, fieldDef));
return timeunit_1.format(fieldDef.timeUnit, isAbbreviated(model, channel, fieldDef));
}
exports.timeFormat = timeFormat;
//# sourceMappingURL=common.js.map

@@ -1,37 +0,79 @@

import {Model} from './Model';
import {FieldDef, OrderChannelDef} from '../fielddef';
import {COLUMN, ROW, X, Y, SIZE, COLOR, SHAPE, TEXT, LABEL, Channel} from '../channel';
import {field} from '../fielddef';
import {COLUMN, ROW, X, Y, SIZE, COLOR, OPACITY, SHAPE, TEXT, LABEL, Channel} from '../channel';
import {FieldDef, field, OrderChannelDef} from '../fielddef';
import {SortOrder} from '../sort';
import {QUANTITATIVE, ORDINAL, TEMPORAL} from '../type';
import {format as timeFormatExpr} from './time';
import {contains} from '../util';
import {contains, union} from '../util';
export const FILL_STROKE_CONFIG = ['fill', 'fillOpacity',
'stroke', 'strokeWidth', 'strokeDash', 'strokeDashOffset', 'strokeOpacity',
import {FacetModel} from './facet';
import {LayerModel} from './layer';
import {Model} from './model';
import {format as timeFormatExpr} from '../timeunit';
import {UnitModel} from './unit';
import {Spec, isUnitSpec, isFacetSpec, isLayerSpec} from '../spec';
export function buildModel(spec: Spec, parent: Model, parentGivenName: string): Model {
if (isFacetSpec(spec)) {
return new FacetModel(spec, parent, parentGivenName);
}
if (isLayerSpec(spec)) {
return new LayerModel(spec, parent, parentGivenName);
}
if (isUnitSpec(spec)) {
return new UnitModel(spec, parent, parentGivenName);
}
console.error('Invalid spec.');
return null;
}
// TODO: figure if we really need opacity in both
export const STROKE_CONFIG = ['stroke', 'strokeWidth',
'strokeDash', 'strokeDashOffset', 'strokeOpacity', 'opacity'];
export const FILL_CONFIG = ['fill', 'fillOpacity',
'opacity'];
export function applyColorAndOpacity(p, model: Model) {
export const FILL_STROKE_CONFIG = union(STROKE_CONFIG, FILL_CONFIG);
export function applyColorAndOpacity(p, model: UnitModel) {
const filled = model.config().mark.filled;
const fieldDef = model.fieldDef(COLOR);
const colorFieldDef = model.fieldDef(COLOR);
const opacityFieldDef = model.fieldDef(OPACITY);
// Apply fill stroke config first so that color field / value can override
// fill / stroke
applyMarkConfig(p, model, FILL_STROKE_CONFIG);
if (filled) {
applyMarkConfig(p, model, FILL_CONFIG);
} else {
applyMarkConfig(p, model, STROKE_CONFIG);
}
let value;
let colorValue;
let opacityValue;
if (model.has(COLOR)) {
value = {
colorValue = {
scale: model.scaleName(COLOR),
field: model.field(COLOR, fieldDef.type === ORDINAL ? {prefn: 'rank_'} : {})
field: model.field(COLOR, colorFieldDef.type === ORDINAL ? {prefn: 'rank_'} : {})
};
} else if (fieldDef && fieldDef.value) {
value = { value: fieldDef.value };
} else if (colorFieldDef && colorFieldDef.value) {
colorValue = { value: colorFieldDef.value };
}
if (value !== undefined) {
if (model.has(OPACITY)) {
opacityValue = {
scale: model.scaleName(OPACITY),
field: model.field(OPACITY, opacityFieldDef.type === ORDINAL ? {prefn: 'rank_'} : {})
};
} else if (opacityFieldDef && opacityFieldDef.value) {
opacityValue = { value: opacityFieldDef.value };
}
if (colorValue !== undefined) {
if (filled) {
p.fill = value;
p.fill = colorValue;
} else {
p.stroke = value;
p.stroke = colorValue;
}

@@ -43,2 +85,6 @@ } else {

}
if (opacityValue !== undefined) {
p.opacity = opacityValue;
}
}

@@ -53,6 +99,7 @@

});
return properties;
}
export function applyMarkConfig(marksProperties, model: Model, propsList: string[]) {
applyConfig(marksProperties, model.config().mark, propsList);
export function applyMarkConfig(marksProperties, model: UnitModel, propsList: string[]) {
return applyConfig(marksProperties, model.config().mark, propsList);
}

@@ -115,2 +162,3 @@

case COLOR:
case OPACITY:
case SHAPE:

@@ -117,0 +165,0 @@ case SIZE:

"use strict";
var Model_1 = require('./Model');
var axis_1 = require('./axis');
var data_1 = require('./data');
var layout_1 = require('./layout');
var facet_1 = require('./facet');
var legend_1 = require('./legend');
var mark_1 = require('./mark/mark');
var scale_1 = require('./scale');
var data_1 = require('../data');
var spec_1 = require('../spec');
var util_1 = require('../util');
var common_1 = require('./common');
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) {
var model = new Model_1.Model(spec);
function compile(inputSpec) {
var spec = spec_1.normalize(inputSpec);
var model = common_1.buildModel(spec, null, '');
model.parse();
return assemble(model);
}
exports.compile = compile;
function assemble(model) {
var config = model.config();
var output = util_1.extend(spec.name ? { name: spec.name } : {}, {
var output = util_1.extend({
width: 1,

@@ -24,4 +20,4 @@ height: 1,

}, config.viewport ? { viewport: config.viewport } : {}, config.background ? { background: config.background } : {}, {
data: data_1.compileData(model).concat([layout_1.compileLayoutData(model)]),
marks: [compileRootGroup(model)]
data: [].concat(model.assembleData([]), model.assembleLayout([])),
marks: [assembleRootGroup(model)]
});

@@ -32,38 +28,18 @@ return {

}
exports.compile = compile;
function compileRootGroup(model) {
var spec = model.spec();
function assembleRootGroup(model) {
var rootGroup = util_1.extend({
name: spec.name ? spec.name + '-root' : 'root',
name: model.name('root'),
type: 'group',
}, spec.description ? { description: spec.description } : {}, {
from: { data: data_2.LAYOUT },
}, model.description() ? { description: model.description() } : {}, {
from: { data: data_1.LAYOUT },
properties: {
update: {
update: util_1.extend({
width: { field: 'width' },
height: { field: 'height' }
}
}, model.assembleParentGroupProperties(model.config().cell))
}
});
var marks = mark_1.compileMark(model);
if (model.has(channel_1.ROW) || model.has(channel_1.COLUMN)) {
util_1.extend(rootGroup, facet_1.facetMixins(model, marks));
}
else {
common_1.applyConfig(rootGroup.properties.update, model.config().cell, common_1.FILL_STROKE_CONFIG.concat(['clip']));
rootGroup.marks = marks;
rootGroup.scales = scale_1.compileScales(model);
var axes = (model.has(channel_1.X) && model.axis(channel_1.X) ? [axis_1.compileAxis(channel_1.X, model)] : [])
.concat(model.has(channel_1.Y) && model.axis(channel_1.Y) ? [axis_1.compileAxis(channel_1.Y, model)] : []);
if (axes.length > 0) {
rootGroup.axes = axes;
}
}
var legends = legend_1.compileLegends(model);
if (legends.length > 0) {
rootGroup.legends = legends;
}
return rootGroup;
return util_1.extend(rootGroup, model.assembleGroup());
}
exports.compileRootGroup = compileRootGroup;
exports.assembleRootGroup = assembleRootGroup;
//# sourceMappingURL=compile.js.map
/**
* Module for compiling Vega-lite spec into Vega spec.
*/
import {Model} from './Model';
import {compileAxis} from './axis';
import {compileData} from './data';
import {compileLayoutData} from './layout';
import {facetMixins} from './facet';
import {compileLegends} from './legend';
import {compileMark} from './mark/mark';
import {compileScales} from './scale';
import {applyConfig, FILL_STROKE_CONFIG} from './common';
import {LAYOUT} from '../data';
import {Model} from './model';
import {normalize, ExtendedSpec} from '../spec';
import {extend} from '../util';
import {LAYOUT} from '../data';
import {COLUMN, ROW, X, Y} from '../channel';
import {buildModel} from './common';
export {Model} from './Model';
export function compile(inputSpec: ExtendedSpec) {
// 1. Convert input spec into a normal form
// (Decompose all extended unit specs into composition of unit spec.)
const spec = normalize(inputSpec);
export function compile(spec) {
const model = new Model(spec);
// 2. Instantiate the model with default properties
const model = buildModel(spec, null, '');
// 3. Parse each part of the model to produce components that will be assembled later
// We traverse the whole tree to parse once for each type of components
// (e.g., data, layout, mark, scale).
// Please see inside model.parse() for order for compilation.
model.parse();
// 4. Assemble a Vega Spec from the parsed components in 3.
return assemble(model);
}
function assemble(model: Model) {
const config = model.config();

@@ -27,3 +35,2 @@

const output = extend(
spec.name ? { name: spec.name } : {},
{

@@ -38,4 +45,9 @@ // Set size to 1 because we rely on padding anyway

{
data: compileData(model).concat([compileLayoutData(model)]),
marks: [compileRootGroup(model)]
// TODO: signal: model.assembleSelectionSignal
data: [].concat(
model.assembleData([]),
model.assembleLayout([])
// TODO: model.assembleSelectionData
),
marks: [assembleRootGroup(model)]
});

@@ -49,44 +61,22 @@

export function compileRootGroup(model: Model) {
const spec = model.spec();
export function assembleRootGroup(model: Model) {
let rootGroup:any = extend({
name: spec.name ? spec.name + '-root' : 'root',
name: model.name('root'),
type: 'group',
},
spec.description ? {description: spec.description} : {},
model.description() ? {description: model.description()} : {},
{
from: {data: LAYOUT},
properties: {
update: {
width: {field: 'width'},
height: {field: 'height'}
}
update: extend(
{
width: {field: 'width'},
height: {field: 'height'}
},
model.assembleParentGroupProperties(model.config().cell)
)
}
});
const marks = compileMark(model);
// Small Multiples
if (model.has(ROW) || model.has(COLUMN)) {
// put the marks inside a facet cell's group
extend(rootGroup, facetMixins(model, marks));
} else {
applyConfig(rootGroup.properties.update, model.config().cell, FILL_STROKE_CONFIG.concat(['clip']));
rootGroup.marks = marks;
rootGroup.scales = compileScales(model);
const axes = (model.has(X) && model.axis(X) ? [compileAxis(X, model)] : [])
.concat(model.has(Y) && model.axis(Y) ? [compileAxis(Y, model)] : []);
if (axes.length > 0) {
rootGroup.axes = axes;
}
}
// legends (similar for either facets or non-facets
const legends = compileLegends(model);
if (legends.length > 0) {
rootGroup.legends = legends;
}
return rootGroup;
return extend(rootGroup, model.assembleGroup());
}

@@ -7,3 +7,3 @@ "use strict";

var util_1 = require('../util');
function compileMarkConfig(mark, encoding, config, stack) {
function initMarkConfig(mark, encoding, config) {
return util_1.extend(['filled', 'opacity', 'orient', 'align'].reduce(function (cfg, property) {

@@ -14,3 +14,3 @@ var value = config.mark[property];

if (value === undefined) {
cfg[property] = mark !== mark_1.POINT && mark !== mark_1.LINE;
cfg[property] = mark !== mark_1.POINT && mark !== mark_1.LINE && mark !== mark_1.RULE;
}

@@ -26,9 +26,19 @@ break;

case 'orient':
if (stack) {
cfg[property] = stack.groupbyChannel === channel_1.Y ? 'horizontal' : undefined;
var xIsMeasure = fielddef_1.isMeasure(encoding.x);
var yIsMeasure = fielddef_1.isMeasure(encoding.y);
if (xIsMeasure && !yIsMeasure) {
if (mark === mark_1.TICK) {
cfg[property] = 'vertical';
}
else {
cfg[property] = 'horizontal';
}
}
if (value === undefined) {
cfg[property] = fielddef_1.isMeasure(encoding[channel_1.X]) && !fielddef_1.isMeasure(encoding[channel_1.Y]) ?
'horizontal' :
undefined;
else if (!xIsMeasure && yIsMeasure) {
if (mark === mark_1.TICK) {
cfg[property] = 'horizontal';
}
else {
cfg[property] = 'vertical';
}
}

@@ -44,3 +54,3 @@ break;

}
exports.compileMarkConfig = compileMarkConfig;
exports.initMarkConfig = initMarkConfig;
//# sourceMappingURL=config.js.map

@@ -0,9 +1,7 @@

import {X, DETAIL} from '../channel';
import {Config} from '../config';
import {Encoding} from '../encoding';
import {Config} from '../config';
import {StackProperties} from './stack';
import {X, Y, DETAIL} from '../channel';
import {isAggregate, has} from '../encoding';
import {isMeasure} from '../fielddef';
import {POINT, LINE, TICK, CIRCLE, SQUARE, Mark} from '../mark';
import {POINT, LINE, TICK, CIRCLE, SQUARE, RULE, Mark} from '../mark';
import {contains, extend} from '../util';

@@ -14,3 +12,3 @@

*/
export function compileMarkConfig(mark: Mark, encoding: Encoding, config: Config, stack: StackProperties) {
export function initMarkConfig(mark: Mark, encoding: Encoding, config: Config) {
return extend(

@@ -22,4 +20,4 @@ ['filled', 'opacity', 'orient', 'align'].reduce(function(cfg, property: string) {

if (value === undefined) {
// Point and line are not filled by default
cfg[property] = mark !== POINT && mark !== LINE;
// Point, line, and rule are not filled by default
cfg[property] = mark !== POINT && mark !== LINE && mark !== RULE;
}

@@ -36,15 +34,22 @@ break;

case 'orient':
if (stack) {
// For stacked chart, explicitly specified orient property will be ignored.
cfg[property] = stack.groupbyChannel === Y ? 'horizontal' : undefined;
const xIsMeasure = isMeasure(encoding.x);
const yIsMeasure = isMeasure(encoding.y);
// When unambiguous, do not allow overriding
if (xIsMeasure && !yIsMeasure) {
if (mark === TICK) {
cfg[property] = 'vertical'; // implicitly vertical
} else {
cfg[property] = 'horizontal'; // implicitly horizontal
}
} else if (!xIsMeasure && yIsMeasure) {
if (mark === TICK) {
cfg[property] = 'horizontal';
} else {
cfg[property] = 'vertical';
}
}
if (value === undefined) {
cfg[property] = isMeasure(encoding[X]) && !isMeasure(encoding[Y]) ?
// horizontal if X is measure and Y is dimension or unspecified
'horizontal' :
// vertical (undefined) otherwise. This includes when
// - Y is measure and X is dimension or unspecified
// - both X and Y are measures or both are dimension
undefined; //
}
// In ambiguous cases (QxQ or OxO) use specified value
// (and implicitly vertical by default.)
break;

@@ -51,0 +56,0 @@ // text-only

"use strict";
var util = require('../util');
var util_1 = require('../util');
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var axis_1 = require('../axis');
var channel_1 = require('../channel');
var config_1 = require('../config');
var data_1 = require('../data');
var encoding_1 = require('../encoding');
var fielddef_1 = require('../fielddef');
var axis_1 = require('./axis');
var scale_1 = require('./scale');
var scale_1 = require('../scale');
var type_1 = require('../type');
var util_1 = require('../util');
var axis_2 = require('./axis');
var common_1 = require('./common');
function facetMixins(model, marks) {
var hasRow = model.has(channel_1.ROW), hasCol = model.has(channel_1.COLUMN);
if (model.has(channel_1.ROW) && !fielddef_1.isDimension(model.encoding().row)) {
util.error('Row encoding should be ordinal.');
var data_2 = require('./data/data');
var layout_1 = require('./layout');
var model_1 = require('./model');
var scale_2 = require('./scale');
var FacetModel = (function (_super) {
__extends(FacetModel, _super);
function FacetModel(spec, parent, parentGivenName) {
_super.call(this, spec, parent, parentGivenName);
var config = this._config = this._initConfig(spec.config, parent);
var child = this._child = common_1.buildModel(spec.spec, this, this.name('child'));
var facet = this._facet = this._initFacet(spec.facet);
this._scale = this._initScale(facet, config, child);
this._axis = this._initAxis(facet, config, child);
}
if (model.has(channel_1.COLUMN) && !fielddef_1.isDimension(model.encoding().column)) {
util.error('Col encoding should be ordinal.');
}
return {
marks: [].concat(getFacetGuideGroups(model), [getFacetGroup(model, marks)]),
scales: scale_1.compileScales(model),
axes: [].concat(hasRow && model.axis(channel_1.ROW) ? [axis_1.compileAxis(channel_1.ROW, model)] : [], hasCol && model.axis(channel_1.COLUMN) ? [axis_1.compileAxis(channel_1.COLUMN, model)] : [])
FacetModel.prototype._initConfig = function (specConfig, parent) {
return util_1.mergeDeep(util_1.duplicate(config_1.defaultConfig), specConfig, parent ? parent.config() : {});
};
}
exports.facetMixins = facetMixins;
function getCellAxes(model) {
var cellAxes = [];
if (model.has(channel_1.X) && model.axis(channel_1.X) && axis_1.gridShow(model, channel_1.X)) {
cellAxes.push(axis_1.compileInnerAxis(channel_1.X, model));
}
if (model.has(channel_1.Y) && model.axis(channel_1.Y) && axis_1.gridShow(model, channel_1.Y)) {
cellAxes.push(axis_1.compileInnerAxis(channel_1.Y, model));
}
return cellAxes;
}
function getFacetGroup(model, marks) {
var name = model.spec().name;
var facetGroup = {
name: (name ? name + '-' : '') + 'cell',
type: 'group',
from: {
data: model.dataTable(),
transform: [{
type: 'facet',
groupby: [].concat(model.has(channel_1.ROW) ? [model.field(channel_1.ROW)] : [], model.has(channel_1.COLUMN) ? [model.field(channel_1.COLUMN)] : [])
}]
},
properties: {
update: getFacetGroupProperties(model)
},
marks: marks
FacetModel.prototype._initFacet = function (facet) {
facet = util_1.duplicate(facet);
var model = this;
encoding_1.channelMappingForEach(this.channels(), facet, function (fieldDef, channel) {
if (!fielddef_1.isDimension(fieldDef)) {
model.addWarning(channel + ' encoding should be ordinal.');
}
if (fieldDef.type) {
fieldDef.type = type_1.getFullName(fieldDef.type);
}
});
return facet;
};
var cellAxes = getCellAxes(model);
if (cellAxes.length > 0) {
facetGroup.axes = cellAxes;
}
return facetGroup;
}
FacetModel.prototype._initScale = function (facet, config, child) {
return [channel_1.ROW, channel_1.COLUMN].reduce(function (_scale, channel) {
if (facet[channel]) {
var scaleSpec = facet[channel].scale || {};
_scale[channel] = util_1.extend({
type: scale_1.ScaleType.ORDINAL,
round: config.facet.scale.round,
padding: (channel === channel_1.ROW && child.has(channel_1.Y)) || (channel === channel_1.COLUMN && child.has(channel_1.X)) ?
config.facet.scale.padding : 0
}, scaleSpec);
}
return _scale;
}, {});
};
FacetModel.prototype._initAxis = function (facet, config, child) {
return [channel_1.ROW, channel_1.COLUMN].reduce(function (_axis, channel) {
if (facet[channel]) {
var axisSpec = facet[channel].axis;
if (axisSpec !== false) {
var modelAxis = _axis[channel] = util_1.extend({}, config.facet.axis, axisSpec === true ? {} : axisSpec || {});
if (channel === channel_1.ROW) {
var yAxis = child.axis(channel_1.Y);
if (yAxis && yAxis.orient !== axis_1.AxisOrient.RIGHT && !modelAxis.orient) {
modelAxis.orient = axis_1.AxisOrient.RIGHT;
}
if (child.has(channel_1.X) && !modelAxis.labelAngle) {
modelAxis.labelAngle = modelAxis.orient === axis_1.AxisOrient.RIGHT ? 90 : 270;
}
}
}
}
return _axis;
}, {});
};
FacetModel.prototype.facet = function () {
return this._facet;
};
FacetModel.prototype.has = function (channel) {
return !!this._facet[channel];
};
FacetModel.prototype.child = function () {
return this._child;
};
FacetModel.prototype.hasSummary = function () {
var summary = this.component.data.summary;
for (var i = 0; i < summary.length; i++) {
if (util_1.keys(summary[i].measures).length > 0) {
return true;
}
}
return false;
};
FacetModel.prototype.dataTable = function () {
return (this.hasSummary() ? data_1.SUMMARY : data_1.SOURCE) + '';
};
FacetModel.prototype.fieldDef = function (channel) {
return this.facet()[channel];
};
FacetModel.prototype.stack = function () {
return null;
};
FacetModel.prototype.parseData = function () {
this.child().parseData();
this.component.data = data_2.parseFacetData(this);
};
FacetModel.prototype.parseSelectionData = function () {
};
FacetModel.prototype.parseLayoutData = function () {
this.child().parseLayoutData();
this.component.layout = layout_1.parseFacetLayout(this);
};
FacetModel.prototype.parseScale = function () {
var child = this.child();
var model = this;
child.parseScale();
var scaleComponent = this.component.scale = scale_2.parseScaleComponent(this);
util_1.keys(child.component.scale).forEach(function (channel) {
if (true) {
scaleComponent[channel] = child.component.scale[channel];
util_1.vals(scaleComponent[channel]).forEach(function (scale) {
var scaleNameWithoutPrefix = scale.name.substr(child.name('').length);
var newName = model.scaleName(scaleNameWithoutPrefix);
child.renameScale(scale.name, newName);
scale.name = newName;
});
delete child.component.scale[channel];
}
});
};
FacetModel.prototype.parseMark = function () {
this.child().parseMark();
this.component.mark = util_1.extend({
name: this.name('cell'),
type: 'group',
from: util_1.extend(this.dataTable() ? { data: this.dataTable() } : {}, {
transform: [{
type: 'facet',
groupby: [].concat(this.has(channel_1.ROW) ? [this.field(channel_1.ROW)] : [], this.has(channel_1.COLUMN) ? [this.field(channel_1.COLUMN)] : [])
}]
}),
properties: {
update: getFacetGroupProperties(this)
}
}, this.child().assembleGroup());
};
FacetModel.prototype.parseAxis = function () {
this.child().parseAxis();
this.component.axis = axis_2.parseAxisComponent(this, [channel_1.ROW, channel_1.COLUMN]);
};
FacetModel.prototype.parseAxisGroup = function () {
var xAxisGroup = parseAxisGroup(this, channel_1.X);
var yAxisGroup = parseAxisGroup(this, channel_1.Y);
this.component.axisGroup = util_1.extend(xAxisGroup ? { x: xAxisGroup } : {}, yAxisGroup ? { y: yAxisGroup } : {});
};
FacetModel.prototype.parseGridGroup = function () {
var child = this.child();
this.component.gridGroup = util_1.extend(!child.has(channel_1.X) && this.has(channel_1.COLUMN) ? { column: getColumnGridGroups(this) } : {}, !child.has(channel_1.Y) && this.has(channel_1.ROW) ? { row: getRowGridGroups(this) } : {});
};
FacetModel.prototype.parseLegend = function () {
this.child().parseLegend();
this.component.legend = this._child.component.legend;
this._child.component.legend = {};
};
FacetModel.prototype.assembleParentGroupProperties = function () {
return null;
};
FacetModel.prototype.assembleData = function (data) {
data_2.assembleData(this, data);
return this._child.assembleData(data);
};
FacetModel.prototype.assembleLayout = function (layoutData) {
this._child.assembleLayout(layoutData);
return layout_1.assembleLayout(this, layoutData);
};
FacetModel.prototype.assembleMarks = function () {
return [].concat(util_1.vals(this.component.axisGroup), util_1.flatten(util_1.vals(this.component.gridGroup)), this.component.mark);
};
FacetModel.prototype.channels = function () {
return [channel_1.ROW, channel_1.COLUMN];
};
FacetModel.prototype.mapping = function () {
return this.facet();
};
FacetModel.prototype.isFacet = function () {
return true;
};
return FacetModel;
}(model_1.Model));
exports.FacetModel = FacetModel;
function getFacetGroupProperties(model) {
var facetGroupProperties = {
var child = model.child();
var mergedCellConfig = util_1.extend({}, child.config().cell, child.config().facet.cell);
return util_1.extend({
x: model.has(channel_1.COLUMN) ? {

@@ -69,38 +211,30 @@ scale: model.scaleName(channel_1.COLUMN),

} : { value: model.config().facet.scale.padding / 2 },
width: { field: { parent: 'cellWidth' } },
height: { field: { parent: 'cellHeight' } }
};
common_1.applyConfig(facetGroupProperties, model.config().cell, common_1.FILL_STROKE_CONFIG.concat(['clip']));
common_1.applyConfig(facetGroupProperties, model.config().facet.cell, common_1.FILL_STROKE_CONFIG.concat(['clip']));
return facetGroupProperties;
width: { field: { parent: model.child().sizeName('width') } },
height: { field: { parent: model.child().sizeName('height') } }
}, child.assembleParentGroupProperties(mergedCellConfig));
}
function getFacetGuideGroups(model) {
var rootAxesGroups = [];
if (model.has(channel_1.X)) {
if (model.axis(channel_1.X)) {
rootAxesGroups.push(getXAxesGroup(model));
function parseAxisGroup(model, channel) {
var axisGroup = null;
var child = model.child();
if (child.has(channel)) {
if (child.axis(channel)) {
if (true) {
axisGroup = channel === channel_1.X ? getXAxesGroup(model) : getYAxesGroup(model);
if (child.axis(channel) && axis_2.gridShow(child, channel)) {
child.component.axis[channel] = axis_2.parseInnerAxis(channel, child);
}
else {
delete child.component.axis[channel];
}
}
else {
}
}
}
else {
if (model.has(channel_1.ROW)) {
rootAxesGroups.push.apply(rootAxesGroups, getRowGridGroups(model));
}
}
if (model.has(channel_1.Y)) {
if (model.axis(channel_1.Y)) {
rootAxesGroups.push(getYAxesGroup(model));
}
}
else {
if (model.has(channel_1.COLUMN)) {
rootAxesGroups.push.apply(rootAxesGroups, getColumnGridGroups(model));
}
}
return rootAxesGroups;
return axisGroup;
}
function getXAxesGroup(model) {
var hasCol = model.has(channel_1.COLUMN);
var name = model.spec().name;
return util_1.extend({
name: (name ? name + '-' : '') + 'x-axes',
name: model.name('x-axes'),
type: 'group'

@@ -119,3 +253,3 @@ }, hasCol ? {

update: {
width: { field: { parent: 'cellWidth' } },
width: { field: { parent: model.child().sizeName('width') } },
height: {

@@ -132,12 +266,10 @@ field: { group: 'height' }

}
}
}, model.axis(channel_1.X) ? {
axes: [axis_1.compileAxis(channel_1.X, model)]
} : {});
},
axes: [axis_2.parseAxis(channel_1.X, model.child())]
});
}
function getYAxesGroup(model) {
var hasRow = model.has(channel_1.ROW);
var name = model.spec().name;
return util_1.extend({
name: (name ? name + '-' : '') + 'y-axes',
name: model.name('y-axes'),
type: 'group'

@@ -159,3 +291,3 @@ }, hasRow ? {

},
height: { field: { parent: 'cellHeight' } },
height: { field: { parent: model.child().sizeName('height') } },
y: hasRow ? {

@@ -170,11 +302,9 @@ scale: model.scaleName(channel_1.ROW),

},
}, model.axis(channel_1.Y) ? {
axes: [axis_1.compileAxis(channel_1.Y, model)]
} : {});
axes: [axis_2.parseAxis(channel_1.Y, model.child())]
});
}
function getRowGridGroups(model) {
var name = model.spec().name;
var facetGridConfig = model.config().facet.grid;
var rowGrid = {
name: (name ? name + '-' : '') + 'row-grid',
name: model.name('row-grid'),
type: 'rule',

@@ -200,3 +330,3 @@ from: {

return [rowGrid, {
name: (name ? name + '-' : '') + 'row-grid-end',
name: model.name('row-grid-end'),
type: 'rule',

@@ -216,6 +346,5 @@ properties: {

function getColumnGridGroups(model) {
var name = model.spec().name;
var facetGridConfig = model.config().facet.grid;
var columnGrid = {
name: (name ? name + '-' : '') + 'column-grid',
name: model.name('column-grid'),
type: 'rule',

@@ -241,3 +370,3 @@ from: {

return [columnGrid, {
name: (name ? name + '-' : '') + 'column-grid-end',
name: model.name('column-grid-end'),
type: 'rule',

@@ -244,0 +373,0 @@ properties: {

@@ -1,145 +0,358 @@

import * as util from '../util';
import {extend} from '../util';
import {COLUMN, ROW, X, Y} from '../channel';
import {isDimension} from '../fielddef';
import {Model} from './Model';
import {AxisOrient, AxisProperties} from '../axis';
import {COLUMN, ROW, X, Y, Channel} from '../channel';
import {defaultConfig, Config} from '../config';
import {SOURCE, SUMMARY} from '../data';
import {Facet} from '../facet';
import {channelMappingForEach} from '../encoding';
import {FieldDef, isDimension} from '../fielddef';
import {Scale, ScaleType} from '../scale';
import {FacetSpec} from '../spec';
import {getFullName} from '../type';
import {extend, keys, vals, flatten, duplicate, mergeDeep, Dict} from '../util';
import {VgData, VgMarkGroup} from '../vega.schema';
import {compileAxis, compileInnerAxis, gridShow} from './axis';
import {compileScales} from './scale';
import {applyConfig, FILL_STROKE_CONFIG} from './common';
import {parseAxis, parseInnerAxis, gridShow, parseAxisComponent} from './axis';
import {buildModel} from './common';
import {assembleData, parseFacetData} from './data/data';
import {assembleLayout, parseFacetLayout} from './layout';
import {Model} from './model';
import {parseScaleComponent} from './scale';
/**
* return mixins that contains marks, scales, and axes for the rootGroup
*/
export function facetMixins(model: Model, marks) {
const hasRow = model.has(ROW), hasCol = model.has(COLUMN);
export class FacetModel extends Model {
private _facet: Facet;
if (model.has(ROW) && !isDimension(model.encoding().row)) {
// TODO: add error to model instead
util.error('Row encoding should be ordinal.');
private _child: Model;
constructor(spec: FacetSpec, parent: Model, parentGivenName: string) {
super(spec, parent, parentGivenName);
// Config must be initialized before child as it gets cascaded to the child
const config = this._config = this._initConfig(spec.config, parent);
const child = this._child = buildModel(spec.spec, this, this.name('child'));
const facet = this._facet = this._initFacet(spec.facet);
this._scale = this._initScale(facet, config, child);
this._axis = this._initAxis(facet, config, child);
}
if (model.has(COLUMN) && !isDimension(model.encoding().column)) {
// TODO: add error to model instead
util.error('Col encoding should be ordinal.');
private _initConfig(specConfig: Config, parent: Model) {
return mergeDeep(duplicate(defaultConfig), specConfig, parent ? parent.config() : {});
}
return {
marks: [].concat(
getFacetGuideGroups(model),
[getFacetGroup(model, marks)]
),
// assuming equal cellWidth here
scales: compileScales(model),
axes: [].concat(
hasRow && model.axis(ROW) ? [compileAxis(ROW, model)] : [],
hasCol && model.axis(COLUMN) ? [compileAxis(COLUMN, model)] : []
)
};
}
private _initFacet(facet: Facet) {
// clone to prevent side effect to the original spec
facet = duplicate(facet);
function getCellAxes(model: Model) {
const cellAxes = [];
if (model.has(X) && model.axis(X) && gridShow(model, X)) {
cellAxes.push(compileInnerAxis(X, model));
const model = this;
channelMappingForEach(this.channels(), facet, function(fieldDef: FieldDef, channel: Channel) {
// TODO: if has no field / datum, then drop the field
if (!isDimension(fieldDef)) {
model.addWarning(channel + ' encoding should be ordinal.');
}
if (fieldDef.type) {
// convert short type to full type
fieldDef.type = getFullName(fieldDef.type);
}
});
return facet;
}
if (model.has(Y) && model.axis(Y) && gridShow(model, Y)) {
cellAxes.push(compileInnerAxis(Y, model));
private _initScale(facet: Facet, config: Config, child: Model): Dict<Scale> {
return [ROW, COLUMN].reduce(function(_scale, channel) {
if (facet[channel]) {
const scaleSpec = facet[channel].scale || {};
_scale[channel] = extend({
type: ScaleType.ORDINAL,
round: config.facet.scale.round,
// TODO: revise this rule for multiple level of nesting
padding: (channel === ROW && child.has(Y)) || (channel === COLUMN && child.has(X)) ?
config.facet.scale.padding : 0
}, scaleSpec);
}
return _scale;
}, {} as Dict<Scale>);
}
return cellAxes;
}
function getFacetGroup(model: Model, marks) {
const name = model.spec().name;
let facetGroup: any = {
name: (name ? name + '-' : '') + 'cell',
type: 'group',
from: {
data: model.dataTable(),
transform: [{
type: 'facet',
groupby: [].concat(
model.has(ROW) ? [model.field(ROW)] : [],
model.has(COLUMN) ? [model.field(COLUMN)] : []
)
}]
},
properties: {
update: getFacetGroupProperties(model)
},
marks: marks
};
private _initAxis(facet: Facet, config: Config, child: Model): Dict<AxisProperties> {
return [ROW, COLUMN].reduce(function(_axis, channel) {
if (facet[channel]) {
const axisSpec = facet[channel].axis;
if (axisSpec !== false) {
const modelAxis = _axis[channel] = extend({},
config.facet.axis,
axisSpec === true ? {} : axisSpec || {}
);
const cellAxes = getCellAxes(model);
if (cellAxes.length > 0) {
facetGroup.axes = cellAxes;
if (channel === ROW) {
const yAxis: any = child.axis(Y);
if (yAxis && yAxis.orient !== AxisOrient.RIGHT && !modelAxis.orient) {
modelAxis.orient = AxisOrient.RIGHT;
}
if( child.has(X) && !modelAxis.labelAngle) {
modelAxis.labelAngle = modelAxis.orient === AxisOrient.RIGHT ? 90 : 270;
}
}
}
}
return _axis;
}, {} as Dict<AxisProperties>);
}
return facetGroup;
public facet() {
return this._facet;
}
public has(channel: Channel): boolean {
return !!this._facet[channel];
}
public child() {
return this._child;
}
private hasSummary() {
const summary = this.component.data.summary;
for (let i = 0 ; i < summary.length ; i++) {
if (keys(summary[i].measures).length > 0) {
return true;
}
}
return false;
}
public dataTable(): string {
return (this.hasSummary() ? SUMMARY : SOURCE) + '';
}
public fieldDef(channel: Channel): FieldDef {
return this.facet()[channel];
}
public stack() {
return null; // this is only a property for UnitModel
}
public parseData() {
this.child().parseData();
this.component.data = parseFacetData(this);
}
public parseSelectionData() {
// TODO: @arvind can write this
// We might need to split this into compileSelectionData and compileSelectionSignals?
}
public parseLayoutData() {
this.child().parseLayoutData();
this.component.layout = parseFacetLayout(this);
}
public parseScale() {
const child = this.child();
const model = this;
child.parseScale();
// TODO: support scales for field reference of parent data (e.g., for SPLOM)
// First, add scale for row and column.
let scaleComponent = this.component.scale = parseScaleComponent(this);
// Then, move shared/union from its child spec.
keys(child.component.scale).forEach(function(channel) {
// TODO: correctly implement independent scale
if (true) { // if shared/union scale
scaleComponent[channel] = child.component.scale[channel];
// for each scale, need to rename
vals(scaleComponent[channel]).forEach(function(scale) {
const scaleNameWithoutPrefix = scale.name.substr(child.name('').length);
const newName = model.scaleName(scaleNameWithoutPrefix);
child.renameScale(scale.name, newName);
scale.name = newName;
});
// Once put in parent, just remove the child's scale.
delete child.component.scale[channel];
}
});
}
public parseMark() {
this.child().parseMark();
this.component.mark = extend(
{
name: this.name('cell'),
type: 'group',
from: extend(
this.dataTable() ? {data: this.dataTable()} : {},
{
transform: [{
type: 'facet',
groupby: [].concat(
this.has(ROW) ? [this.field(ROW)] : [],
this.has(COLUMN) ? [this.field(COLUMN)] : []
)
}]
}
),
properties: {
update: getFacetGroupProperties(this)
}
},
// Call child's assembleGroup to add marks, scales, axes, and legends.
// Note that we can call child's assembleGroup() here because parseMark()
// is the last method in compile() and thus the child is completely compiled
// at this point.
this.child().assembleGroup()
);
}
public parseAxis() {
this.child().parseAxis();
this.component.axis = parseAxisComponent(this, [ROW, COLUMN]);
}
public parseAxisGroup() {
// TODO: with nesting, we might need to consider calling child
// this.child().parseAxisGroup();
const xAxisGroup = parseAxisGroup(this, X);
const yAxisGroup = parseAxisGroup(this, Y);
this.component.axisGroup = extend(
xAxisGroup ? {x: xAxisGroup} : {},
yAxisGroup ? {y: yAxisGroup} : {}
);
}
public parseGridGroup() {
// TODO: with nesting, we might need to consider calling child
// this.child().parseGridGroup();
const child = this.child();
this.component.gridGroup = extend(
!child.has(X) && this.has(COLUMN) ? { column: getColumnGridGroups(this) } : {},
!child.has(Y) && this.has(ROW) ? { row: getRowGridGroups(this) } : {}
);
}
public parseLegend() {
this.child().parseLegend();
// TODO: support legend for independent non-position scale across facets
// TODO: support legend for field reference of parent data (e.g., for SPLOM)
// For now, assuming that non-positional scales are always shared across facets
// Thus, just move all legends from its child
this.component.legend = this._child.component.legend;
this._child.component.legend = {};
}
public assembleParentGroupProperties() {
return null;
}
public assembleData(data: VgData[]): VgData[] {
// Prefix traversal – parent data might be referred by children data
assembleData(this, data);
return this._child.assembleData(data);
}
public assembleLayout(layoutData: VgData[]): VgData[] {
// Postfix traversal – layout is assembled bottom-up
this._child.assembleLayout(layoutData);
return assembleLayout(this, layoutData);
}
public assembleMarks(): any[] {
return [].concat(
// axisGroup is a mapping to VgMarkGroup
vals(this.component.axisGroup),
flatten(vals(this.component.gridGroup)),
this.component.mark
);
}
public channels() {
return [ROW, COLUMN];
}
protected mapping() {
return this.facet();
}
public isFacet() {
return true;
}
}
function getFacetGroupProperties(model: Model) {
let facetGroupProperties: any = {
x: model.has(COLUMN) ? {
scale: model.scaleName(COLUMN),
field: model.field(COLUMN),
// TODO: move the rest of the file into FacetModel if possible
function getFacetGroupProperties(model: FacetModel) {
const child = model.child();
const mergedCellConfig = extend({}, child.config().cell, child.config().facet.cell);
return extend({
x: model.has(COLUMN) ? {
scale: model.scaleName(COLUMN),
field: model.field(COLUMN),
// offset by the padding
offset: model.scale(COLUMN).padding / 2
} : {value: model.config().facet.scale.padding / 2},
y: model.has(ROW) ? {
scale: model.scaleName(ROW),
field: model.field(ROW),
// offset by the padding
offset: model.scale(COLUMN).padding / 2
offset: model.scale(ROW).padding / 2
} : {value: model.config().facet.scale.padding / 2},
y: model.has(ROW) ? {
scale: model.scaleName(ROW),
field: model.field(ROW),
// offset by the padding
offset: model.scale(ROW).padding / 2
} : {value: model.config().facet.scale.padding / 2},
width: {field: {parent: model.child().sizeName('width')}},
height: {field: {parent: model.child().sizeName('height')}}
},
child.assembleParentGroupProperties(mergedCellConfig)
);
}
width: {field: {parent: 'cellWidth'}},
height: {field: {parent: 'cellHeight'}}
};
function parseAxisGroup(model: FacetModel, channel: Channel) {
// TODO: add a case where inner spec is not a unit (facet/layer/concat)
let axisGroup = null;
// apply both config from cell and facet.cell (with higher precedence for facet.cell)
applyConfig(facetGroupProperties, model.config().cell, FILL_STROKE_CONFIG.concat(['clip']));
applyConfig(facetGroupProperties, model.config().facet.cell, FILL_STROKE_CONFIG.concat(['clip']));
const child = model.child();
if (child.has(channel)) {
if (child.axis(channel)) {
if (true) { // the channel has shared axes
return facetGroupProperties;
}
// add a group for the shared axes
axisGroup = channel === X ? getXAxesGroup(model) : getYAxesGroup(model);
/**
* Return groups of axes or manually drawn grids.
*/
function getFacetGuideGroups(model: Model) {
let rootAxesGroups = [] ;
if (model.has(X)) {
if (model.axis(X)) {
rootAxesGroups.push(getXAxesGroup(model));
if (child.axis(channel) && gridShow(child, channel)) { // show inner grid
// add inner axis (aka axis that shows only grid to )
child.component.axis[channel] = parseInnerAxis(channel, child);
} else {
delete child.component.axis[channel];
}
} else {
// TODO: implement independent axes support
}
}
} else {
// TODO: consider if row has axis and if row's axis.grid is true
if (model.has(ROW)) {
// manually draw grid (use apply to push all members of an array)
rootAxesGroups.push.apply(rootAxesGroups, getRowGridGroups(model));
}
}
if (model.has(Y)) {
if (model.axis(Y)) {
rootAxesGroups.push(getYAxesGroup(model));
}
} else {
// TODO: consider if column has axis and if column's axis.grid is true
if (model.has(COLUMN)) {
// manually draw grid (use apply to push all members of an array)
rootAxesGroups.push.apply(rootAxesGroups, getColumnGridGroups(model));
}
}
return rootAxesGroups;
return axisGroup;
}
function getXAxesGroup(model: Model) { // TODO: VgMarks
function getXAxesGroup(model: FacetModel): VgMarkGroup {
const hasCol = model.has(COLUMN);
const name = model.spec().name;
return extend(
{
name: (name ? name + '-' : '') + 'x-axes',
name: model.name('x-axes'),
type: 'group'

@@ -160,3 +373,3 @@ },

update: {
width: {field: {parent: 'cellWidth'}},
width: {field: {parent: model.child().sizeName('width')}},
height: {

@@ -175,16 +388,13 @@ field: {group: 'height'}

}
}
},
model.axis(X) ? {
axes: [compileAxis(X, model)]
}: {}
},
axes: [parseAxis(X, model.child())]
}
);
}
function getYAxesGroup(model: Model) { // TODO: VgMarks
function getYAxesGroup(model: FacetModel): VgMarkGroup {
const hasRow = model.has(ROW);
const name = model.spec().name;
return extend(
{
name: (name ? name + '-' : '') + 'y-axes',
name: model.name('y-axes'),
type: 'group'

@@ -208,3 +418,3 @@ },

},
height: {field: {parent: 'cellHeight'}},
height: {field: {parent: model.child().sizeName('height')}},
y: hasRow ? {

@@ -221,6 +431,4 @@ scale: model.scaleName(ROW),

},
},
model.axis(Y) ? {
axes: [compileAxis(Y, model)]
}: {}
axes: [parseAxis(Y, model.child())]
}
);

@@ -230,7 +438,6 @@ }

function getRowGridGroups(model: Model): any[] { // TODO: VgMarks
const name = model.spec().name;
const facetGridConfig = model.config().facet.grid;
const rowGrid = {
name: (name ? name + '-' : '') + 'row-grid',
name: model.name('row-grid'),
type: 'rule',

@@ -257,3 +464,3 @@ from: {

return [rowGrid, {
name: (name ? name + '-' : '') + 'row-grid-end',
name: model.name('row-grid-end'),
type: 'rule',

@@ -274,7 +481,6 @@ properties: {

function getColumnGridGroups(model: Model): any { // TODO: VgMarks
const name = model.spec().name;
const facetGridConfig = model.config().facet.grid;
const columnGrid = {
name: (name ? name + '-' : '') + 'column-grid',
name: model.name('column-grid'),
type: 'rule',

@@ -301,3 +507,3 @@ from: {

return [columnGrid, {
name: (name ? name + '-' : '') + 'column-grid-end',
name: model.name('column-grid-end'),
type: 'rule',

@@ -304,0 +510,0 @@ properties: {

"use strict";
var channel_1 = require('../channel');
var data_1 = require('../data');
var scale_1 = require('../scale');
var util_1 = require('../util');
var mark_1 = require('../mark');
var time_1 = require('./time');
function compileLayoutData(model) {
var distinctSummary = [channel_1.X, channel_1.Y, channel_1.ROW, channel_1.COLUMN].reduce(function (summary, channel) {
if (model.has(channel) && model.isOrdinalScale(channel)) {
var scale = model.scale(channel);
if (!(scale.domain instanceof Array)) {
summary.push({
field: model.field(channel),
ops: ['distinct']
});
function assembleLayout(model, layoutData) {
var layoutComponent = model.component.layout;
if (!layoutComponent.width && !layoutComponent.height) {
return layoutData;
}
if (true) {
var distinctFields = util_1.keys(util_1.extend(layoutComponent.width.distinct, layoutComponent.height.distinct));
var formula = layoutComponent.width.formula.concat(layoutComponent.height.formula)
.map(function (formula) {
return util_1.extend({ type: 'formula' }, formula);
});
return [
distinctFields.length > 0 ? {
name: model.dataName(data_1.LAYOUT),
source: model.dataTable(),
transform: [{
type: 'aggregate',
summarize: distinctFields.map(function (field) {
return { field: field, ops: ['distinct'] };
})
}].concat(formula)
} : {
name: model.dataName(data_1.LAYOUT),
values: [{}],
transform: formula
}
}
return summary;
}, []);
var cellWidthFormula = scaleWidthFormula(model, channel_1.X, model.cellWidth());
var cellHeightFormula = scaleWidthFormula(model, channel_1.Y, model.cellHeight());
var isFacet = model.has(channel_1.COLUMN) || model.has(channel_1.ROW);
var formulas = [{
type: 'formula',
field: 'cellWidth',
expr: cellWidthFormula
}, {
type: 'formula',
field: 'cellHeight',
expr: cellHeightFormula
}, {
type: 'formula',
field: 'width',
expr: isFacet ?
facetScaleWidthFormula(model, channel_1.COLUMN, 'datum.cellWidth') :
cellWidthFormula
}, {
type: 'formula',
field: 'height',
expr: isFacet ?
facetScaleWidthFormula(model, channel_1.ROW, 'datum.cellHeight') :
cellHeightFormula
}];
return distinctSummary.length > 0 ? {
name: data_1.LAYOUT,
source: model.dataTable(),
transform: [].concat([{
type: 'aggregate',
summarize: distinctSummary
}], formulas)
} : {
name: data_1.LAYOUT,
values: [{}],
transform: formulas
];
}
}
exports.assembleLayout = assembleLayout;
function parseUnitLayout(model) {
return {
width: parseUnitSizeLayout(model, channel_1.X),
height: parseUnitSizeLayout(model, channel_1.Y)
};
}
exports.compileLayoutData = compileLayoutData;
function cardinalityFormula(model, channel) {
var scale = model.scale(channel);
if (scale.domain instanceof Array) {
return scale.domain.length;
}
var timeUnit = model.fieldDef(channel).timeUnit;
var timeUnitDomain = timeUnit ? time_1.rawDomain(timeUnit, channel) : null;
return timeUnitDomain !== null ? timeUnitDomain.length :
model.field(channel, { datum: true, prefn: 'distinct_' });
exports.parseUnitLayout = parseUnitLayout;
function parseUnitSizeLayout(model, channel) {
var cellConfig = model.config().cell;
var nonOrdinalSize = channel === channel_1.X ? cellConfig.width : cellConfig.height;
return {
distinct: getDistinct(model, channel),
formula: [{
field: model.channelSizeName(channel),
expr: unitSizeExpr(model, channel, nonOrdinalSize)
}]
};
}
function scaleWidthFormula(model, channel, nonOrdinalSize) {
function unitSizeExpr(model, channel, nonOrdinalSize) {
if (model.has(channel)) {

@@ -86,13 +75,83 @@ if (model.isOrdinalScale(channel)) {

}
function facetScaleWidthFormula(model, channel, innerWidth) {
function parseFacetLayout(model) {
return {
width: parseFacetSizeLayout(model, channel_1.COLUMN),
height: parseFacetSizeLayout(model, channel_1.ROW)
};
}
exports.parseFacetLayout = parseFacetLayout;
function parseFacetSizeLayout(model, channel) {
var childLayoutComponent = model.child().component.layout;
var sizeType = channel === channel_1.ROW ? 'height' : 'width';
var childSizeComponent = childLayoutComponent[sizeType];
if (true) {
var distinct = util_1.extend(getDistinct(model, channel), childSizeComponent.distinct);
var formula = childSizeComponent.formula.concat([{
field: model.channelSizeName(channel),
expr: facetSizeFormula(model, channel, model.child().channelSizeName(channel))
}]);
delete childLayoutComponent[sizeType];
return {
distinct: distinct,
formula: formula
};
}
}
function facetSizeFormula(model, channel, innerSize) {
var scale = model.scale(channel);
if (model.has(channel)) {
var cardinality = scale.domain instanceof Array ? scale.domain.length :
model.field(channel, { datum: true, prefn: 'distinct_' });
return '(' + innerWidth + ' + ' + scale.padding + ')' + ' * ' + cardinality;
return '(datum.' + innerSize + ' + ' + scale.padding + ')' + ' * ' + cardinalityFormula(model, channel);
}
else {
return innerWidth + ' + ' + model.config().facet.scale.padding;
return 'datum.' + innerSize + ' + ' + model.config().facet.scale.padding;
}
}
function parseLayerLayout(model) {
return {
width: parseLayerSizeLayout(model, channel_1.X),
height: parseLayerSizeLayout(model, channel_1.Y)
};
}
exports.parseLayerLayout = parseLayerLayout;
function parseLayerSizeLayout(model, channel) {
if (true) {
var childLayoutComponent = model.children()[0].component.layout;
var sizeType_1 = channel === channel_1.Y ? 'height' : 'width';
var childSizeComponent = childLayoutComponent[sizeType_1];
var distinct = childSizeComponent.distinct;
var formula = [{
field: model.channelSizeName(channel),
expr: childSizeComponent.formula[0].expr
}];
model.children().forEach(function (child) {
delete child.component.layout[sizeType_1];
});
return {
distinct: distinct,
formula: formula
};
}
}
function getDistinct(model, channel) {
if (model.has(channel) && model.isOrdinalScale(channel)) {
var scale = model.scale(channel);
if (scale.type === scale_1.ScaleType.ORDINAL && !(scale.domain instanceof Array)) {
var distinctField = model.field(channel);
var distinct = {};
distinct[distinctField] = true;
return distinct;
}
}
return {};
}
function cardinalityFormula(model, channel) {
var scale = model.scale(channel);
if (scale.domain instanceof Array) {
return scale.domain.length;
}
var timeUnit = model.fieldDef(channel).timeUnit;
var timeUnitDomain = timeUnit ? time_1.rawDomain(timeUnit, channel) : null;
return timeUnitDomain !== null ? timeUnitDomain.length :
model.field(channel, { datum: true, prefn: 'distinct_' });
}
//# sourceMappingURL=layout.js.map

@@ -1,85 +0,89 @@

import {VgData} from '../vega.schema';
import {Model} from './Model';
import {Channel, X, Y, ROW, COLUMN} from '../channel';
import {LAYOUT} from '../data';
import {ScaleType} from '../scale';
import {Formula} from '../transform';
import {extend, keys, StringSet} from '../util';
import {VgData} from '../vega.schema';
import {FacetModel} from './facet';
import {LayerModel} from './layer';
import {TEXT as TEXT_MARK} from '../mark';
import {Model} from './model';
import {rawDomain} from './time';
import {UnitModel} from './unit';
export function compileLayoutData(model: Model): VgData {
/* Aggregation summary object for fields with ordinal scales
* that wee need to calculate cardinality for. */
const distinctSummary = [X, Y, ROW, COLUMN].reduce(function(summary, channel: Channel) {
if (model.has(channel) && model.isOrdinalScale(channel)) {
const scale = model.scale(channel);
// FIXME: for nesting x and y, we need to declare x,y layout separately before joining later
// For now, let's always assume shared scale
export interface LayoutComponent {
width: SizeComponent;
height: SizeComponent;
}
if (!(scale.domain instanceof Array)) {
// if explicit domain is declared, use array length
summary.push({
field: model.field(channel),
ops: ['distinct']
});
}
}
return summary;
}, []);
export interface SizeComponent {
/** Field that we need to calculate distinct */
distinct: StringSet;
/** Array of formulas */
formula: Formula[];
}
// TODO: handle "fit" mode
const cellWidthFormula = scaleWidthFormula(model, X, model.cellWidth());
const cellHeightFormula = scaleWidthFormula(model, Y, model.cellHeight());
const isFacet = model.has(COLUMN) || model.has(ROW);
export function assembleLayout(model: Model, layoutData: VgData[]): VgData[] {
const layoutComponent = model.component.layout;
if (!layoutComponent.width && !layoutComponent.height) {
return layoutData; // Do nothing
}
const formulas = [{
type: 'formula',
field: 'cellWidth',
expr: cellWidthFormula
},{
type: 'formula',
field: 'cellHeight',
expr: cellHeightFormula
},{
type: 'formula',
field: 'width',
expr: isFacet ?
facetScaleWidthFormula(model, COLUMN, 'datum.cellWidth') :
cellWidthFormula
},{
type: 'formula',
field: 'height',
expr: isFacet ?
facetScaleWidthFormula(model, ROW, 'datum.cellHeight') :
cellHeightFormula
}];
if (true) { // if both are shared scale, we can simply merge data source for width and for height
const distinctFields = keys(extend(layoutComponent.width.distinct, layoutComponent.height.distinct));
const formula = layoutComponent.width.formula.concat(layoutComponent.height.formula)
.map(function(formula) {
return extend({type: 'formula'}, formula);
});
return distinctSummary.length > 0 ? {
name: LAYOUT,
source: model.dataTable(),
transform: [].concat(
[{
type: 'aggregate',
summarize: distinctSummary
}],
formulas)
} : {
name: LAYOUT,
values: [{}],
transform: formulas
return [
distinctFields.length > 0 ? {
name: model.dataName(LAYOUT),
source: model.dataTable(),
transform: [{
type: 'aggregate',
summarize: distinctFields.map(function(field) {
return { field: field, ops: ['distinct'] };
})
}].concat(formula)
} : {
name: model.dataName(LAYOUT),
values: [{}],
transform: formula
}
];
}
// FIXME: implement
// otherwise, we need to join width and height (cross)
}
// FIXME: for nesting x and y, we need to declare x,y layout separately before joining later
// For now, let's always assume shared scale
export function parseUnitLayout(model: UnitModel): LayoutComponent {
return {
width: parseUnitSizeLayout(model, X),
height: parseUnitSizeLayout(model, Y)
};
}
function cardinalityFormula(model: Model, channel: Channel) {
const scale = model.scale(channel);
if (scale.domain instanceof Array) {
return scale.domain.length;
}
function parseUnitSizeLayout(model: UnitModel, channel: Channel): SizeComponent {
// TODO: think about whether this config has to be the cell or facet cell config
const cellConfig = model.config().cell;
const nonOrdinalSize = channel === X ? cellConfig.width : cellConfig.height;
const timeUnit = model.fieldDef(channel).timeUnit;
const timeUnitDomain = timeUnit ? rawDomain(timeUnit, channel) : null;
return timeUnitDomain !== null ? timeUnitDomain.length :
model.field(channel, {datum: true, prefn: 'distinct_'});
return {
distinct: getDistinct(model, channel),
formula: [{
field: model.channelSizeName(channel),
expr: unitSizeExpr(model, channel, nonOrdinalSize)
}]
};
}
function scaleWidthFormula(model: Model, channel: Channel, nonOrdinalSize: number): string {
function unitSizeExpr(model: UnitModel, channel: Channel, nonOrdinalSize: number): string {
if (model.has(channel)) {

@@ -89,4 +93,4 @@ if (model.isOrdinalScale(channel)) {

return '(' + cardinalityFormula(model, channel) +
' + ' + scale.padding +
') * ' + scale.bandSize;
' + ' + scale.padding +
') * ' + scale.bandSize;
} else {

@@ -104,12 +108,101 @@ return nonOrdinalSize + '';

function facetScaleWidthFormula(model: Model, channel: Channel, innerWidth: string) {
export function parseFacetLayout(model: FacetModel): LayoutComponent {
return {
width: parseFacetSizeLayout(model, COLUMN),
height: parseFacetSizeLayout(model, ROW)
};
}
function parseFacetSizeLayout(model: FacetModel, channel: Channel): SizeComponent {
const childLayoutComponent = model.child().component.layout;
const sizeType = channel === ROW ? 'height' : 'width';
const childSizeComponent: SizeComponent = childLayoutComponent[sizeType];
if (true) { // assume shared scale
// For shared scale, we can simply merge the layout into one data source
const distinct = extend(getDistinct(model, channel), childSizeComponent.distinct);
const formula = childSizeComponent.formula.concat([{
field: model.channelSizeName(channel),
expr: facetSizeFormula(model, channel, model.child().channelSizeName(channel))
}]);
delete childLayoutComponent[sizeType];
return {
distinct: distinct,
formula: formula
};
}
// FIXME implement independent scale as well
// TODO: - also consider when children have different data source
}
function facetSizeFormula(model: Model, channel: Channel, innerSize: string) {
const scale = model.scale(channel);
if (model.has(channel)) {
const cardinality = scale.domain instanceof Array ? scale.domain.length :
model.field(channel, {datum: true, prefn: 'distinct_'});
return '(' + innerWidth + ' + ' + scale.padding + ')' + ' * ' + cardinality;
return '(datum.' + innerSize + ' + ' + scale.padding + ')' + ' * ' + cardinalityFormula(model, channel);
} else {
return innerWidth + ' + ' + model.config().facet.scale.padding; // need to add outer padding for facet
return 'datum.' + innerSize + ' + ' + model.config().facet.scale.padding; // need to add outer padding for facet
}
}
export function parseLayerLayout(model: LayerModel): LayoutComponent {
return {
width: parseLayerSizeLayout(model, X),
height: parseLayerSizeLayout(model, Y)
};
}
function parseLayerSizeLayout(model: LayerModel, channel: Channel): SizeComponent {
if (true) {
// For shared scale, we can simply merge the layout into one data source
// TODO: don't just take the layout from the first child
const childLayoutComponent = model.children()[0].component.layout;
const sizeType = channel === Y ? 'height' : 'width';
const childSizeComponent: SizeComponent = childLayoutComponent[sizeType];
const distinct = childSizeComponent.distinct;
const formula = [{
field: model.channelSizeName(channel),
expr: childSizeComponent.formula[0].expr
}];
model.children().forEach((child) => {
delete child.component.layout[sizeType];
});
return {
distinct: distinct,
formula: formula
};
}
}
function getDistinct(model: Model, channel: Channel): StringSet {
if (model.has(channel) && model.isOrdinalScale(channel)) {
const scale = model.scale(channel);
if (scale.type === ScaleType.ORDINAL && !(scale.domain instanceof Array)) {
// if explicit domain is declared, use array length
const distinctField = model.field(channel);
let distinct: StringSet = {};
distinct[distinctField] = true;
return distinct;
}
}
return {};
}
// TODO: rename to cardinalityExpr
function cardinalityFormula(model: Model, channel: Channel) {
const scale = model.scale(channel);
if (scale.domain instanceof Array) {
return scale.domain.length;
}
const timeUnit = model.fieldDef(channel).timeUnit;
const timeUnitDomain = timeUnit ? rawDomain(timeUnit, channel) : null;
return timeUnitDomain !== null ? timeUnitDomain.length :
model.field(channel, {datum: true, prefn: 'distinct_'});
}

@@ -5,33 +5,36 @@ "use strict";

var mark_1 = require('../mark');
var type_1 = require('../type');
var util_1 = require('../util');
var common_1 = require('./common');
var type_1 = require('../type');
var scale_1 = require('./scale');
function compileLegends(model) {
var defs = [];
if (model.has(channel_1.COLOR) && model.legend(channel_1.COLOR)) {
var fieldDef = model.fieldDef(channel_1.COLOR);
var scale = model.scaleName(useColorLegendScale(fieldDef) ?
scale_1.COLOR_LEGEND :
channel_1.COLOR);
var def = model.config().mark.filled ? { fill: scale } : { stroke: scale };
defs.push(compileLegend(model, channel_1.COLOR, def));
function parseLegendComponent(model) {
return [channel_1.COLOR, channel_1.SIZE, channel_1.SHAPE].reduce(function (legendComponent, channel) {
if (model.legend(channel)) {
legendComponent[channel] = parseLegend(model, channel);
}
return legendComponent;
}, {});
}
exports.parseLegendComponent = parseLegendComponent;
function getLegendDefWithScale(model, channel) {
switch (channel) {
case channel_1.COLOR:
var fieldDef = model.fieldDef(channel_1.COLOR);
var scale = model.scaleName(useColorLegendScale(fieldDef) ?
scale_1.COLOR_LEGEND :
channel_1.COLOR);
return model.config().mark.filled ? { fill: scale } : { stroke: scale };
case channel_1.SIZE:
return { size: model.scaleName(channel_1.SIZE) };
case channel_1.SHAPE:
return { shape: model.scaleName(channel_1.SHAPE) };
}
if (model.has(channel_1.SIZE) && model.legend(channel_1.SIZE)) {
defs.push(compileLegend(model, channel_1.SIZE, {
size: model.scaleName(channel_1.SIZE)
}));
}
if (model.has(channel_1.SHAPE) && model.legend(channel_1.SHAPE)) {
defs.push(compileLegend(model, channel_1.SHAPE, {
shape: model.scaleName(channel_1.SHAPE)
}));
}
return defs;
return null;
}
exports.compileLegends = compileLegends;
function compileLegend(model, channel, def) {
function parseLegend(model, channel) {
var fieldDef = model.fieldDef(channel);
var legend = model.legend(channel);
var def = getLegendDefWithScale(model, channel);
def.title = title(legend, fieldDef);
def.offset = offset(legend, fieldDef);
util_1.extend(def, formatMixins(legend, model, channel));

@@ -49,3 +52,3 @@ ['orient', 'values'].forEach(function (property) {

props[group];
if (value !== undefined) {
if (value !== undefined && util_1.keys(value).length > 0) {
def.properties = def.properties || {};

@@ -57,3 +60,18 @@ def.properties[group] = value;

}
exports.compileLegend = compileLegend;
exports.parseLegend = parseLegend;
function offset(legend, fieldDef) {
if (legend.offset !== undefined) {
return legend.offset;
}
return 0;
}
exports.offset = offset;
function orient(legend, fieldDef) {
var orient = legend.orient;
if (orient) {
return orient;
}
return 'vertical';
}
exports.orient = orient;
function title(legend, fieldDef) {

@@ -83,2 +101,3 @@ if (typeof legend !== 'boolean' && legend.title) {

var mark = model.mark();
var legend = model.legend(channel);
switch (mark) {

@@ -100,3 +119,7 @@ case mark_1.BAR:

var filled = model.config().mark.filled;
common_1.applyMarkConfig(symbols, model, util_1.without(common_1.FILL_STROKE_CONFIG, [filled ? 'fill' : 'stroke']));
var config = channel === channel_1.COLOR ?
util_1.without(common_1.FILL_STROKE_CONFIG, [filled ? 'fill' : 'stroke', 'strokeDash', 'strokeDashOffset']) :
util_1.without(common_1.FILL_STROKE_CONFIG, ['strokeDash', 'strokeDashOffset']);
config = util_1.without(config, ['strokeDash', 'strokeDashOffset']);
common_1.applyMarkConfig(symbols, model, config);
if (filled) {

@@ -126,2 +149,14 @@ symbols.strokeWidth = { value: 0 };

}
if (legend.symbolColor !== undefined) {
symbols.fill = { value: legend.symbolColor };
}
if (legend.symbolShape !== undefined) {
symbols.shape = { value: legend.symbolShape };
}
if (legend.symbolSize !== undefined) {
symbols.size = { value: legend.symbolSize };
}
if (legend.symbolStrokeWidth !== undefined) {
symbols.strokeWidth = { value: legend.symbolStrokeWidth };
}
symbols = util_1.extend(symbols, symbolsSpec || {});

@@ -131,6 +166,8 @@ return util_1.keys(symbols).length > 0 ? symbols : undefined;

properties.symbols = symbols;
function labels(fieldDef, symbolsSpec, model, channel) {
function labels(fieldDef, labelsSpec, model, channel) {
var legend = model.legend(channel);
var labels = {};
if (channel === channel_1.COLOR) {
if (fieldDef.type === type_1.ORDINAL) {
return {
labelsSpec = util_1.extend({
text: {

@@ -140,6 +177,6 @@ scale: model.scaleName(scale_1.COLOR_LEGEND),

}
};
}, labelsSpec || {});
}
else if (fieldDef.bin) {
return {
labelsSpec = util_1.extend({
text: {

@@ -149,16 +186,51 @@ scale: model.scaleName(scale_1.COLOR_LEGEND_LABEL),

}
};
}, labelsSpec || {});
}
else if (fieldDef.timeUnit) {
return {
labelsSpec = util_1.extend({
text: {
template: '{{ datum.data | time:\'' + common_1.timeFormat(model, channel) + '\'}}'
}
};
}, labelsSpec || {});
}
}
return undefined;
if (legend.labelAlign !== undefined) {
labels.align = { value: legend.labelAlign };
}
if (legend.labelColor !== undefined) {
labels.stroke = { value: legend.labelColor };
}
if (legend.labelFont !== undefined) {
labels.font = { value: legend.labelFont };
}
if (legend.labelFontSize !== undefined) {
labels.fontSize = { value: legend.labelFontSize };
}
if (legend.labelBaseline !== undefined) {
labels.baseline = { value: legend.labelBaseline };
}
labels = util_1.extend(labels, labelsSpec || {});
return util_1.keys(labels).length > 0 ? labels : undefined;
}
properties.labels = labels;
})(properties || (properties = {}));
function title(fieldDef, titleSpec, model, channel) {
var legend = model.legend(channel);
var titles = {};
if (legend.titleColor !== undefined) {
titles.stroke = { value: legend.titleColor };
}
if (legend.titleFont !== undefined) {
titles.font = { value: legend.titleFont };
}
if (legend.titleFontSize !== undefined) {
titles.fontSize = { value: legend.titleFontSize };
}
if (legend.titleFontWeight !== undefined) {
titles.fontWeight = { value: legend.titleFontWeight };
}
titles = util_1.extend(titles, titleSpec || {});
return util_1.keys(titles).length > 0 ? titles : undefined;
}
properties.title = title;
})(properties = exports.properties || (exports.properties = {}));
//# sourceMappingURL=legend.js.map

@@ -0,52 +1,57 @@

import {COLOR, SIZE, SHAPE, Channel} from '../channel';
import {FieldDef} from '../fielddef';
import {LegendProperties} from '../legend';
import {COLOR, SIZE, SHAPE, Channel} from '../channel';
import {title as fieldTitle} from '../fielddef';
import {AREA, BAR, TICK, TEXT, LINE, POINT, CIRCLE, SQUARE} from '../mark';
import {extend, keys, without} from '../util';
import {Model} from './Model';
import {ORDINAL} from '../type';
import {extend, keys, without, Dict} from '../util';
import {applyMarkConfig, FILL_STROKE_CONFIG, formatMixins as utilFormatMixins, timeFormat} from './common';
import {ORDINAL} from '../type';
import {COLOR_LEGEND, COLOR_LEGEND_LABEL} from './scale';
import {UnitModel} from './unit';
import {VgLegend} from '../vega.schema';
export function compileLegends(model: Model) {
let defs = [];
if (model.has(COLOR) && model.legend(COLOR)) {
const fieldDef = model.fieldDef(COLOR);
const scale = model.scaleName(useColorLegendScale(fieldDef) ?
// To produce ordinal legend (list, rather than linear range) with correct labels:
// - For an ordinal field, provide an ordinal scale that maps rank values to field values
// - For a field with bin or timeUnit, provide an identity ordinal scale
// (mapping the field values to themselves)
COLOR_LEGEND :
COLOR
);
export function parseLegendComponent(model: UnitModel): Dict<VgLegend> {
return [COLOR, SIZE, SHAPE].reduce(function(legendComponent, channel) {
if (model.legend(channel)) {
legendComponent[channel] = parseLegend(model, channel);
}
return legendComponent;
}, {} as Dict<VgLegend>);
}
const def = model.config().mark.filled ? { fill: scale } : { stroke: scale };
defs.push(compileLegend(model, COLOR, def));
}
function getLegendDefWithScale(model: UnitModel, channel: Channel): VgLegend {
switch (channel) {
case COLOR:
const fieldDef = model.fieldDef(COLOR);
const scale = model.scaleName(useColorLegendScale(fieldDef) ?
// To produce ordinal legend (list, rather than linear range) with correct labels:
// - For an ordinal field, provide an ordinal scale that maps rank values to field values
// - For a field with bin or timeUnit, provide an identity ordinal scale
// (mapping the field values to themselves)
COLOR_LEGEND :
COLOR
);
if (model.has(SIZE) && model.legend(SIZE)) {
defs.push(compileLegend(model, SIZE, {
size: model.scaleName(SIZE)
}));
return model.config().mark.filled ? { fill: scale } : { stroke: scale };
case SIZE:
return { size: model.scaleName(SIZE) };
case SHAPE:
return { shape: model.scaleName(SHAPE) };
}
if (model.has(SHAPE) && model.legend(SHAPE)) {
defs.push(compileLegend(model, SHAPE, {
shape: model.scaleName(SHAPE)
}));
}
return defs;
return null;
}
export function compileLegend(model: Model, channel: Channel, def) {
export function parseLegend(model: UnitModel, channel: Channel): VgLegend {
const fieldDef = model.fieldDef(channel);
const legend = model.legend(channel);
let def: VgLegend = getLegendDefWithScale(model, channel);
// 1.1 Add properties with special rules
def.title = title(legend, fieldDef);
def.offset = offset(legend, fieldDef);
extend(def, formatMixins(legend, model, channel));

@@ -68,3 +73,3 @@

props[group]; // no rule -- just default values
if (value !== undefined) {
if (value !== undefined && keys(value).length > 0) {
def.properties = def.properties || {};

@@ -78,2 +83,17 @@ def.properties[group] = value;

export function offset(legend: LegendProperties, fieldDef: FieldDef) {
if (legend.offset !== undefined) {
return legend.offset;
}
return 0;
}
export function orient(legend: LegendProperties, fieldDef: FieldDef) {
const orient = legend.orient;
if (orient) {
return orient;
}
return 'vertical';
}
export function title(legend: LegendProperties, fieldDef: FieldDef) {

@@ -87,3 +107,3 @@ if (typeof legend !== 'boolean' && legend.title) {

export function formatMixins(legend: LegendProperties, model: Model, channel: Channel) {
export function formatMixins(legend: LegendProperties, model: UnitModel, channel: Channel) {
const fieldDef = model.fieldDef(channel);

@@ -104,6 +124,7 @@

namespace properties {
export function symbols(fieldDef: FieldDef, symbolsSpec, model: Model, channel: Channel) {
export namespace properties {
export function symbols(fieldDef: FieldDef, symbolsSpec, model: UnitModel, channel: Channel) {
let symbols:any = {};
const mark = model.mark();
const legend = model.legend(channel);

@@ -129,8 +150,13 @@ switch (mark) {

applyMarkConfig(symbols, model,
// Do not set fill (when filled) or stroke (when unfilled) property from config
// because the value from the scale should have precedence
without(FILL_STROKE_CONFIG, [ filled ? 'fill' : 'stroke'])
);
let config = channel === COLOR ?
/* For color's legend, do not set fill (when filled) or stroke (when unfilled) property from config because the the legend's `fill` or `stroke` scale should have precedence */
without(FILL_STROKE_CONFIG, [ filled ? 'fill' : 'stroke', 'strokeDash', 'strokeDashOffset']) :
/* For other legend, no need to omit. */
without(FILL_STROKE_CONFIG, ['strokeDash', 'strokeDashOffset']);
config = without(config, ['strokeDash', 'strokeDashOffset']);
applyMarkConfig(symbols, model, config);
if (filled) {

@@ -164,2 +190,18 @@ symbols.strokeWidth = { value: 0 };

if (legend.symbolColor !== undefined) {
symbols.fill = {value: legend.symbolColor};
}
if (legend.symbolShape !== undefined) {
symbols.shape = {value: legend.symbolShape};
}
if (legend.symbolSize !== undefined) {
symbols.size = {value: legend.symbolSize};
}
if (legend.symbolStrokeWidth !== undefined) {
symbols.strokeWidth = {value: legend.symbolStrokeWidth};
}
symbols = extend(symbols, symbolsSpec || {});

@@ -170,6 +212,10 @@

export function labels(fieldDef: FieldDef, symbolsSpec, model: Model, channel: Channel): any {
export function labels(fieldDef: FieldDef, labelsSpec, model: UnitModel, channel: Channel) {
const legend = model.legend(channel);
let labels:any = {};
if (channel === COLOR) {
if (fieldDef.type === ORDINAL) {
return {
labelsSpec = extend({
text: {

@@ -179,5 +225,5 @@ scale: model.scaleName(COLOR_LEGEND),

}
};
}, labelsSpec || {});
} else if (fieldDef.bin) {
return {
labelsSpec = extend({
text: {

@@ -187,13 +233,62 @@ scale: model.scaleName(COLOR_LEGEND_LABEL),

}
};
}, labelsSpec || {});
} else if (fieldDef.timeUnit) {
return {
labelsSpec = extend({
text: {
template: '{{ datum.data | time:\'' + timeFormat(model, channel) + '\'}}'
}
};
}, labelsSpec || {});
}
}
return undefined;
if (legend.labelAlign !== undefined) {
labels.align = {value: legend.labelAlign};
}
if (legend.labelColor !== undefined) {
labels.stroke = {value: legend.labelColor};
}
if (legend.labelFont !== undefined) {
labels.font = {value: legend.labelFont};
}
if (legend.labelFontSize !== undefined) {
labels.fontSize = {value: legend.labelFontSize};
}
if (legend.labelBaseline !== undefined) {
labels.baseline = {value: legend.labelBaseline};
}
labels = extend(labels, labelsSpec || {});
return keys(labels).length > 0 ? labels : undefined;
}
export function title(fieldDef: FieldDef, titleSpec, model: UnitModel, channel: Channel) {
const legend = model.legend(channel);
let titles:any = {};
if (legend.titleColor !== undefined) {
titles.stroke = {value: legend.titleColor};
}
if (legend.titleFont !== undefined) {
titles.font = {value: legend.titleFont};
}
if (legend.titleFontSize !== undefined) {
titles.fontSize = {value: legend.titleFontSize};
}
if (legend.titleFontWeight !== undefined) {
titles.fontWeight = {value: legend.titleFontWeight};
}
titles = extend(titles, titleSpec || {});
return keys(titles).length > 0 ? titles : undefined;
}
}

@@ -1,2 +0,2 @@

import {Model} from '../Model';
import {UnitModel} from '../unit';
import {X, Y} from '../../channel';

@@ -11,3 +11,3 @@ import {isDimension, isMeasure} from '../../fielddef';

export function properties(model: Model) {
export function properties(model: UnitModel) {
// TODO Use Vega's marks properties interface

@@ -91,3 +91,3 @@ let p: any = {};

export function labels(model: Model) {
export function labels(model: UnitModel) {
// TODO(#240): fill this method

@@ -94,0 +94,0 @@ return undefined;

@@ -1,7 +0,7 @@

import {Model} from '../Model';
import {X, Y, SIZE, Channel} from '../../channel';
import {isMeasure} from '../../fielddef';
import {UnitModel} from '../unit';
import {applyColorAndOpacity} from '../common';
export namespace bar {

@@ -12,3 +12,3 @@ export function markType() {

export function properties(model: Model) {
export function properties(model: UnitModel) {
// TODO Use Vega's marks properties interface

@@ -171,3 +171,3 @@ let p: any = {};

function sizeValue(model: Model, channel: Channel) {
function sizeValue(model: UnitModel, channel: Channel) {
const fieldDef = model.fieldDef(SIZE);

@@ -193,3 +193,3 @@ if (fieldDef && fieldDef.value !== undefined) {

export function labels(model: Model) {
export function labels(model: UnitModel) {
// TODO(#64): fill this method

@@ -196,0 +196,0 @@ return undefined;

@@ -32,5 +32,16 @@ "use strict";

common_1.applyMarkConfig(p, model, ['interpolate', 'tension']);
var size = sizeValue(model);
if (size) {
p.strokeWidth = { value: size };
}
return p;
}
line.properties = properties;
function sizeValue(model) {
var fieldDef = model.fieldDef(channel_1.SIZE);
if (fieldDef && fieldDef.value !== undefined) {
return fieldDef.value;
}
return model.config().mark.lineSize;
}
function labels(model) {

@@ -37,0 +48,0 @@ return undefined;

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

import {Model} from '../Model';
import {X, Y} from '../../channel';
import {UnitModel} from '../unit';
import {X, Y, SIZE} from '../../channel';
import {applyColorAndOpacity, applyMarkConfig} from '../common';

@@ -11,3 +11,3 @@

export function properties(model: Model) {
export function properties(model: UnitModel) {
// TODO Use Vega's marks properties interface

@@ -38,6 +38,20 @@ let p: any = {};

applyMarkConfig(p, model, ['interpolate', 'tension']);
// size as a channel is not supported in Vega yet.
const size = sizeValue(model);
if (size) {
p.strokeWidth = { value: size };
}
return p;
}
export function labels(model: Model) {
function sizeValue(model: UnitModel) {
const fieldDef = model.fieldDef(SIZE);
if (fieldDef && fieldDef.value !== undefined) {
return fieldDef.value;
}
return model.config().mark.lineSize;
}
export function labels(model: UnitModel) {
// TODO(#240): fill this method

@@ -44,0 +58,0 @@ return undefined;

@@ -12,2 +12,3 @@ "use strict";

var tick_1 = require('./tick');
var rule_1 = require('./rule');
var common_1 = require('../common');

@@ -21,25 +22,28 @@ var markCompiler = {

tick: tick_1.tick,
rule: rule_1.rule,
circle: point_1.circle,
square: point_1.square
};
function compileMark(model) {
function parseMark(model) {
if (util_1.contains([mark_1.LINE, mark_1.AREA], model.mark())) {
return compilePathMark(model);
return parsePathMark(model);
}
else {
return compileNonPathMark(model);
return parseNonPathMark(model);
}
}
exports.compileMark = compileMark;
function compilePathMark(model) {
exports.parseMark = parseMark;
function parsePathMark(model) {
var mark = model.mark();
var name = model.spec().name;
var hasParentData = model.has(channel_1.ROW) || model.has(channel_1.COLUMN);
var isFaceted = model.parent() && model.parent().isFacet();
var dataFrom = { data: model.dataTable() };
var details = detailFields(model);
var pathMarks = [util_1.extend(name ? { name: name + '-marks' } : {}, {
var pathMarks = [
{
name: model.name('marks'),
type: markCompiler[mark].markType(),
from: util_1.extend(hasParentData || details.length > 0 ? {} : dataFrom, { transform: [{ type: 'sort', by: sortPathBy(model) }] }),
from: util_1.extend(isFaceted || details.length > 0 ? {} : dataFrom, { transform: [{ type: 'sort', by: sortPathBy(model) }] }),
properties: { update: markCompiler[mark].properties(model) }
})];
}
];
if (details.length > 0) {

@@ -51,5 +55,5 @@ var facetTransform = { type: 'facet', groupby: details };

return [{
name: (name ? name + '-' : '') + mark + '-facet',
name: model.name('pathgroup'),
type: 'group',
from: util_1.extend(hasParentData ? {} : dataFrom, { transform: transform }),
from: util_1.extend(isFaceted ? {} : dataFrom, { transform: transform }),
properties: {

@@ -68,6 +72,5 @@ update: {

}
function compileNonPathMark(model) {
function parseNonPathMark(model) {
var mark = model.mark();
var name = model.spec().name;
var hasParentData = model.has(channel_1.ROW) || model.has(channel_1.COLUMN);
var isFaceted = model.parent() && model.parent().isFacet();
var dataFrom = { data: model.dataTable() };

@@ -78,6 +81,12 @@ var marks = [];

model.config().mark.applyColorToBackground && !model.has(channel_1.X) && !model.has(channel_1.Y)) {
marks.push(util_1.extend(name ? { name: name + '-background' } : {}, { type: 'rect' }, hasParentData ? {} : { from: dataFrom }, { properties: { update: text_1.text.background(model) } }));
marks.push(util_1.extend({
name: model.name('background'),
type: 'rect'
}, isFaceted ? {} : { from: dataFrom }, { properties: { update: text_1.text.background(model) } }));
}
marks.push(util_1.extend(name ? { name: name + '-marks' } : {}, { type: markCompiler[mark].markType() }, (!hasParentData || model.stack() || model.has(channel_1.ORDER)) ? {
from: util_1.extend(hasParentData ? {} : dataFrom, model.stack() ?
marks.push(util_1.extend({
name: model.name('marks'),
type: markCompiler[mark].markType()
}, (!isFaceted || model.stack() || model.has(channel_1.ORDER)) ? {
from: util_1.extend(isFaceted ? {} : dataFrom, model.stack() ?
{ transform: [stack_1.stackTransform(model)] } :

@@ -91,3 +100,6 @@ model.has(channel_1.ORDER) ?

if (labelProperties !== undefined) {
marks.push(util_1.extend(name ? { name: name + '-label' } : {}, { type: 'text' }, hasParentData ? {} : { from: dataFrom }, { properties: { update: labelProperties } }));
marks.push(util_1.extend({
name: model.name('label'),
type: 'text'
}, isFaceted ? {} : { from: dataFrom }, { properties: { update: labelProperties } }));
}

@@ -124,3 +136,3 @@ }

function detailFields(model) {
return [channel_1.COLOR, channel_1.DETAIL, channel_1.SHAPE].reduce(function (details, channel) {
return [channel_1.COLOR, channel_1.DETAIL, channel_1.OPACITY, channel_1.SHAPE].reduce(function (details, channel) {
if (model.has(channel) && !model.fieldDef(channel).aggregate) {

@@ -127,0 +139,0 @@ details.push(model.field(channel));

@@ -1,5 +0,5 @@

import {Model} from '../Model';
import {UnitModel} from '../unit';
import {OrderChannelDef} from '../../fielddef';
import {X, Y, COLOR, TEXT, SHAPE, PATH, ORDER, DETAIL, ROW, COLUMN, LABEL} from '../../channel';
import {X, Y, COLOR, TEXT, SHAPE, PATH, ORDER, OPACITY, DETAIL, LABEL} from '../../channel';
import {AREA, LINE, TEXT as TEXTMARK} from '../../mark';

@@ -14,5 +14,5 @@ import {imputeTransform, stackTransform} from '../stack';

import {tick} from './tick';
import {rule} from './rule';
import {sortField} from '../common';
const markCompiler = {

@@ -25,2 +25,3 @@ area: area,

tick: tick,
rule: rule,
circle: circle,

@@ -30,21 +31,20 @@ square: square

export function compileMark(model: Model): any[] {
export function parseMark(model: UnitModel): any[] {
if (contains([LINE, AREA], model.mark())) {
return compilePathMark(model);
return parsePathMark(model);
} else {
return compileNonPathMark(model);
return parseNonPathMark(model);
}
}
function compilePathMark(model: Model) { // TODO: extract this into compilePathMark
function parsePathMark(model: UnitModel) { // TODO: extract this into compilePathMark
const mark = model.mark();
const name = model.spec().name;
// TODO: replace this with more general case for composition
const hasParentData = model.has(ROW) || model.has(COLUMN);
const isFaceted = model.parent() && model.parent().isFacet();
const dataFrom = {data: model.dataTable()};
const details = detailFields(model);
let pathMarks: any = [extend(
name ? { name: name + '-marks' } : {},
let pathMarks: any = [
{
name: model.name('marks'),
type: markCompiler[mark].markType(),

@@ -55,3 +55,3 @@ from: extend(

// If has no subfacet, add from.data.
hasParentData || details.length > 0 ? {} : dataFrom,
isFaceted || details.length > 0 ? {} : dataFrom,

@@ -63,3 +63,3 @@ // sort transform

}
)];
];

@@ -80,3 +80,3 @@ if (details.length > 0) { // have level of details - need to facet line into subgroups

return [{
name: (name ? name + '-' : '') + mark + '-facet',
name: model.name('pathgroup'),
type: 'group',

@@ -86,3 +86,3 @@ from: extend(

// Otherwise, add it here.
hasParentData ? {} : dataFrom,
isFaceted ? {} : dataFrom,
{transform: transform}

@@ -103,7 +103,5 @@ ),

function compileNonPathMark(model: Model) {
function parseNonPathMark(model: UnitModel) {
const mark = model.mark();
const name = model.spec().name;
// TODO: replace this with more general case for composition
const hasParentData = model.has(ROW) || model.has(COLUMN);
const isFaceted = model.parent() && model.parent().isFacet();
const dataFrom = {data: model.dataTable()};

@@ -118,7 +116,9 @@

marks.push(extend(
name ? { name: name + '-background' } : {},
{ type: 'rect' },
{
name: model.name('background'),
type: 'rect'
},
// If has facet, `from.data` will be added in the cell group.
// Otherwise, add it here.
hasParentData ? {} : {from: dataFrom},
isFaceted ? {} : {from: dataFrom},
// Properties

@@ -130,10 +130,12 @@ { properties: { update: text.background(model) } }

marks.push(extend(
name ? { name: name + '-marks' } : {},
{ type: markCompiler[mark].markType() },
{
name: model.name('marks'),
type: markCompiler[mark].markType()
},
// Add `from` if needed
(!hasParentData || model.stack() || model.has(ORDER)) ? {
(!isFaceted || model.stack() || model.has(ORDER)) ? {
from: extend(
// If faceted, `from.data` will be added in the cell group.
// Otherwise, add it here
hasParentData ? {} : dataFrom,
isFaceted ? {} : dataFrom,
// `from.transform`

@@ -159,7 +161,9 @@ model.stack() ? // Stacked Chart need stack transform

marks.push(extend(
name ? { name: name + '-label' } : {},
{type: 'text'},
{
name: model.name('label'),
type: 'text'
},
// If has facet, `from.data` will be added in the cell group.
// Otherwise, add it here.
hasParentData ? {} : {from: dataFrom},
isFaceted ? {} : {from: dataFrom},
// Properties

@@ -174,3 +178,3 @@ { properties: { update: labelProperties } }

function sortBy(model: Model): string | string[] {
function sortBy(model: UnitModel): string | string[] {
if (model.has(ORDER)) {

@@ -192,3 +196,3 @@ let channelDef = model.encoding().order;

*/
function sortPathBy(model: Model): string | string[] {
function sortPathBy(model: UnitModel): string | string[] {
if (model.mark() === LINE && model.has(PATH)) {

@@ -214,4 +218,4 @@ // For only line, sort by the path field if it is specified.

*/
function detailFields(model: Model): string[] {
return [COLOR, DETAIL, SHAPE].reduce(function(details, channel) {
function detailFields(model: UnitModel): string[] {
return [COLOR, DETAIL, OPACITY, SHAPE].reduce(function(details, channel) {
if (model.has(channel) && !model.fieldDef(channel).aggregate) {

@@ -218,0 +222,0 @@ details.push(model.field(channel));

@@ -1,2 +0,2 @@

import {Model} from '../Model';
import {UnitModel} from '../unit';
import {X, Y, SHAPE, SIZE} from '../../channel';

@@ -10,3 +10,3 @@ import {applyColorAndOpacity} from '../common';

export function properties(model: Model, fixedShape?: string) {
export function properties(model: UnitModel, fixedShape?: string) {
// TODO Use Vega's marks properties interface

@@ -63,3 +63,3 @@ let p: any = {};

function sizeValue(model: Model) {
function sizeValue(model: UnitModel) {
const fieldDef = model.fieldDef(SIZE);

@@ -73,3 +73,3 @@ if (fieldDef && fieldDef.value !== undefined) {

export function labels(model: Model) {
export function labels(model: UnitModel) {
// TODO(#240): fill this method

@@ -84,7 +84,7 @@ }

export function properties(model: Model) {
export function properties(model: UnitModel) {
return point.properties(model, 'circle');
}
export function labels(model: Model) {
export function labels(model: UnitModel) {
// TODO(#240): fill this method

@@ -100,7 +100,7 @@ return undefined;

export function properties(model: Model) {
export function properties(model: UnitModel) {
return point.properties(model, 'square');
}
export function labels(model: Model) {
export function labels(model: UnitModel) {
// TODO(#240): fill this method

@@ -107,0 +107,0 @@ return undefined;

@@ -1,2 +0,2 @@

import {Model} from '../Model';
import {UnitModel} from '../unit';
import {X, Y, COLOR, TEXT, SIZE} from '../../channel';

@@ -12,3 +12,3 @@ import {applyMarkConfig, applyColorAndOpacity, formatMixins} from '../common';

export function background(model: Model) {
export function background(model: UnitModel) {
return {

@@ -26,3 +26,3 @@ x: { value: 0 },

export function properties(model: Model) {
export function properties(model: UnitModel) {
// TODO Use Vega's marks properties interface

@@ -97,3 +97,3 @@ let p: any = {};

function sizeValue(model: Model) {
function sizeValue(model: UnitModel) {
const fieldDef = model.fieldDef(SIZE);

@@ -100,0 +100,0 @@ if (fieldDef && fieldDef.value !== undefined) {

@@ -31,18 +31,18 @@ "use strict";

if (model.config().mark.orient === 'horizontal') {
p.width = { value: model.config().mark.tickThickness };
p.height = model.has(channel_1.SIZE) ? {
p.width = model.has(channel_1.SIZE) ? {
scale: model.scaleName(channel_1.SIZE),
field: model.field(channel_1.SIZE)
} : {
value: sizeValue(model, channel_1.Y)
value: sizeValue(model, channel_1.X)
};
p.height = { value: model.config().mark.tickThickness };
}
else {
p.width = model.has(channel_1.SIZE) ? {
p.width = { value: model.config().mark.tickThickness };
p.height = model.has(channel_1.SIZE) ? {
scale: model.scaleName(channel_1.SIZE),
field: model.field(channel_1.SIZE)
} : {
value: sizeValue(model, channel_1.X)
value: sizeValue(model, channel_1.Y)
};
p.height = { value: model.config().mark.tickThickness };
}

@@ -49,0 +49,0 @@ common_1.applyColorAndOpacity(p, model);

@@ -1,3 +0,4 @@

import {Model} from '../Model';
import {X, Y, SIZE, Channel} from '../../channel';
import {UnitModel} from '../unit';
import {applyColorAndOpacity} from '../common';

@@ -10,5 +11,7 @@

export function properties(model: Model) {
export function properties(model: UnitModel) {
let p: any = {};
// TODO: support explicit value
// x

@@ -35,2 +38,11 @@ if (model.has(X)) {

if (model.config().mark.orient === 'horizontal') {
p.width = model.has(SIZE)? {
scale: model.scaleName(SIZE),
field: model.field(SIZE)
} : {
value: sizeValue(model, X)
};
p.height = { value: model.config().mark.tickThickness };
} else {
p.width = { value: model.config().mark.tickThickness };

@@ -43,10 +55,2 @@ p.height = model.has(SIZE)? {

};
} else {
p.width = model.has(SIZE)? {
scale: model.scaleName(SIZE),
field: model.field(SIZE)
} : {
value: sizeValue(model, X)
};
p.height = { value: model.config().mark.tickThickness };
}

@@ -58,3 +62,3 @@

function sizeValue(model: Model, channel: Channel) {
function sizeValue(model: UnitModel, channel: Channel) {
const fieldDef = model.fieldDef(SIZE);

@@ -77,3 +81,3 @@ if (fieldDef && fieldDef.value !== undefined) {

export function labels(model: Model) {
export function labels(model: UnitModel) {
// TODO(#240): fill this method

@@ -80,0 +84,0 @@ return undefined;

"use strict";
var util_1 = require('../util');
var aggregate_1 = require('../aggregate');
var channel_1 = require('../channel');
var config_1 = require('../config');
var data_1 = require('../data');
var type_1 = require('../type');
var fielddef_1 = require('../fielddef');
var mark_1 = require('../mark');
var time_1 = require('./time');
var scale_1 = require('../scale');
var config_1 = require('../config');
var timeunit_1 = require('../timeunit');
var fielddef_1 = require('../fielddef');
var type_1 = require('../type');
var util_1 = require('../util');
var time_1 = require('./time');
exports.COLOR_LEGEND = 'color_legend';
exports.COLOR_LEGEND_LABEL = 'color_legend_label';
function compileScales(model) {
return model.channelWithScales().reduce(function (scales, channel) {
var fieldDef = model.fieldDef(channel);
if (channel === channel_1.COLOR && model.legend(channel_1.COLOR) && (fieldDef.type === type_1.ORDINAL || fieldDef.bin || fieldDef.timeUnit)) {
scales.push(colorLegendScale(model, fieldDef));
if (fieldDef.bin) {
scales.push(binColorLegendLabel(model, fieldDef));
function parseScaleComponent(model) {
return model.channels().reduce(function (scale, channel) {
if (model.scale(channel)) {
var fieldDef = model.fieldDef(channel);
var scales = {
main: parseMainScale(model, fieldDef, channel)
};
if (channel === channel_1.COLOR && model.legend(channel_1.COLOR) && (fieldDef.type === type_1.ORDINAL || fieldDef.bin || fieldDef.timeUnit)) {
scales.colorLegend = parseColorLegendScale(model, fieldDef);
if (fieldDef.bin) {
scales.binColorLegend = parseBinColorLegendLabel(model, fieldDef);
}
}
scale[channel] = scales;
}
scales.push(mainScale(model, fieldDef, channel));
return scales;
}, []);
return scale;
}, {});
}
exports.compileScales = compileScales;
function mainScale(model, fieldDef, channel) {
exports.parseScaleComponent = parseScaleComponent;
function parseMainScale(model, fieldDef, channel) {
var scale = model.scale(channel);

@@ -34,6 +39,6 @@ var sort = model.sort(channel);

name: model.scaleName(channel),
type: scaleType(scale, fieldDef, channel, model.mark()),
type: scale.type,
};
scaleDef.domain = domain(scale, model, channel, scaleDef.type);
util_1.extend(scaleDef, rangeMixins(scale, model, channel, scaleDef.type));
scaleDef.domain = domain(scale, model, channel);
util_1.extend(scaleDef, rangeMixins(scale, model, channel));
if (sort && (typeof sort === 'string' ? sort : sort.order) === 'descending') {

@@ -48,3 +53,3 @@ scaleDef.reverse = true;

].forEach(function (property) {
var value = exports[property](scale[property], scaleDef.type, channel, fieldDef);
var value = exports[property](scale, channel, fieldDef, model);
if (value !== undefined) {

@@ -56,9 +61,10 @@ scaleDef[property] = value;

}
function colorLegendScale(model, fieldDef) {
function parseColorLegendScale(model, fieldDef) {
return {
name: model.scaleName(exports.COLOR_LEGEND),
type: 'ordinal',
type: scale_1.ScaleType.ORDINAL,
domain: {
data: model.dataTable(),
field: model.field(channel_1.COLOR, (fieldDef.bin || fieldDef.timeUnit) ? {} : { prefn: 'rank_' }), sort: true
field: model.field(channel_1.COLOR, (fieldDef.bin || fieldDef.timeUnit) ? {} : { prefn: 'rank_' }),
sort: true
},

@@ -68,9 +74,9 @@ range: { data: model.dataTable(), field: model.field(channel_1.COLOR), sort: true }

}
function binColorLegendLabel(model, fieldDef) {
function parseBinColorLegendLabel(model, fieldDef) {
return {
name: model.scaleName(exports.COLOR_LEGEND_LABEL),
type: 'ordinal',
type: scale_1.ScaleType.ORDINAL,
domain: {
data: model.dataTable(),
field: model.field(channel_1.COLOR, { prefn: 'rank_' }),
field: model.field(channel_1.COLOR),
sort: true

@@ -130,3 +136,3 @@ },

exports.scaleType = scaleType;
function domain(scale, model, channel, scaleType) {
function domain(scale, model, channel) {
var fieldDef = model.fieldDef(channel);

@@ -158,8 +164,8 @@ if (scale.domain) {

return {
data: data_1.STACKED_SCALE,
data: model.dataName(data_1.STACKED_SCALE),
field: model.field(channel, { prefn: 'sum_' })
};
}
var includeRawDomain = _includeRawDomain(scale, model, channel, scaleType), sort = domainSort(model, channel, scaleType);
if (includeRawDomain) {
var useRawDomain = _useRawDomain(scale, model, channel), sort = domainSort(model, channel, scale.type);
if (useRawDomain) {
return {

@@ -171,3 +177,3 @@ data: data_1.SOURCE,

else if (fieldDef.bin) {
return scaleType === scale_1.ScaleType.ORDINAL ? {
return scale.type === scale_1.ScaleType.ORDINAL ? {
data: model.dataTable(),

@@ -222,13 +228,14 @@ field: model.field(channel, { binSuffix: '_range' }),

exports.domainSort = domainSort;
function _includeRawDomain(scale, model, channel, scaleType) {
function _useRawDomain(scale, model, channel) {
var fieldDef = model.fieldDef(channel);
return scale.includeRawDomain &&
return 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 && util_1.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scaleType)));
(fieldDef.type === type_1.TEMPORAL && util_1.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scale.type)));
}
function rangeMixins(scale, model, channel, scaleType) {
var fieldDef = model.fieldDef(channel), scaleConfig = model.config().scale;
if (scaleType === scale_1.ScaleType.ORDINAL && scale.bandSize && util_1.contains([channel_1.X, channel_1.Y], channel)) {
function rangeMixins(scale, model, channel) {
var fieldDef = model.fieldDef(channel);
var scaleConfig = model.config().scale;
if (scale.type === scale_1.ScaleType.ORDINAL && scale.bandSize && util_1.contains([channel_1.X, channel_1.Y], channel)) {
return { bandSize: scale.bandSize };

@@ -240,14 +247,21 @@ }

switch (channel) {
case channel_1.ROW:
return { range: 'height' };
case channel_1.COLUMN:
return { range: 'width' };
}
var unitModel = model;
switch (channel) {
case channel_1.X:
return {
rangeMin: 0,
rangeMax: model.cellWidth()
rangeMax: unitModel.config().cell.width
};
case channel_1.Y:
return {
rangeMin: model.cellHeight(),
rangeMin: unitModel.config().cell.height,
rangeMax: 0
};
case channel_1.SIZE:
if (model.mark() === mark_1.BAR) {
if (unitModel.mark() === mark_1.BAR) {
if (scaleConfig.barSizeRange !== undefined) {

@@ -259,13 +273,15 @@ return { range: scaleConfig.barSizeRange };

}
else if (model.mark() === mark_1.TEXT) {
else if (unitModel.mark() === mark_1.TEXT) {
return { range: scaleConfig.fontSizeRange };
}
else if (unitModel.mark() === mark_1.RULE) {
return { range: scaleConfig.ruleSizeRange };
}
else if (unitModel.mark() === mark_1.TICK) {
return { range: scaleConfig.tickSizeRange };
}
if (scaleConfig.pointSizeRange !== undefined) {
return { range: scaleConfig.pointSizeRange };
}
var xIsMeasure = fielddef_1.isMeasure(model.encoding().x);
var yIsMeasure = fielddef_1.isMeasure(model.encoding().y);
var bandSize = xIsMeasure !== yIsMeasure ?
model.scale(xIsMeasure ? channel_1.Y : channel_1.X).bandSize :
Math.min(model.scale(channel_1.X).bandSize || scaleConfig.bandSize, model.scale(channel_1.Y).bandSize || scaleConfig.bandSize);
var bandSize = pointBandSize(unitModel);
return { range: [9, (bandSize - 2) * (bandSize - 2)] };

@@ -279,6 +295,4 @@ case channel_1.SHAPE:

return { range: scaleConfig.sequentialColorRange };
case channel_1.ROW:
return { range: 'height' };
case channel_1.COLUMN:
return { range: 'width' };
case channel_1.OPACITY:
return { range: scaleConfig.opacity };
}

@@ -288,6 +302,25 @@ return {};

exports.rangeMixins = rangeMixins;
function clamp(prop, scaleType) {
function pointBandSize(model) {
var scaleConfig = model.config().scale;
var hasX = model.has(channel_1.X);
var hasY = model.has(channel_1.Y);
var xIsMeasure = fielddef_1.isMeasure(model.encoding().x);
var yIsMeasure = fielddef_1.isMeasure(model.encoding().y);
if (hasX && hasY) {
return xIsMeasure !== yIsMeasure ?
model.scale(xIsMeasure ? channel_1.Y : channel_1.X).bandSize :
Math.min(model.scale(channel_1.X).bandSize || scaleConfig.bandSize, model.scale(channel_1.Y).bandSize || scaleConfig.bandSize);
}
else if (hasY) {
return yIsMeasure ? model.config().scale.bandSize : model.scale(channel_1.Y).bandSize;
}
else if (hasX) {
return xIsMeasure ? model.config().scale.bandSize : model.scale(channel_1.X).bandSize;
}
return model.config().scale.bandSize;
}
function clamp(scale) {
if (util_1.contains([scale_1.ScaleType.LINEAR, scale_1.ScaleType.POW, scale_1.ScaleType.SQRT,
scale_1.ScaleType.LOG, scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scaleType)) {
return prop;
scale_1.ScaleType.LOG, scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scale.type)) {
return scale.clamp;
}

@@ -297,5 +330,5 @@ return undefined;

exports.clamp = clamp;
function exponent(prop, scaleType) {
if (scaleType === scale_1.ScaleType.POW) {
return prop;
function exponent(scale) {
if (scale.type === scale_1.ScaleType.POW) {
return scale.exponent;
}

@@ -305,9 +338,9 @@ return undefined;

exports.exponent = exponent;
function nice(prop, scaleType, channel, fieldDef) {
function nice(scale, channel, fieldDef) {
if (util_1.contains([scale_1.ScaleType.LINEAR, scale_1.ScaleType.POW, scale_1.ScaleType.SQRT, scale_1.ScaleType.LOG,
scale_1.ScaleType.TIME, scale_1.ScaleType.UTC, scale_1.ScaleType.QUANTIZE], scaleType)) {
if (prop !== undefined) {
return prop;
scale_1.ScaleType.TIME, scale_1.ScaleType.UTC, scale_1.ScaleType.QUANTIZE], scale.type)) {
if (scale.nice !== undefined) {
return scale.nice;
}
if (util_1.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scaleType)) {
if (util_1.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC], scale.type)) {
return time_1.smallestUnit(fieldDef.timeUnit);

@@ -320,5 +353,5 @@ }

exports.nice = nice;
function padding(prop, scaleType, channel) {
if (scaleType === scale_1.ScaleType.ORDINAL && util_1.contains([channel_1.X, channel_1.Y], channel)) {
return prop;
function padding(scale, channel) {
if (scale.type === scale_1.ScaleType.ORDINAL && util_1.contains([channel_1.X, channel_1.Y], channel)) {
return scale.padding;
}

@@ -328,4 +361,4 @@ return undefined;

exports.padding = padding;
function points(__, scaleType, channel) {
if (scaleType === scale_1.ScaleType.ORDINAL && util_1.contains([channel_1.X, channel_1.Y], channel)) {
function points(scale, channel, __, model) {
if (scale.type === scale_1.ScaleType.ORDINAL && util_1.contains([channel_1.X, channel_1.Y], channel)) {
return true;

@@ -336,5 +369,5 @@ }

exports.points = points;
function round(prop, scaleType, channel) {
if (util_1.contains([channel_1.X, channel_1.Y, channel_1.ROW, channel_1.COLUMN, channel_1.SIZE], channel) && prop !== undefined) {
return prop;
function round(scale, channel) {
if (util_1.contains([channel_1.X, channel_1.Y, channel_1.ROW, channel_1.COLUMN, channel_1.SIZE], channel) && scale.round !== undefined) {
return scale.round;
}

@@ -344,6 +377,6 @@ return undefined;

exports.round = round;
function zero(prop, scaleType, channel, fieldDef) {
if (!util_1.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC, scale_1.ScaleType.ORDINAL], scaleType)) {
if (prop !== undefined) {
return prop;
function zero(scale, channel, fieldDef) {
if (!util_1.contains([scale_1.ScaleType.TIME, scale_1.ScaleType.UTC, scale_1.ScaleType.ORDINAL], scale.type)) {
if (scale.zero !== undefined) {
return scale.zero;
}

@@ -350,0 +383,0 @@ return !fieldDef.bin && util_1.contains([channel_1.X, channel_1.Y], channel);

// https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#11-ambient-declarations
declare var exports;
import {FieldDef} from '../fielddef';
import {contains, extend} from '../util';
import {Model} from './Model';
import {SHARED_DOMAIN_OPS} from '../aggregate';
import {COLUMN, ROW, X, Y, SHAPE, SIZE, COLOR, TEXT, hasScale, Channel} from '../channel';
import {COLUMN, ROW, X, Y, SHAPE, SIZE, COLOR, OPACITY, TEXT, hasScale, Channel} from '../channel';
import {StackOffset} from '../config';
import {SOURCE, STACKED_SCALE} from '../data';
import {FieldDef, field, isMeasure} from '../fielddef';
import {Mark, BAR, TEXT as TEXT_MARK, RULE, TICK} from '../mark';
import {Scale, ScaleType, NiceTime} from '../scale';
import {TimeUnit} from '../timeunit';
import {NOMINAL, ORDINAL, QUANTITATIVE, TEMPORAL} from '../type';
import {Mark, BAR, TEXT as TEXT_MARK} from '../mark';
import {contains, extend, Dict} from '../util';
import {VgScale} from '../vega.schema';
import {Model} from './model';
import {rawDomain, smallestUnit} from './time';
import {Scale, ScaleType} from '../scale';
import {StackOffset} from '../config';
import {TimeUnit} from '../timeunit';
import {field, isMeasure} from '../fielddef';
import {UnitModel} from './unit';

@@ -28,18 +29,36 @@ /**

export function compileScales(model: Model) {
return model.channelWithScales().reduce(function(scales: any[], channel: Channel) {
const fieldDef = model.fieldDef(channel);
// Add additional scales needed to support ordinal legends (list of values)
// for color ramp.
if (channel === COLOR && model.legend(COLOR) && (fieldDef.type === ORDINAL || fieldDef.bin || fieldDef.timeUnit)) {
scales.push(colorLegendScale(model, fieldDef));
if (fieldDef.bin) {
scales.push(binColorLegendLabel(model, fieldDef));
// FIXME: With layer and concat, scaleComponent should decompose between
// ScaleSignature and ScaleDomain[].
// Basically, if two unit specs has the same scale, signature for a particular channel,
// the scale can be unioned by combining the domain.
export type ScaleComponent = VgScale;
export type ScaleComponents = {
main: ScaleComponent;
colorLegend?: ScaleComponent,
binColorLegend?: ScaleComponent
}
export function parseScaleComponent(model: Model): Dict<ScaleComponents> {
return model.channels().reduce(function(scale: Dict<ScaleComponents>, channel: Channel) {
if (model.scale(channel)) {
const fieldDef = model.fieldDef(channel);
const scales: ScaleComponents = {
main: parseMainScale(model, fieldDef, channel)
};
// Add additional scales needed to support ordinal legends (list of values)
// for color ramp.
if (channel === COLOR && model.legend(COLOR) && (fieldDef.type === ORDINAL || fieldDef.bin || fieldDef.timeUnit)) {
scales.colorLegend = parseColorLegendScale(model, fieldDef);
if (fieldDef.bin) {
scales.binColorLegend = parseBinColorLegendLabel(model, fieldDef);
}
}
scale[channel] = scales;
}
scales.push(mainScale(model, fieldDef, channel));
return scales;
}, []);
return scale;
}, {} as Dict<ScaleComponents>);
}

@@ -50,3 +69,3 @@

*/
function mainScale(model: Model, fieldDef: FieldDef, channel: Channel) {
function parseMainScale(model: Model, fieldDef: FieldDef, channel: Channel) {
const scale = model.scale(channel);

@@ -57,7 +76,7 @@ const sort = model.sort(channel);

name: model.scaleName(channel),
type: scaleType(scale, fieldDef, channel, model.mark()),
type: scale.type,
};
scaleDef.domain = domain(scale, model, channel, scaleDef.type);
extend(scaleDef, rangeMixins(scale, model, channel, scaleDef.type));
scaleDef.domain = domain(scale, model, channel);
extend(scaleDef, rangeMixins(scale, model, channel));

@@ -79,3 +98,3 @@ if (sort && (typeof sort === 'string' ? sort : sort.order) === 'descending') {

].forEach(function(property) {
const value = exports[property](scale[property], scaleDef.type, channel, fieldDef);
const value = exports[property](scale, channel, fieldDef, model);
if (value !== undefined) {

@@ -95,10 +114,11 @@ scaleDef[property] = value;

*/
function colorLegendScale(model: Model, fieldDef: FieldDef) {
function parseColorLegendScale(model: Model, fieldDef: FieldDef): ScaleComponent {
return {
name: model.scaleName(COLOR_LEGEND),
type: 'ordinal',
type: ScaleType.ORDINAL,
domain: {
data: model.dataTable(),
// use rank_<field> for ordinal type, for bin and timeUnit use default field
field: model.field(COLOR, (fieldDef.bin || fieldDef.timeUnit) ? {} : {prefn: 'rank_'}), sort: true
field: model.field(COLOR, (fieldDef.bin || fieldDef.timeUnit) ? {} : {prefn: 'rank_'}),
sort: true
},

@@ -112,9 +132,9 @@ range: {data: model.dataTable(), field: model.field(COLOR), sort: true}

*/
function binColorLegendLabel(model: Model, fieldDef: FieldDef) {
function parseBinColorLegendLabel(model: Model, fieldDef: FieldDef): ScaleComponent {
return {
name: model.scaleName(COLOR_LEGEND_LABEL),
type: 'ordinal',
type: ScaleType.ORDINAL,
domain: {
data: model.dataTable(),
field: model.field(COLOR, {prefn: 'rank_'}),
field: model.field(COLOR),
sort: true

@@ -185,3 +205,3 @@ },

export function domain(scale: Scale, model: Model, channel:Channel, scaleType: ScaleType): any {
export function domain(scale: Scale, model: Model, channel:Channel): any {
const fieldDef = model.fieldDef(channel);

@@ -219,3 +239,3 @@

return {
data: STACKED_SCALE,
data: model.dataName(STACKED_SCALE),
// STACKED_SCALE produces sum of the field's value e.g., sum of sum, sum of distinct

@@ -226,6 +246,6 @@ field: model.field(channel, {prefn: 'sum_'})

const includeRawDomain = _includeRawDomain(scale, model, channel, scaleType),
sort = domainSort(model, channel, scaleType);
const useRawDomain = _useRawDomain(scale, model, channel),
sort = domainSort(model, channel, scale.type);
if (includeRawDomain) { // includeRawDomain - only Q/T
if (useRawDomain) { // useRawDomain - only Q/T
return {

@@ -236,3 +256,3 @@ data: SOURCE,

} else if (fieldDef.bin) { // bin
return scaleType === ScaleType.ORDINAL ? {
return scale.type === ScaleType.ORDINAL ? {
// ordinal bin scale takes domain from bin_range, ordered by bin_start

@@ -297,12 +317,12 @@ data: model.dataTable(),

/**
* Determine if includeRawDomain should be activated for this scale.
* Determine if useRawDomain should be activated for this scale.
* @return {Boolean} Returns true if all of the following conditons applies:
* 1. `includeRawDomain` is enabled either through scale or config
* 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.
*/
function _includeRawDomain (scale: Scale, model: Model, channel: Channel, scaleType: ScaleType) {
function _useRawDomain (scale: Scale, model: Model, channel: Channel) {
const fieldDef = model.fieldDef(channel);
return scale.includeRawDomain && // if includeRawDomain is enabled
return scale.useRawDomain && // if useRawDomain is enabled
// only applied to aggregate table

@@ -319,3 +339,3 @@ fieldDef.aggregate &&

// T uses non-ordinal scale when there's no unit or when the unit is not ordinal.
(fieldDef.type === TEMPORAL && contains([ScaleType.TIME, ScaleType.UTC], scaleType))
(fieldDef.type === TEMPORAL && contains([ScaleType.TIME, ScaleType.UTC], scale.type))
);

@@ -325,9 +345,9 @@ }

export function rangeMixins(scale: Scale, model: Model, channel: Channel, scaleType: ScaleType): any {
export function rangeMixins(scale: Scale, model: Model, channel: Channel): any {
// TODO: need to add rule for quantile, quantize, threshold scale
const fieldDef = model.fieldDef(channel),
scaleConfig = model.config().scale;
const fieldDef = model.fieldDef(channel);
const scaleConfig = model.config().scale;
if (scaleType === ScaleType.ORDINAL && scale.bandSize && contains([X, Y], channel)) {
if (scale.type === ScaleType.ORDINAL && scale.bandSize && contains([X, Y], channel)) {
return {bandSize: scale.bandSize};

@@ -340,3 +360,11 @@ }

}
switch (channel) {
case ROW:
return {range: 'height'};
case COLUMN:
return {range: 'width'};
}
// If not ROW / COLUMN, we can assume that this is a unit spec.
const unitModel = model as UnitModel;
switch (channel) {

@@ -349,11 +377,12 @@ case X:

rangeMin: 0,
rangeMax: model.cellWidth() // Fixed cell width for non-ordinal
rangeMax: unitModel.config().cell.width // Fixed cell width for non-ordinal
};
case Y:
return {
rangeMin: model.cellHeight(), // Fixed cell height for non-ordinal
rangeMin: unitModel.config().cell.height, // Fixed cell height for non-ordinal
rangeMax: 0
};
case SIZE:
if (model.mark() === BAR) {
if (unitModel.mark() === BAR) {
if (scaleConfig.barSizeRange !== undefined) {

@@ -363,5 +392,9 @@ return {range: scaleConfig.barSizeRange};

const dimension = model.config().mark.orient === 'horizontal' ? Y : X;
return {range: [ model.config().mark.barThinSize, model.scale(dimension).bandSize]};
} else if (model.mark() === TEXT_MARK) {
return {range: [model.config().mark.barThinSize, model.scale(dimension).bandSize]};
} else if (unitModel.mark() === TEXT_MARK) {
return {range: scaleConfig.fontSizeRange };
} else if (unitModel.mark() === RULE) {
return {range: scaleConfig.ruleSizeRange };
} else if (unitModel.mark() === TICK) {
return {range: scaleConfig.tickSizeRange };
}

@@ -373,12 +406,4 @@ // else -- point, square, circle

const xIsMeasure = isMeasure(model.encoding().x);
const yIsMeasure = isMeasure(model.encoding().y);
const bandSize = pointBandSize(unitModel);
const bandSize = xIsMeasure !== yIsMeasure ?
model.scale(xIsMeasure ? Y : X).bandSize :
Math.min(
model.scale(X).bandSize || scaleConfig.bandSize,
model.scale(Y).bandSize || scaleConfig.bandSize
);
return {range: [9, (bandSize - 2) * (bandSize - 2)]};

@@ -393,6 +418,4 @@ case SHAPE:

return {range: scaleConfig.sequentialColorRange};
case ROW:
return {range: 'height'};
case COLUMN:
return {range: 'width'};
case OPACITY:
return {range: scaleConfig.opacity};
}

@@ -402,8 +425,32 @@ return {};

export function clamp(prop: boolean, scaleType: ScaleType) {
function pointBandSize(model: UnitModel) {
const scaleConfig = model.config().scale;
const hasX = model.has(X);
const hasY = model.has(Y);
const xIsMeasure = isMeasure(model.encoding().x);
const yIsMeasure = isMeasure(model.encoding().y);
if (hasX && hasY) {
return xIsMeasure !== yIsMeasure ?
model.scale(xIsMeasure ? Y : X).bandSize :
Math.min(
model.scale(X).bandSize || scaleConfig.bandSize,
model.scale(Y).bandSize || scaleConfig.bandSize
);
} else if (hasY) {
return yIsMeasure ? model.config().scale.bandSize : model.scale(Y).bandSize;
} else if (hasX) {
return xIsMeasure ? model.config().scale.bandSize : model.scale(X).bandSize;
}
return model.config().scale.bandSize;
}
export function clamp(scale: Scale) {
// Only works for scale with both continuous domain continuous range
// (Doesn't work for quantize, quantile, threshold, ordinal)
if (contains([ScaleType.LINEAR, ScaleType.POW, ScaleType.SQRT,
ScaleType.LOG, ScaleType.TIME, ScaleType.UTC], scaleType)) {
return prop;
ScaleType.LOG, ScaleType.TIME, ScaleType.UTC], scale.type)) {
return scale.clamp;
}

@@ -413,5 +460,5 @@ return undefined;

export function exponent(prop: number, scaleType: ScaleType) {
if (scaleType === ScaleType.POW) {
return prop;
export function exponent(scale: Scale) {
if (scale.type === ScaleType.POW) {
return scale.exponent;
}

@@ -421,10 +468,11 @@ return undefined;

export function nice(prop: boolean|string, scaleType: ScaleType, channel: Channel, fieldDef: FieldDef) {
export function nice(scale: Scale, channel: Channel, fieldDef: FieldDef): boolean | NiceTime {
if (contains([ScaleType.LINEAR, ScaleType.POW, ScaleType.SQRT, ScaleType.LOG,
ScaleType.TIME, ScaleType.UTC, ScaleType.QUANTIZE], scaleType)) {
if (prop !== undefined) {
return prop;
ScaleType.TIME, ScaleType.UTC, ScaleType.QUANTIZE], scale.type)) {
if (scale.nice !== undefined) {
return scale.nice;
}
if (contains([ScaleType.TIME, ScaleType.UTC], scaleType)) {
return smallestUnit(fieldDef.timeUnit);
if (contains([ScaleType.TIME, ScaleType.UTC], scale.type)) {
return smallestUnit(fieldDef.timeUnit) as any;
}

@@ -437,3 +485,3 @@ return contains([X, Y], channel); // return true for quantitative X/Y

export function padding(prop: number, scaleType: ScaleType, channel: Channel) {
export function padding(scale: Scale, channel: Channel) {
/* Padding is only allowed for X and Y.

@@ -447,4 +495,4 @@ *

*/
if (scaleType === ScaleType.ORDINAL && contains([X, Y], channel)) {
return prop;
if (scale.type === ScaleType.ORDINAL && contains([X, Y], channel)) {
return scale.padding;
}

@@ -454,4 +502,4 @@ return undefined;

export function points(__, scaleType: ScaleType, channel: Channel) {
if (scaleType === ScaleType.ORDINAL && contains([X, Y], channel)) {
export function points(scale: Scale, channel: Channel, __, model: Model) {
if (scale.type === ScaleType.ORDINAL && contains([X, Y], channel)) {
// We always use ordinal point scale for x and y.

@@ -464,5 +512,5 @@ // Thus `points` isn't included in the scale's schema.

export function round(prop: boolean, scaleType: ScaleType, channel: Channel) {
if (contains([X, Y, ROW, COLUMN, SIZE], channel) && prop !== undefined) {
return prop;
export function round(scale: Scale, channel: Channel) {
if (contains([X, Y, ROW, COLUMN, SIZE], channel) && scale.round !== undefined) {
return scale.round;
}

@@ -473,7 +521,7 @@

export function zero(prop: boolean, scaleType: ScaleType, channel: Channel, fieldDef: FieldDef) {
export function zero(scale: Scale, channel: Channel, fieldDef: FieldDef) {
// only applicable for non-ordinal scale
if (!contains([ScaleType.TIME, ScaleType.UTC, ScaleType.ORDINAL], scaleType)) {
if (prop !== undefined) {
return prop;
if (!contains([ScaleType.TIME, ScaleType.UTC, ScaleType.ORDINAL], scale.type)) {
if (scale.zero !== undefined) {
return scale.zero;
}

@@ -480,0 +528,0 @@ // By default, return true only for non-binned, quantitative x-scale or y-scale.

@@ -10,3 +10,2 @@ "use strict";

var common_1 = require('./common');
var scale_2 = require('./scale');
function compileStackProperties(mark, encoding, scale, config) {

@@ -39,4 +38,4 @@ var stackFields = getStackFields(mark, encoding, scale);

exports.compileStackProperties = compileStackProperties;
function getStackFields(mark, encoding, scale) {
return [channel_1.COLOR, channel_1.DETAIL].reduce(function (fields, channel) {
function getStackFields(mark, encoding, scaleMap) {
return [channel_1.COLOR, channel_1.DETAIL, channel_1.OPACITY, channel_1.SIZE].reduce(function (fields, channel) {
var channelEncoding = encoding[channel];

@@ -51,4 +50,5 @@ if (encoding_1.has(encoding, channel)) {

var fieldDef = channelEncoding;
var scale = scaleMap[channel];
fields.push(fielddef_1.field(fieldDef, {
binSuffix: scale_2.scaleType(scale[channel], fieldDef, channel, mark) === scale_1.ScaleType.ORDINAL ? '_range' : '_start'
binSuffix: scale && scale.type === scale_1.ScaleType.ORDINAL ? '_range' : '_start'
}));

@@ -55,0 +55,0 @@ }

import {Encoding} from '../encoding';
import {Config} from '../config';
import {FieldDef} from '../fielddef';
import {Model, ScaleMap} from './Model';
import {Channel, X, Y, COLOR, DETAIL, ORDER} from '../channel';
import {ScaleType} from '../scale';
import {Channel, X, Y, COLOR, DETAIL, ORDER, OPACITY, SIZE} from '../channel';
import {Scale, ScaleType} from '../scale';
import {StackOffset} from '../config';

@@ -11,6 +10,8 @@ import {BAR, AREA, Mark} from '../mark';

import {has, isAggregate} from '../encoding';
import {isArray, contains} from '../util';
import {isArray, contains, Dict} from '../util';
import {sortField} from './common';
import {Model} from './model';
import {UnitModel} from './unit';
import {scaleType} from './scale';

@@ -41,3 +42,3 @@ export interface StackProperties {

/** Compile stack properties from a given spec */
export function compileStackProperties(mark: Mark, encoding: Encoding, scale: ScaleMap, config: Config) {
export function compileStackProperties(mark: Mark, encoding: Encoding, scale: Dict<Scale>, config: Config) {
const stackFields = getStackFields(mark, encoding, scale);

@@ -73,4 +74,4 @@

/** Compile stack-by field names from (from 'color' and 'detail') */
function getStackFields(mark: Mark, encoding: Encoding, scale: ScaleMap) {
return [COLOR, DETAIL].reduce(function(fields, channel) {
function getStackFields(mark: Mark, encoding: Encoding, scaleMap: Dict<Scale>) {
return [COLOR, DETAIL, OPACITY, SIZE].reduce(function(fields, channel) {
const channelEncoding = encoding[channel];

@@ -84,4 +85,5 @@ if (has(encoding, channel)) {

const fieldDef: FieldDef = channelEncoding;
const scale = scaleMap[channel];
fields.push(field(fieldDef, {
binSuffix: scaleType(scale[channel], fieldDef, channel, mark) === ScaleType.ORDINAL ? '_range' : '_start'
binSuffix: scale && scale.type === ScaleType.ORDINAL ? '_range' : '_start'
}));

@@ -107,3 +109,3 @@ }

export function stackTransform(model: Model) {
export function stackTransform(model: UnitModel) {
const stack = model.stack();

@@ -110,0 +112,0 @@ const encoding = model.encoding();

@@ -5,44 +5,2 @@ "use strict";

var timeunit_1 = require('../timeunit');
function format(timeUnit, abbreviated) {
if (abbreviated === void 0) { abbreviated = false; }
if (!timeUnit) {
return undefined;
}
var timeString = timeUnit.toString();
var dateComponents = [];
if (timeString.indexOf('year') > -1) {
dateComponents.push(abbreviated ? '%y' : '%Y');
}
if (timeString.indexOf('month') > -1) {
dateComponents.push(abbreviated ? '%b' : '%B');
}
if (timeString.indexOf('day') > -1) {
dateComponents.push(abbreviated ? '%a' : '%A');
}
else if (timeString.indexOf('date') > -1) {
dateComponents.push('%d');
}
var timeComponents = [];
if (timeString.indexOf('hours') > -1) {
timeComponents.push('%H');
}
if (timeString.indexOf('minutes') > -1) {
timeComponents.push('%M');
}
if (timeString.indexOf('seconds') > -1) {
timeComponents.push('%S');
}
if (timeString.indexOf('milliseconds') > -1) {
timeComponents.push('%L');
}
var out = [];
if (dateComponents.length > 0) {
out.push(dateComponents.join('-'));
}
if (timeComponents.length > 0) {
out.push(timeComponents.join(':'));
}
return out.length > 0 ? out.join(' ') : undefined;
}
exports.format = format;
function smallestUnit(timeUnit) {

@@ -49,0 +7,0 @@ if (!timeUnit) {

@@ -5,52 +5,2 @@ import {contains, range} from '../util';

/** returns the template name used for axis labels for a time unit */
export function format(timeUnit: TimeUnit, abbreviated = false): string {
if (!timeUnit) {
return undefined;
}
let timeString = timeUnit.toString();
let dateComponents = [];
if (timeString.indexOf('year') > -1) {
dateComponents.push(abbreviated ? '%y' : '%Y');
}
if (timeString.indexOf('month') > -1) {
dateComponents.push(abbreviated ? '%b' : '%B');
}
if (timeString.indexOf('day') > -1) {
dateComponents.push(abbreviated ? '%a' : '%A');
} else if (timeString.indexOf('date') > -1) {
dateComponents.push('%d');
}
let timeComponents = [];
if (timeString.indexOf('hours') > -1) {
timeComponents.push('%H');
}
if (timeString.indexOf('minutes') > -1) {
timeComponents.push('%M');
}
if (timeString.indexOf('seconds') > -1) {
timeComponents.push('%S');
}
if (timeString.indexOf('milliseconds') > -1) {
timeComponents.push('%L');
}
let out = [];
if (dateComponents.length > 0) {
out.push(dateComponents.join('-'));
}
if (timeComponents.length > 0) {
out.push(timeComponents.join(':'));
}
return out.length > 0 ? out.join(' ') : undefined;
}
/** returns the smallest nice unit for scale.nice */

@@ -57,0 +7,0 @@ export function smallestUnit(timeUnit): string {

@@ -62,2 +62,18 @@ "use strict";

var StackOffset = exports.StackOffset;
(function (Interpolate) {
Interpolate[Interpolate["LINEAR"] = 'linear'] = "LINEAR";
Interpolate[Interpolate["LINEAR_CLOSED"] = 'linear-closed'] = "LINEAR_CLOSED";
Interpolate[Interpolate["STEP"] = 'step'] = "STEP";
Interpolate[Interpolate["STEP_BEFORE"] = 'step-before'] = "STEP_BEFORE";
Interpolate[Interpolate["STEP_AFTER"] = 'step-after'] = "STEP_AFTER";
Interpolate[Interpolate["BASIS"] = 'basis'] = "BASIS";
Interpolate[Interpolate["BASIS_OPEN"] = 'basis-open'] = "BASIS_OPEN";
Interpolate[Interpolate["BASIS_CLOSED"] = 'basis-closed'] = "BASIS_CLOSED";
Interpolate[Interpolate["CARDINAL"] = 'cardinal'] = "CARDINAL";
Interpolate[Interpolate["CARDINAL_OPEN"] = 'cardinal-open'] = "CARDINAL_OPEN";
Interpolate[Interpolate["CARDINAL_CLOSED"] = 'cardinal-closed'] = "CARDINAL_CLOSED";
Interpolate[Interpolate["BUNDLE"] = 'bundle'] = "BUNDLE";
Interpolate[Interpolate["MONOTONE"] = 'monotone'] = "MONOTONE";
})(exports.Interpolate || (exports.Interpolate = {}));
var Interpolate = exports.Interpolate;
exports.defaultMarkConfig = {

@@ -68,2 +84,3 @@ color: '#4682b4',

barThinSize: 2,
ruleSize: 1,
tickThickness: 1,

@@ -70,0 +87,0 @@ fontSize: 10,

@@ -20,3 +20,4 @@ import {ScaleConfig, FacetScaleConfig, defaultScaleConfig, defaultFacetScaleConfig} from './scale';

strokeOpacity?: number;
strokeDash?: number;
/** An array of alternating stroke, space lengths for creating dashed or dotted lines. */
strokeDash?: number[];
/** The offset (in pixels) into which to begin drawing with the stroke dash array. */

@@ -101,2 +102,31 @@ strokeDashOffset?: number;

export enum Interpolate {
/** piecewise linear segments, as in a polyline */
LINEAR = 'linear' as any,
/** close the linear segments to form a polygon */
LINEAR_CLOSED = 'linear-closed' as any,
/** alternate between horizontal and vertical segments, as in a step function */
STEP = 'step' as any,
/** alternate between vertical and horizontal segments, as in a step function */
STEP_BEFORE = 'step-before' as any,
/** alternate between horizontal and vertical segments, as in a step function */
STEP_AFTER = 'step-after' as any,
/** a B-spline, with control point duplication on the ends */
BASIS = 'basis' as any,
/** an open B-spline; may not intersect the start or end */
BASIS_OPEN = 'basis-open' as any,
/** a closed B-spline, as in a loop */
BASIS_CLOSED = 'basis-closed' as any,
/** a Cardinal spline, with control point duplication on the ends */
CARDINAL = 'cardinal' as any,
/** an open Cardinal spline; may not intersect the start or end, but will intersect other control points */
CARDINAL_OPEN = 'cardinal-open' as any,
/** a closed Cardinal spline, as in a loop */
CARDINAL_CLOSED = 'cardinal-closed' as any,
/** equivalent to basis, except the tension parameter is used to straighten the spline */
BUNDLE = 'bundle' as any,
/** cubic interpolation that preserves monotonicity in y */
MONOTONE = 'monotone' as any,
}
export interface MarkConfig {

@@ -170,3 +200,3 @@

* The value is either horizontal (default) or vertical.
* - For bar and tick, this determines whether the size of the bar and tick
* - For bar, rule and tick, this determines whether the size of the bar and tick
* should be applied to x or y dimension.

@@ -183,5 +213,5 @@ * - For area, this property determines the orient property of the Vega output.

/**
* The line interpolation method to use. One of linear, step-before, step-after, basis, basis-open, basis-closed, bundle, cardinal, cardinal-open, cardinal-closed, monotone.
* The line interpolation method to use. One of linear, step-before, step-after, basis, basis-open, cardinal, cardinal-open, monotone.
*/
interpolate?: string;
interpolate?: Interpolate;
/**

@@ -192,2 +222,14 @@ * Depending on the interpolation type, sets the tension parameter.

// ---------- Line ---------
/**
* Size of line mark.
*/
lineSize?: number;
// ---------- Rule ---------
/**
* Size of rule mark.
*/
ruleSize?: number;
// ---------- Bar ----------

@@ -293,2 +335,4 @@ /**

barThinSize: 2,
// lineSize is undefined by default, and refer to value from strokeWidth
ruleSize: 1,
tickThickness: 1,

@@ -295,0 +339,0 @@

@@ -9,6 +9,13 @@ "use strict";

var DataFormat = exports.DataFormat;
exports.SUMMARY = 'summary';
exports.SOURCE = 'source';
exports.STACKED_SCALE = 'stacked_scale';
exports.LAYOUT = 'layout';
(function (DataTable) {
DataTable[DataTable["SOURCE"] = 'source'] = "SOURCE";
DataTable[DataTable["SUMMARY"] = 'summary'] = "SUMMARY";
DataTable[DataTable["STACKED_SCALE"] = 'stacked_scale'] = "STACKED_SCALE";
DataTable[DataTable["LAYOUT"] = 'layout'] = "LAYOUT";
})(exports.DataTable || (exports.DataTable = {}));
var DataTable = exports.DataTable;
exports.SUMMARY = DataTable.SUMMARY;
exports.SOURCE = DataTable.SOURCE;
exports.STACKED_SCALE = DataTable.STACKED_SCALE;
exports.LAYOUT = DataTable.LAYOUT;
exports.types = {

@@ -15,0 +22,0 @@ 'boolean': type_1.Type.NOMINAL,

@@ -21,7 +21,13 @@ /*

export enum DataTable {
SOURCE = 'source' as any,
SUMMARY = 'summary' as any,
STACKED_SCALE = 'stacked_scale' as any,
LAYOUT = 'layout' as any
}
export const SUMMARY = 'summary';
export const SOURCE = 'source';
export const STACKED_SCALE = 'stacked_scale';
export const LAYOUT = 'layout';
export const SUMMARY = DataTable.SUMMARY;
export const SOURCE = DataTable.SOURCE;
export const STACKED_SCALE = DataTable.STACKED_SCALE;
export const LAYOUT = DataTable.LAYOUT;

@@ -28,0 +34,0 @@ /** Mapping from datalib's inferred type to Vega-lite's type */

@@ -9,2 +9,5 @@ "use strict";

}
if (encoding.opacity) {
count++;
}
if (encoding.size) {

@@ -59,7 +62,11 @@ count++;

function forEach(encoding, f, thisArg) {
channelMappingForEach(channel_1.CHANNELS, encoding, f, thisArg);
}
exports.forEach = forEach;
function channelMappingForEach(channels, mapping, f, thisArg) {
var i = 0;
channel_1.CHANNELS.forEach(function (channel) {
if (has(encoding, channel)) {
if (util_1.isArray(encoding[channel])) {
encoding[channel].forEach(function (fieldDef) {
channels.forEach(function (channel) {
if (has(mapping, channel)) {
if (util_1.isArray(mapping[channel])) {
mapping[channel].forEach(function (fieldDef) {
f.call(thisArg, fieldDef, channel, i++);

@@ -69,3 +76,3 @@ });

else {
f.call(thisArg, encoding[channel], channel, i++);
f.call(thisArg, mapping[channel], channel, i++);
}

@@ -75,14 +82,18 @@ }

}
exports.forEach = forEach;
exports.channelMappingForEach = channelMappingForEach;
function map(encoding, f, thisArg) {
return channelMappingMap(channel_1.CHANNELS, encoding, f, thisArg);
}
exports.map = map;
function channelMappingMap(channels, mapping, f, thisArg) {
var arr = [];
channel_1.CHANNELS.forEach(function (channel) {
if (has(encoding, channel)) {
if (util_1.isArray(encoding[channel])) {
encoding[channel].forEach(function (fieldDef) {
arr.push(f.call(thisArg, fieldDef, channel, encoding));
channels.forEach(function (channel) {
if (has(mapping, channel)) {
if (util_1.isArray(mapping[channel])) {
mapping[channel].forEach(function (fieldDef) {
arr.push(f.call(thisArg, fieldDef, channel));
});
}
else {
arr.push(f.call(thisArg, encoding[channel], channel, encoding));
arr.push(f.call(thisArg, mapping[channel], channel));
}

@@ -93,14 +104,18 @@ }

}
exports.map = map;
exports.channelMappingMap = channelMappingMap;
function reduce(encoding, f, init, thisArg) {
return channelMappingReduce(channel_1.CHANNELS, encoding, f, init, thisArg);
}
exports.reduce = reduce;
function channelMappingReduce(channels, mapping, f, init, thisArg) {
var r = init;
channel_1.CHANNELS.forEach(function (channel) {
if (has(encoding, channel)) {
if (util_1.isArray(encoding[channel])) {
encoding[channel].forEach(function (fieldDef) {
r = f.call(thisArg, r, fieldDef, channel, encoding);
if (has(mapping, channel)) {
if (util_1.isArray(mapping[channel])) {
mapping[channel].forEach(function (fieldDef) {
r = f.call(thisArg, r, fieldDef, channel);
});
}
else {
r = f.call(thisArg, r, encoding[channel], channel, encoding);
r = f.call(thisArg, r, mapping[channel], channel);
}

@@ -111,3 +126,3 @@ }

}
exports.reduce = reduce;
exports.channelMappingReduce = channelMappingReduce;
//# sourceMappingURL=encoding.js.map

@@ -6,8 +6,8 @@ // utility for encoding mapping

export interface Encoding {
// TODO: once we decompose facet, rename this to Encoding
export interface UnitEncoding {
x?: PositionChannelDef;
y?: PositionChannelDef;
row?: FacetChannelDef;
column?: FacetChannelDef;
color?: ChannelDefWithLegend;
opacity?: ChannelDefWithLegend;
size?: ChannelDefWithLegend;

@@ -23,2 +23,7 @@ shape?: ChannelDefWithLegend; // TODO: maybe distinguish ordinal-only

// TODO: once we decompose facet, rename this to ExtendedEncoding
export interface Encoding extends UnitEncoding {
row?: FacetChannelDef;
column?: FacetChannelDef;
}

@@ -28,2 +33,3 @@ export function countRetinal(encoding: Encoding) {

if (encoding.color) { count++; }
if (encoding.opacity) { count++; }
if (encoding.size) { count++; }

@@ -76,11 +82,17 @@ if (encoding.shape) { count++; }

thisArg?: any) {
channelMappingForEach(CHANNELS, encoding, f, thisArg);
}
export function channelMappingForEach(channels: Channel[], mapping: any,
f: (fd: FieldDef, c: Channel, i: number) => void,
thisArg?: any) {
let i = 0;
CHANNELS.forEach(function(channel) {
if (has(encoding, channel)) {
if (isArray(encoding[channel])) {
encoding[channel].forEach(function(fieldDef) {
channels.forEach(function(channel) {
if (has(mapping, channel)) {
if (isArray(mapping[channel])) {
mapping[channel].forEach(function(fieldDef) {
f.call(thisArg, fieldDef, channel, i++);
});
} else {
f.call(thisArg, encoding[channel], channel, i++);
f.call(thisArg, mapping[channel], channel, i++);
}

@@ -92,13 +104,19 @@ }

export function map(encoding: Encoding,
f: (fd: FieldDef, c: Channel, e: Encoding) => any,
f: (fd: FieldDef, c: Channel, i: number) => any,
thisArg?: any) {
return channelMappingMap(CHANNELS, encoding, f , thisArg);
}
export function channelMappingMap(channels: Channel[], mapping: any,
f: (fd: FieldDef, c: Channel, i: number) => any,
thisArg?: any) {
let arr = [];
CHANNELS.forEach(function(channel) {
if (has(encoding, channel)) {
if (isArray(encoding[channel])) {
encoding[channel].forEach(function(fieldDef) {
arr.push(f.call(thisArg, fieldDef, channel, encoding));
channels.forEach(function(channel) {
if (has(mapping, channel)) {
if (isArray(mapping[channel])) {
mapping[channel].forEach(function(fieldDef) {
arr.push(f.call(thisArg, fieldDef, channel));
});
} else {
arr.push(f.call(thisArg, encoding[channel], channel, encoding));
arr.push(f.call(thisArg, mapping[channel], channel));
}

@@ -109,16 +127,22 @@ }

}
export function reduce(encoding: Encoding,
f: (acc: any, fd: FieldDef, c: Channel, e: Encoding) => any,
f: (acc: any, fd: FieldDef, c: Channel) => any,
init,
thisArg?: any) {
return channelMappingReduce(CHANNELS, encoding, f, init, thisArg);
}
export function channelMappingReduce(channels: Channel[], mapping: any,
f: (acc: any, fd: FieldDef, c: Channel) => any,
init,
thisArg?: any) {
let r = init;
CHANNELS.forEach(function(channel) {
if (has(encoding, channel)) {
if (isArray(encoding[channel])) {
encoding[channel].forEach(function(fieldDef) {
r = f.call(thisArg, r, fieldDef, channel, encoding);
if (has(mapping, channel)) {
if (isArray(mapping[channel])) {
mapping[channel].forEach(function(fieldDef) {
r = f.call(thisArg, r, fieldDef, channel);
});
} else {
r = f.call(thisArg, r, encoding[channel], channel, encoding);
r = f.call(thisArg, r, mapping[channel], channel);
}

@@ -125,0 +149,0 @@ }

@@ -55,5 +55,5 @@ "use strict";

exports.isMeasure = isMeasure;
exports.COUNT_DISPLAYNAME = 'Number of Records';
exports.COUNT_TITLE = 'Number of Records';
function count() {
return { field: '*', aggregate: aggregate_1.AggregateOp.COUNT, type: type_1.QUANTITATIVE, displayName: exports.COUNT_DISPLAYNAME };
return { field: '*', aggregate: aggregate_1.AggregateOp.COUNT, type: type_1.QUANTITATIVE, title: exports.COUNT_TITLE };
}

@@ -103,4 +103,7 @@ exports.count = count;

function title(fieldDef) {
if (fieldDef.title != null) {
return fieldDef.title;
}
if (isCount(fieldDef)) {
return exports.COUNT_DISPLAYNAME;
return exports.COUNT_TITLE;
}

@@ -107,0 +110,0 @@ var fn = fieldDef.aggregate || fieldDef.timeUnit || (fieldDef.bin && 'bin');

@@ -28,5 +28,4 @@ // utility for a field definition object

// TODO: maybe extend this in other app?
// unused metadata -- for other application
displayName?: string;
// metadata
title?: string;
}

@@ -121,6 +120,6 @@

export const COUNT_DISPLAYNAME = 'Number of Records';
export const COUNT_TITLE = 'Number of Records';
export function count(): FieldDef {
return { field: '*', aggregate: AggregateOp.COUNT, type: QUANTITATIVE, displayName: COUNT_DISPLAYNAME };
return { field: '*', aggregate: AggregateOp.COUNT, type: QUANTITATIVE, title: COUNT_TITLE };
}

@@ -180,4 +179,7 @@

export function title(fieldDef: FieldDef) {
if (fieldDef.title != null) {
return fieldDef.title;
}
if (isCount(fieldDef)) {
return COUNT_DISPLAYNAME;
return COUNT_TITLE;
}

@@ -184,0 +186,0 @@ const fn = fieldDef.aggregate || fieldDef.timeUnit || (fieldDef.bin && 'bin');

@@ -7,2 +7,54 @@ export interface LegendConfig {

/**
* The offset, in pixels, by which to displace the legend from the edge of the enclosing group or data rectangle.
*/
offset?: number;
/**
* The padding, in pixels, between the lengend and axis.
*/
padding?: number;
/**
* The margin around the legend, in pixels
*/
margin?: number;
/**
* The color of the gradient stroke, can be in hex color code or regular color name.
*/
gradientStrokeColor?: string;
/**
* The width of the gradient stroke, in pixels.
*/
gradientStrokeWidth?: number;
/**
* The height of the gradient, in pixels.
*/
gradientHeight?: number;
/**
* The width of the gradient, in pixels.
*/
gradientWidth?: number;
/**
* The alignment of the legend label, can be left, middle or right.
*/
labelAlign?: string;
/**
* The position of the baseline of legend label, can be top, middle or bottom.
*/
labelBaseline?: string;
/**
* The color of the legend label, can be in hex color code or regular color name.
*/
labelColor?: string;
/**
* The font of the lengend label.
*/
labelFont?: string;
/**
* The font size of lengend lable.
*/
labelFontSize?: number;
/**
* The offset of the legend label.
*/
labelOffset?: number;
/**
* Whether month names and weekday names should be abbreviated.

@@ -12,4 +64,40 @@ */

/**
* The color of the legend symbol,
*/
symbolColor?: string;
/**
* The shape of the legend symbol, can be the 'circle', 'square', 'cross', 'diamond',
* 'triangle-up', 'triangle-down'.
*/
symbolShape?: string;
/**
* The size of the lengend symbol, in pixels.
*/
symbolSize?: number;
/**
* The width of the symbol's stroke.
*/
symbolStrokeWidth?: number;
/**
* Optional mark property definitions for custom legend styling.
*/
/**
* The color of the legend title, can be in hex color code or regular color name.
*/
titleColor?: string;
/**
* The font of the legend title.
*/
titleFont?: string;
/**
* The font size of the legend title.
*/
titleFontSize?: number;
/**
* The font weight of the legend title.
*/
titleFontWeight?: string;
/**
* Optional mark property definitions for custom legend styling.
*/
properties?: any; // TODO(#975) replace with config properties

@@ -16,0 +104,0 @@ }

@@ -9,2 +9,3 @@ "use strict";

Mark[Mark["TICK"] = 'tick'] = "TICK";
Mark[Mark["RULE"] = 'rule'] = "RULE";
Mark[Mark["CIRCLE"] = 'circle'] = "CIRCLE";

@@ -20,4 +21,5 @@ Mark[Mark["SQUARE"] = 'square'] = "SQUARE";

exports.TICK = Mark.TICK;
exports.RULE = Mark.RULE;
exports.CIRCLE = Mark.CIRCLE;
exports.SQUARE = Mark.SQUARE;
//# sourceMappingURL=mark.js.map

@@ -8,2 +8,3 @@ export enum Mark {

TICK = 'tick' as any,
RULE = 'rule' as any,
CIRCLE = 'circle' as any,

@@ -19,4 +20,5 @@ SQUARE = 'square' as any

export const TICK = Mark.TICK;
export const RULE = Mark.RULE;
export const CIRCLE = Mark.CIRCLE;
export const SQUARE = Mark.SQUARE;

@@ -29,7 +29,10 @@ "use strict";

padding: 1,
includeRawDomain: false,
useRawDomain: false,
opacity: [0.3, 0.8],
nominalColorRange: 'category10',
sequentialColorRange: ['#AFC6A3', '#09622A'],
shapeRange: 'shapes',
fontSizeRange: [8, 40]
fontSizeRange: [8, 40],
ruleSizeRange: [1, 5],
tickSizeRange: [1, 20]
};

@@ -36,0 +39,0 @@ exports.defaultFacetScaleConfig = {

@@ -42,2 +42,6 @@ export enum ScaleType {

/**
* Default range for opacity.
*/
opacity?: number[];
/**
* Default padding for `x` and `y` ordinal scales.

@@ -47,4 +51,7 @@ */

// Experimental Feature
includeRawDomain?: boolean;
/**
* Uses the source data range as scale domain instead of aggregated data for aggregate axis.
* This property only works with aggregate functions that produce values within the raw data domain (`"mean"`, `"average"`, `"stdev"`, `"stdevp"`, `"median"`, `"q1"`, `"q3"`, `"min"`, `"max"`). For other aggregations that produce values outside of the raw data domain (e.g. `"count"`, `"sum"`), this property is ignored.
*/
useRawDomain?: boolean;

@@ -64,2 +71,8 @@ /** Default range for nominal color scale */

/** Default range for rule stroke widths */
ruleSizeRange?: number[];
/** Default range for tick spans */
tickSizeRange?: number[];
/** Default range for bar size scale */

@@ -77,3 +90,4 @@ pointSizeRange?: number[];

padding: 1,
includeRawDomain: false,
useRawDomain: false,
opacity: [0.3, 0.8],

@@ -83,3 +97,5 @@ nominalColorRange: 'category10',

shapeRange: 'shapes',
fontSizeRange: [8, 40]
fontSizeRange: [8, 40],
ruleSizeRange: [1, 5],
tickSizeRange: [1, 20]
};

@@ -142,5 +158,6 @@

/**
* Uses the source 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.
* Uses the source data range as scale domain instead of aggregated data for aggregate axis.
* This property only works with aggregate functions that produce values within the raw data domain (`"mean"`, `"average"`, `"stdev"`, `"stdevp"`, `"median"`, `"q1"`, `"q3"`, `"min"`, `"max"`). For other aggregations that produce values outside of the raw data domain (e.g. `"count"`, `"sum"`), this property is ignored.
*/
includeRawDomain?: boolean;
useRawDomain?: boolean;
}

@@ -5,3 +5,3 @@ /** module for shorthand */

import {FieldDef} from './fielddef';
import {Spec} from './spec';
import {ExtendedUnitSpec} from './spec';

@@ -20,3 +20,3 @@ import {AggregateOp, AGGREGATE_OPS} from './aggregate';

export function shorten(spec: Spec): string {
export function shorten(spec: ExtendedUnitSpec): string {
return 'mark' + ASSIGN + spec.mark +

@@ -31,3 +31,3 @@ DELIM + shortenEncoding(spec.encoding);

let spec:Spec = {
let spec:ExtendedUnitSpec = {
mark: Mark[mark],

@@ -34,0 +34,0 @@ encoding: encoding

"use strict";
var encoding_1 = require('./encoding');
var channel_1 = require('./channel');

@@ -6,2 +7,48 @@ var vlEncoding = require('./encoding');

var util_1 = require('./util');
function isFacetSpec(spec) {
return spec['facet'] !== undefined;
}
exports.isFacetSpec = isFacetSpec;
function isExtendedUnitSpec(spec) {
if (isSomeUnitSpec(spec)) {
var hasRow = encoding_1.has(spec.encoding, channel_1.ROW);
var hasColumn = encoding_1.has(spec.encoding, channel_1.COLUMN);
return hasRow || hasColumn;
}
return false;
}
exports.isExtendedUnitSpec = isExtendedUnitSpec;
function isUnitSpec(spec) {
if (isSomeUnitSpec(spec)) {
return !isExtendedUnitSpec(spec);
}
return false;
}
exports.isUnitSpec = isUnitSpec;
function isSomeUnitSpec(spec) {
return spec['mark'] !== undefined;
}
exports.isSomeUnitSpec = isSomeUnitSpec;
function isLayerSpec(spec) {
return spec['layers'] !== undefined;
}
exports.isLayerSpec = isLayerSpec;
function normalize(spec) {
if (isExtendedUnitSpec(spec)) {
var hasRow = encoding_1.has(spec.encoding, channel_1.ROW);
var hasColumn = encoding_1.has(spec.encoding, channel_1.COLUMN);
var encoding = util_1.duplicate(spec.encoding);
delete encoding.column;
delete encoding.row;
return util_1.extend(spec.name ? { name: spec.name } : {}, spec.description ? { description: spec.description } : {}, { data: spec.data }, spec.transform ? { transform: spec.transform } : {}, {
facet: util_1.extend(hasRow ? { row: spec.encoding.row } : {}, hasColumn ? { column: spec.encoding.column } : {}),
spec: {
mark: spec.mark,
encoding: encoding
}
}, spec.config ? { config: spec.config } : {});
}
return spec;
}
exports.normalize = normalize;
function alwaysNoOcclusion(spec) {

@@ -8,0 +55,0 @@ return vlEncoding.isAggregate(spec.encoding);

@@ -8,33 +8,132 @@ /* Utilities for a Vega-Lite specificiation */

import {Data} from './data';
import {Encoding} from './encoding';
import {Encoding, UnitEncoding, has} from './encoding';
import {Facet} from './facet';
import {Mark} from './mark';
import {Transform} from './transform';
import {COLOR, SHAPE} from './channel';
import {COLOR, SHAPE, ROW, COLUMN} from './channel';
import * as vlEncoding from './encoding';
import {BAR, AREA} from './mark';
import {duplicate} from './util';
import {duplicate, extend} from './util';
export interface BaseSpec {
name?: string;
description?: string;
data?: Data;
transform?: Transform;
config?: Config;
}
export interface UnitSpec extends BaseSpec {
mark: Mark;
encoding?: UnitEncoding;
}
/**
* Schema for Vega-Lite specification
* Schema for a unit Vega-Lite specification, with the syntactic sugar extensions:
* - `row` and `column` are included in the encoding.
* - (Future) label, box plot
*
* Note: the spec could contain facet.
*
* @required ["mark", "encoding"]
*/
export interface Spec {
export interface ExtendedUnitSpec extends BaseSpec {
/**
* A name for the specification. The name is used to annotate marks, scale names, and more.
*/
name?: string;
description?: string;
data?: Data;
transform?: Transform;
mark: Mark;
encoding: Encoding;
config?: Config;
encoding?: Encoding;
}
export interface FacetSpec extends BaseSpec {
facet: Facet;
spec: LayerSpec | UnitSpec;
}
export interface LayerSpec extends BaseSpec {
layers: UnitSpec[];
}
/** This is for the future schema */
export interface ExtendedFacetSpec extends BaseSpec {
facet: Facet;
spec: ExtendedUnitSpec | FacetSpec;
}
export type ExtendedSpec = ExtendedUnitSpec | FacetSpec | LayerSpec;
export type Spec = UnitSpec | FacetSpec | LayerSpec;
/* Custom type guards */
export function isFacetSpec(spec: ExtendedSpec): spec is FacetSpec {
return spec['facet'] !== undefined;
}
export function isExtendedUnitSpec(spec: ExtendedSpec): spec is ExtendedUnitSpec {
if (isSomeUnitSpec(spec)) {
const hasRow = has(spec.encoding, ROW);
const hasColumn = has(spec.encoding, COLUMN);
return hasRow || hasColumn;
}
return false;
}
export function isUnitSpec(spec: ExtendedSpec): spec is UnitSpec {
if (isSomeUnitSpec(spec)) {
return !isExtendedUnitSpec(spec);
}
return false;
}
export function isSomeUnitSpec(spec: ExtendedSpec): spec is ExtendedUnitSpec | UnitSpec {
return spec['mark'] !== undefined;
}
export function isLayerSpec(spec: ExtendedSpec): spec is LayerSpec {
return spec['layers'] !== undefined;
}
/**
* Decompose extended unit specs into composition of pure unit specs.
*/
export function normalize(spec: ExtendedSpec): Spec {
if (isExtendedUnitSpec(spec)) {
const hasRow = has(spec.encoding, ROW);
const hasColumn = has(spec.encoding, COLUMN);
// TODO: @arvind please add interaction syntax here
let encoding = duplicate(spec.encoding);
delete encoding.column;
delete encoding.row;
return extend(
spec.name ? { name: spec.name } : {},
spec.description ? { description: spec.description } : {},
{ data: spec.data },
spec.transform ? { transform: spec.transform } : {},
{
facet: extend(
hasRow ? { row: spec.encoding.row } : {},
hasColumn ? { column: spec.encoding.column } : {}
),
spec: {
mark: spec.mark,
encoding: encoding
}
},
spec.config ? { config: spec.config } : {}
);
}
return spec;
}
// TODO: add vl.spec.validate & move stuff from vl.validate to here
export function alwaysNoOcclusion(spec: Spec): boolean {
export function alwaysNoOcclusion(spec: ExtendedUnitSpec): boolean {
// FIXME raw OxQ with # of rows = # of O

@@ -44,3 +143,3 @@ return vlEncoding.isAggregate(spec.encoding);

export function fieldDefs(spec: Spec): FieldDef[] {
export function fieldDefs(spec: ExtendedUnitSpec): FieldDef[] {
// TODO: refactor this once we have composition

@@ -50,3 +149,3 @@ return vlEncoding.fieldDefs(spec.encoding);

export function getCleanSpec(spec: Spec): Spec {
export function getCleanSpec(spec: ExtendedUnitSpec): ExtendedUnitSpec {
// TODO: move toSpec to here!

@@ -56,3 +155,3 @@ return spec;

export function isStack(spec: Spec): boolean {
export function isStack(spec: ExtendedUnitSpec): boolean {
return (vlEncoding.has(spec.encoding, COLOR) || vlEncoding.has(spec.encoding, SHAPE)) &&

@@ -65,3 +164,3 @@ (spec.mark === BAR || spec.mark === AREA) &&

// TODO revise
export function transpose(spec: Spec): Spec {
export function transpose(spec: ExtendedUnitSpec): ExtendedUnitSpec {
const oldenc = spec.encoding;

@@ -68,0 +167,0 @@ let encoding = duplicate(spec.encoding);

@@ -47,2 +47,44 @@ "use strict";

];
function format(timeUnit, abbreviated) {
if (abbreviated === void 0) { abbreviated = false; }
if (!timeUnit) {
return undefined;
}
var timeString = timeUnit.toString();
var dateComponents = [];
if (timeString.indexOf('year') > -1) {
dateComponents.push(abbreviated ? '%y' : '%Y');
}
if (timeString.indexOf('month') > -1) {
dateComponents.push(abbreviated ? '%b' : '%B');
}
if (timeString.indexOf('day') > -1) {
dateComponents.push(abbreviated ? '%a' : '%A');
}
else if (timeString.indexOf('date') > -1) {
dateComponents.push('%d');
}
var timeComponents = [];
if (timeString.indexOf('hours') > -1) {
timeComponents.push('%H');
}
if (timeString.indexOf('minutes') > -1) {
timeComponents.push('%M');
}
if (timeString.indexOf('seconds') > -1) {
timeComponents.push('%S');
}
if (timeString.indexOf('milliseconds') > -1) {
timeComponents.push('%L');
}
var out = [];
if (dateComponents.length > 0) {
out.push(dateComponents.join('-'));
}
if (timeComponents.length > 0) {
out.push(timeComponents.join(':'));
}
return out.length > 0 ? out.join(' ') : undefined;
}
exports.format = format;
//# sourceMappingURL=timeunit.js.map

@@ -47,1 +47,51 @@

];
/** returns the template name used for axis labels for a time unit */
export function format(timeUnit: TimeUnit, abbreviated = false): string {
if (!timeUnit) {
return undefined;
}
let timeString = timeUnit.toString();
let dateComponents = [];
if (timeString.indexOf('year') > -1) {
dateComponents.push(abbreviated ? '%y' : '%Y');
}
if (timeString.indexOf('month') > -1) {
dateComponents.push(abbreviated ? '%b' : '%B');
}
if (timeString.indexOf('day') > -1) {
dateComponents.push(abbreviated ? '%a' : '%A');
} else if (timeString.indexOf('date') > -1) {
dateComponents.push('%d');
}
let timeComponents = [];
if (timeString.indexOf('hours') > -1) {
timeComponents.push('%H');
}
if (timeString.indexOf('minutes') > -1) {
timeComponents.push('%M');
}
if (timeString.indexOf('seconds') > -1) {
timeComponents.push('%S');
}
if (timeString.indexOf('milliseconds') > -1) {
timeComponents.push('%L');
}
let out = [];
if (dateComponents.length > 0) {
out.push(dateComponents.join('-'));
}
if (timeComponents.length > 0) {
out.push(timeComponents.join(':'));
}
return out.length > 0 ? out.join(' ') : undefined;
}

@@ -13,7 +13,7 @@ export interface Transform {

*/
calculate?: VgFormula[];
calculate?: Formula[];
}
// TODO move all Vega interfaces to one central position
export interface VgFormula {
export interface Formula {
/**

@@ -20,0 +20,0 @@ * The field in which to store the computed formula value.

"use strict";
var stringify = require('json-stable-stringify');
var util_1 = require('datalib/src/util');

@@ -11,4 +12,19 @@ exports.keys = util_1.keys;

exports.isObject = util_1.isObject;
exports.isString = util_1.isString;
exports.isNumber = util_1.isNumber;
exports.isBoolean = util_1.isBoolean;
var generate_1 = require('datalib/src/generate');
exports.range = generate_1.range;
var encoding_1 = require('./encoding');
exports.has = encoding_1.has;
var channel_1 = require('./channel');
exports.Channel = channel_1.Channel;
var util_2 = require('datalib/src/util');
function hash(a) {
if (util_2.isString(a) || util_2.isNumber(a) || util_2.isBoolean(a)) {
return String(a);
}
return stringify(a);
}
exports.hash = hash;
function contains(array, item) {

@@ -18,8 +34,12 @@ return array.indexOf(item) > -1;

exports.contains = contains;
function without(array, items) {
function without(array, excludedItems) {
return array.filter(function (item) {
return !contains(items, item);
return !contains(excludedItems, item);
});
}
exports.without = without;
function union(array, other) {
return array.concat(without(other, array));
}
exports.union = union;
function forEach(obj, f, thisArg) {

@@ -87,2 +107,6 @@ if (obj.forEach) {

exports.all = all;
function flatten(arrays) {
return [].concat.apply([], arrays);
}
exports.flatten = flatten;
function mergeDeep(dest) {

@@ -132,2 +156,21 @@ var src = [];

exports.getbins = getbins;
function unique(values, f) {
var results = [];
var u = {}, v, i, n;
for (i = 0, n = values.length; i < n; ++i) {
v = f ? f(values[i]) : values[i];
if (v in u) {
continue;
}
u[v] = 1;
results.push(values[i]);
}
return results;
}
exports.unique = unique;
;
function warning(message) {
console.warn('[VL Warning]', message);
}
exports.warning = warning;
function error(message) {

@@ -137,2 +180,13 @@ console.error('[VL Error]', message);

exports.error = error;
function differ(dict, other) {
for (var key in dict) {
if (dict.hasOwnProperty(key)) {
if (other[key] && dict[key] && other[key] !== dict[key]) {
return true;
}
}
}
return false;
}
exports.differ = differ;
//# sourceMappingURL=util.js.map
/// <reference path="../typings/datalib.d.ts"/>
/// <reference path="../typings/json-stable-stringify.d.ts"/>
export {keys, extend, duplicate, isArray, vals, truncate, toMap, isObject} from 'datalib/src/util';
import * as stringify from 'json-stable-stringify';
export {keys, extend, duplicate, isArray, vals, truncate, toMap, isObject, isString, isNumber, isBoolean} from 'datalib/src/util';
export {range} from 'datalib/src/generate';
export {has} from './encoding'
export {FieldDef} from './fielddef';
export {Channel} from './channel';
import {isString, isNumber, isBoolean} from 'datalib/src/util';
export function hash(a: any) {
if (isString(a) || isNumber(a) || isBoolean(a)) {
return String(a);
}
return stringify(a);
}
export function contains<T>(array: Array<T>, item: T) {

@@ -11,9 +25,13 @@ return array.indexOf(item) > -1;

/** Returns the array without the elements in item */
export function without<T>(array: Array<T>, items: Array<T>) {
export function without<T>(array: Array<T>, excludedItems: Array<T>) {
return array.filter(function(item) {
return !contains(items, item);
return !contains(excludedItems, item);
});
}
export function forEach(obj, f: (a, d, k, o) => any, thisArg) {
export function union<T>(array: Array<T>, other: Array<T>) {
return array.concat(without(other, array));
}
export function forEach(obj, f: (a, d, k, o) => any, thisArg?) {
if (obj.forEach) {

@@ -77,2 +95,6 @@ obj.forEach.call(thisArg, f);

export function flatten(arrays: any[]) {
return [].concat.apply([], arrays);
}
export function mergeDeep(dest, ...src: any[]) {

@@ -119,4 +141,42 @@ for (let i = 0; i < src.length; i++) {

export function unique<T>(values: T[], f?: (item: T) => string) {
let results = [];
var u = {}, v, i, n;
for (i = 0, n = values.length; i < n; ++i) {
v = f ? f(values[i]) : values[i];
if (v in u) {
continue;
}
u[v] = 1;
results.push(values[i]);
}
return results;
};
export function warning(message: any) {
console.warn('[VL Warning]', message);
}
export function error(message: any) {
console.error('[VL Error]', message);
}
export interface Dict<T> {
[key: string]: T;
}
export type StringSet = Dict<boolean>;
/**
* Returns true if the two dicitonaries disagree. Applies only to defioned values.
*/
export function differ<T>(dict: Dict<T>, other: Dict<T>) {
for (let key in dict) {
if (dict.hasOwnProperty(key)) {
if (other[key] && dict[key] && other[key] !== dict[key]) {
return true;
}
}
}
return false;
}

@@ -1,2 +0,2 @@

import {Spec} from './spec';
import {ExtendedUnitSpec} from './spec';

@@ -58,3 +58,3 @@ // TODO: move to vl.spec.validator?

*/
export function getEncodingMappingError(spec: Spec,
export function getEncodingMappingError(spec: ExtendedUnitSpec,
requiredChannelMap: RequiredChannelMap = DEFAULT_REQUIRED_CHANNEL_MAP,

@@ -61,0 +61,0 @@ supportedChannelMap: SupportedChannelMap = DEFAULT_SUPPORTED_CHANNEL_TYPE

"use strict";
var util_1 = require('./util');
function isUnionedDomain(domain) {
if (!util_1.isArray(domain)) {
return 'fields' in domain;
}
return false;
}
exports.isUnionedDomain = isUnionedDomain;
function isDataRefDomain(domain) {
if (!util_1.isArray(domain)) {
return 'data' in domain;
}
return false;
}
exports.isDataRefDomain = isDataRefDomain;
//# sourceMappingURL=vega.schema.js.map

@@ -0,1 +1,4 @@

import {isArray} from './util';
import {ScaleType, NiceTime} from './scale';
export interface VgData {

@@ -9,1 +12,61 @@ name: string;

}
type VgParentRef = {
parent: string
};
type VgFieldRef = string | VgParentRef | VgParentRef[];
export type VgDataRef = {
data: string,
field: VgFieldRef,
sort: boolean | {
field: VgFieldRef,
op: string
}
};
export type UnionedDomain = {
fields: VgDataRef[]
};
export type VgScale = {
name: string,
type: ScaleType,
domain?: any[] | UnionedDomain | VgDataRef,
domainMin?: any,
domainMax?: any
range?: any[] | VgDataRef | string,
rangeMin?: any,
rangeMax?: any,
bandSize?: number,
clamp?: boolean,
exponent?: number,
nice?: boolean | NiceTime,
padding?: number,
points?: boolean,
reverse?: boolean,
round?: boolean,
zero?: boolean
}
export function isUnionedDomain(domain: any[] | UnionedDomain | VgDataRef): domain is UnionedDomain {
if (!isArray(domain)) {
return 'fields' in domain;
}
return false;
}
export function isDataRefDomain(domain: any[] | UnionedDomain | VgDataRef): domain is VgDataRef {
if (!isArray(domain)) {
return 'data' in domain;
}
return false;
}
// TODO: declare
export type VgMarkGroup = any;
export type VgAxis = any;
export type VgLegend = any;
export type VgTransform = any;
"use strict";
var vlBin = require('./bin');
var vlChannel = require('./channel');
var vlConfig = require('./config');
var vlData = require('./data');

@@ -17,2 +18,3 @@ var vlEncoding = require('./encoding');

exports.compile = vlCompile.compile;
exports.config = vlConfig;
exports.data = vlData;

@@ -19,0 +21,0 @@ exports.encoding = vlEncoding;

import * as vlBin from './bin';
import * as vlChannel from './channel';
import * as vlConfig from './config';
import * as vlData from './data';

@@ -17,2 +18,3 @@ import * as vlEncoding from './encoding';

export const compile = vlCompile.compile;
export const config = vlConfig;
export const data = vlData;

@@ -19,0 +21,0 @@ export const encoding = vlEncoding;

@@ -42,4 +42,17 @@ {

"src/compile/config.ts",
"src/compile/data.ts",
"src/compile/data/bin.ts",
"src/compile/data/colorrank.ts",
"src/compile/data/data.ts",
"src/compile/data/filter.ts",
"src/compile/data/formatparse.ts",
"src/compile/data/formula.ts",
"src/compile/data/nonpositivenullfilter.ts",
"src/compile/data/nullfilter.ts",
"src/compile/data/source.ts",
"src/compile/data/stackscale.ts",
"src/compile/data/summary.ts",
"src/compile/data/timeunit.ts",
"src/compile/data/timeunitdomain.ts",
"src/compile/facet.ts",
"src/compile/layer.ts",
"src/compile/layout.ts",

@@ -52,11 +65,14 @@ "src/compile/legend.ts",

"src/compile/mark/point.ts",
"src/compile/mark/rule.ts",
"src/compile/mark/text.ts",
"src/compile/mark/tick.ts",
"src/compile/Model.ts",
"src/compile/model.ts",
"src/compile/scale.ts",
"src/compile/stack.ts",
"src/compile/time.ts",
"src/compile/unit.ts",
"src/config.ts",
"src/data.ts",
"src/encoding.ts",
"src/facet.ts",
"src/fielddef.ts",

@@ -80,2 +96,4 @@ "src/legend.ts",

"test/compile/data.test.ts",
"test/compile/facet.test.ts",
"test/compile/layer.test.ts",
"test/compile/legend.test.ts",

@@ -85,10 +103,12 @@ "test/compile/mark/area.test.ts",

"test/compile/mark/line.test.ts",
"test/compile/mark/mark.test.ts",
"test/compile/mark/point.test.ts",
"test/compile/mark/text.test.ts",
"test/compile/mark/tick.test.ts",
"test/compile/Model.test.ts",
"test/compile/scale.test.ts",
"test/compile/stack.test.ts",
"test/compile/unit.test.ts",
"test/fielddef.test.ts",
"test/schema.test.ts",
"test/spec.test.ts",
"test/type.test.ts",

@@ -101,2 +121,3 @@ "test/util.ts",

"typings/datalib.d.ts",
"typings/json-stable-stringify.d.ts",
"typings/mocha.d.ts",

@@ -103,0 +124,0 @@ "typings/node.d.ts",

declare module 'datalib/src/util' {
export function keys(a): Array<string>;
export function extend(a, b, ...rest);
export function duplicate(a);
export function isArray(a): boolean;
export function duplicate<T>(a: T): T;
export function isArray(a: any | any[]): a is any[];
export function vals(a);
export function truncate(a: string, length: number): string;
export function toMap(a);
export function isObject(a): boolean;
export function isObject(a): a is any;
export function isString(a): a is string;
export function isNumber(a): a is number;
export function isBoolean(a): a is boolean;
}
declare module 'datalib/src/generate' {
export function range(a: number, b?: number): Array<number>;
export function range(a: number, b?: number, step?: number): Array<number>;
}
declare module 'datalib/src/stats' {
export function summary(a: Array<Array<any>>);
}
interface BinFunc {

@@ -22,0 +20,0 @@ (o: any): {

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

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 too big to display

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

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

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