Socket
Socket
Sign inDemoInstall

vega-lite

Package Overview
Dependencies
Maintainers
2
Versions
470
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vega-lite - npm Package Compare versions

Comparing version 0.7.7 to 0.7.8

editor/bower.json

2

bower.json
{
"name": "vega-lite",
"main": "vega-lite.js",
"version": "0.7.7",
"version": "0.7.8",
"homepage": "https://github.com/uwdata/vega-lite",

@@ -6,0 +6,0 @@ "authors": [

@@ -158,6 +158,3 @@ 'use strict';

if (err) return alert('Error loading data ' + err.statusText);
dataset.stats = vl.summary(data).reduce(function(s, p) {
s[p.field] = p;
return s;
},{});
dataset.stats = vl.data.stats(data);
callback();

@@ -164,0 +161,0 @@ });

@@ -8,3 +8,3 @@ 'use strict';

// runs the tests
gulp.task('test', ['jshint'], function() {
gulp.task('test', function() {
return gulp.src(['test/**/*.spec.js'], { read: false })

@@ -15,2 +15,11 @@ .pipe(mocha({

.on('error', gutil.log);
});
// quick test
gulp.task('t', function() {
return gulp.src(['test/**/*.spec.js'], { read: false })
.pipe(mocha({
istanbul: false
}))
.on('error', gutil.log);
});

@@ -12,3 +12,3 @@ 'use strict';

gulp.task('watch-test', function() {
gulp.watch(['src/**', 'test/**'], ['test']);
gulp.watch(['src/**', 'test/**'], ['jshint', 'test']);
});
{
"name": "vega-lite",
"author": "Jeffrey Heer, Dominik Moritz, Kanit \"Ham\" Wongsuphasawat",
"version": "0.7.7",
"version": "0.7.8",
"collaborators": [

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

"scripts": {
"test": "gulp test"
"deploy": "npm run lint && npm run test && scripts/deploy.sh",
"lint": "gulp jshint",
"test": "gulp t"
},

@@ -29,30 +31,30 @@ "repository": {

"devDependencies": {
"browser-sync": "^1.9.1",
"browserify": "^8.1.1",
"browserify-shim": "^3.8.5",
"chai": "^1.10.0",
"commander": "^2.6.0",
"browser-sync": "^2.7.13",
"browserify": "^10.2.4",
"browserify-shim": "^3.8.9",
"chai": "^3.0.0",
"commander": "^2.8.1",
"coveralls": "^2.11.2",
"d3": "^3.5.5",
"deep-diff": "^0.3.0",
"gulp": "^3.8.10",
"gulp-bump": "^0.3.0",
"gulp-git": "^1.2.3",
"gulp-jshint": "^1.10.0",
"gulp-load-plugins": "^0.10.0",
"gulp-rename": "^1.2.0",
"gulp-run": "^1.6.6",
"gulp-sourcemaps": "^1.3.0",
"gulp-spawn-mocha": "^2.0.1",
"d3": "^3.5.6",
"deep-diff": "^0.3.2",
"gulp": "^3.9.0",
"gulp-bump": "^0.3.1",
"gulp-git": "^1.2.4",
"gulp-jshint": "^1.11.1",
"gulp-load-plugins": "^1.0.0-rc",
"gulp-rename": "^1.2.2",
"gulp-run": "^1.6.8",
"gulp-sourcemaps": "^1.5.2",
"gulp-spawn-mocha": "^2.2.1",
"gulp-tag-version": "^1.2.1",
"gulp-uglify": "^1.1.0",
"gulp-util": "^3.0.2",
"jshint-stylish": "^1.0.2",
"lodash": "^2.4.1",
"mocha": "^2.1.0",
"gulp-uglify": "^1.2.0",
"gulp-util": "^3.0.6",
"jshint-stylish": "^2.0.1",
"lodash": "^3.10.0",
"mocha": "^2.2.5",
"require-dir": "^0.3.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.0.0",
"watchify": "^2.2.1",
"z-schema": "^3.4.3"
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.2.3",
"z-schema": "^3.12.0"
},

@@ -62,3 +64,5 @@ "dependencies": {

"d3-color": "^0.2.1",
"datalib": "^1.1.12"
"d3-format": "^0.2.3",
"d3-time-format": "0.0.2",
"datalib": "^1.3.0"
},

@@ -65,0 +69,0 @@ "browserify": {

@@ -25,3 +25,3 @@ 'use strict';

} else {
dims[field.name] = encoding.field(encType);
dims[field.name] = encoding.fieldRef(encType);
if (encType == ROW || encType == COL) {

@@ -28,0 +28,0 @@ facets[field.name] = dims[field.name];

@@ -28,23 +28,68 @@ 'use strict';

axis.def = function(name, encoding, layout, stats, opt) {
var type = name;
var isCol = name == COL, isRow = name == ROW;
var rowOffset = axisTitleOffset(encoding, layout, Y) + 20,
cellPadding = layout.cellPadding;
var isCol = name == COL,
isRow = name == ROW,
type = isCol ? 'x' : isRow ? 'y' : name;
if (isCol) type = 'x';
if (isRow) type = 'y';
var def = {
type: type,
scale: name
scale: name,
properties: {},
layer: encoding.field(name).axis.layer,
orient: axis.orient(name, encoding, stats)
};
// Add axis label custom scale (for bin / time)
def = axis.labels.scale(def, encoding, name);
def = axis.labels.format(def, name, encoding, stats);
// for x-axis, set ticks for Q or rotate scale for ordinal scale
if (name == X) {
if (encoding.isDimension(X) || encoding.isType(X, T)) {
// TODO(kanitw): Jul 19, 2015 - #506 add condition for rotation
def = axis.labels.rotate(def);
} else { // Q
def.ticks = encoding.field(name).axis.ticks;
}
}
// TitleOffset depends on labels rotation
def.titleOffset = axis.titleOffset(encoding, layout, name);
//def.offset is used in axis.grid
if(isRow) def.offset = axis.titleOffset(encoding, layout, Y) + 20;
// FIXME(kanitw): Jul 19, 2015 - offset for column when x is put on top
def = axis.grid(def, name, encoding, layout);
def = axis.title(def, name, encoding, layout, opt);
if (isRow || isCol) def = axis.hideTicks(def);
return def;
};
axis.orient = function(name, encoding, stats) {
var orient = encoding.field(name).axis.orient;
if (orient) return orient;
if (name===COL) return 'top';
// x-axis for long y - put on top
if (name===X && encoding.has(Y) && encoding.isOrdinalScale(Y) && encoding.cardinality(Y, stats) > 30) {
return 'top';
}
return undefined;
};
axis.grid = function(def, name, encoding, layout) {
var cellPadding = layout.cellPadding,
isCol = name == COL,
isRow = name == ROW;
if (encoding.axis(name).grid) {
def.grid = true;
def.layer = 'back';
if (isCol) {
// set grid property -- put the lines on the right the cell
setter(def, ['properties', 'grid'], {
def.properties.grid = {
x: {

@@ -60,6 +105,6 @@ offset: layout.cellWidth * (1+ cellPadding/2.0),

opacity: { value: encoding.config('cellGridOpacity') }
});
};
} else if (isRow) {
// set grid property -- put the lines on the top
setter(def, ['properties', 'grid'], {
def.properties.grid = {
y: {

@@ -71,6 +116,6 @@ offset: -layout.cellHeight * (cellPadding/2),

x: {
value: rowOffset
value: def.offset
},
x2: {
offset: rowOffset + (layout.cellWidth * 0.05),
offset: def.offset + (layout.cellWidth * 0.05),
// default value(s) -- vega doesn't do recursive merge

@@ -82,71 +127,43 @@ group: 'mark.group.width',

opacity: { value: encoding.config('cellGridOpacity') }
});
};
} else {
setter(def, ['properties', 'grid'], {
def.properties.grid = {
stroke: { value: encoding.config('gridColor') },
opacity: { value: encoding.config('gridOpacity') }
});
};
}
}
return def;
};
if (encoding.axis(name).title) {
def = axis_title(def, name, encoding, layout, opt);
}
axis.hideTicks = function(def) {
def.properties.ticks = {opacity: {value: 0}};
def.properties.majorTicks = {opacity: {value: 0}};
def.properties.axis = {opacity: {value: 0}};
return def;
};
if (isRow || isCol) {
setter(def, ['properties', 'ticks'], {
opacity: {value: 0}
});
setter(def, ['properties', 'majorTicks'], {
opacity: {value: 0}
});
setter(def, ['properties', 'axis'], {
opacity: {value: 0}
});
}
axis.title = function (def, name, encoding, layout) {
var ax = encoding.field(name).axis;
if (isCol) {
def.orient = 'top';
}
if (ax.title) {
def.title = ax.title;
} else {
// if not defined, automatically determine axis title from field def
var fieldTitle = encoding.fieldTitle(name),
maxLength;
if (isRow) {
def.offset = rowOffset;
}
if (name == X) {
if (encoding.has(Y) && encoding.isOrdinalScale(Y) && encoding.cardinality(Y, stats) > 30) {
def.orient = 'top';
if (ax.titleMaxLength) {
maxLength = ax.titleMaxLength;
} else if (name===X) {
maxLength = layout.cellWidth / encoding.config('characterWidth');
} else if (name === Y) {
maxLength = layout.cellHeight / encoding.config('characterWidth');
}
if (encoding.isDimension(X) || encoding.isType(X, T)) {
setter(def, ['properties','labels'], {
angle: {value: 270},
align: {value: 'right'},
baseline: {value: 'middle'}
});
} else { // Q
def.ticks = 5;
}
def.title = maxLength ? util.truncate(fieldTitle, maxLength) : fieldTitle;
}
def = axis_labels(def, name, encoding, layout, opt);
return def;
};
function axis_title(def, name, encoding, layout, opt) {
// jshint unused:false
var maxlength = null,
fieldTitle = encoding.fieldTitle(name);
if (name===X) {
maxlength = layout.cellWidth / encoding.config('characterWidth');
} else if (name === Y) {
maxlength = layout.cellHeight / encoding.config('characterWidth');
}
def.title = maxlength ? util.truncate(fieldTitle, maxlength) : fieldTitle;
if (name === ROW) {
setter(def, ['properties','title'], {
def.properties.title = {
angle: {value: 0},

@@ -156,44 +173,63 @@ align: {value: 'right'},

dy: {value: (-layout.height/2) -20}
});
};
}
def.titleOffset = axisTitleOffset(encoding, layout, name);
return def;
}
};
function axis_labels(def, name, encoding, layout, opt) {
// jshint unused:false
axis.labels = {};
var timeUnit;
// add custom label for time type
if (encoding.isType(name, T) && (timeUnit = encoding.timeUnit(name)) && (time.hasScale(timeUnit))) {
/** add custom label for time type and bin */
axis.labels.scale = function(def, encoding, name) {
// time
var timeUnit = encoding.field(name).timeUnit;
if (encoding.isType(name, T) && timeUnit && (time.hasScale(timeUnit))) {
setter(def, ['properties','labels','text','scale'], 'time-'+ timeUnit);
}
// FIXME bin
return def;
};
var textTemplatePath = ['properties','labels','text','template'];
/**
* Determine number format or truncate if maxLabel length is presented.
*/
axis.labels.format = function (def, name, encoding, stats) {
var fieldStats = stats[encoding.field(name).name];
if (encoding.axis(name).format) {
def.format = encoding.axis(name).format;
} else if (encoding.isType(name, Q)) {
setter(def, textTemplatePath, '{{data | number:\'.3s\'}}');
} else if (encoding.isType(name, Q) || fieldStats.type === 'number') {
def.format = encoding.numberFormat(fieldStats);
} else if (encoding.isType(name, T)) {
if (!encoding.timeUnit(name)) {
setter(def, textTemplatePath, '{{data | time:\'%Y-%m-%d\'}}');
} else if (encoding.timeUnit(name) === 'year') {
setter(def, textTemplatePath, '{{data | number:\'d\'}}');
var timeUnit = encoding.field(name).timeUnit;
if (!timeUnit) {
def.format = encoding.config('timeFormat');
} else if (timeUnit === 'year') {
def.format = 'd';
}
} else if (encoding.isTypes(name, [N, O]) && encoding.axis(name).maxLabelLength) {
setter(def, textTemplatePath, '{{data | truncate:' + encoding.axis(name).maxLabelLength + '}}');
} else {
// nothing
setter(def,
['properties','labels','text','template'],
'{{data | truncate:' + encoding.axis(name).maxLabelLength + '}}'
);
}
return def;
}
};
function axisTitleOffset(encoding, layout, name) {
axis.labels.rotate = function(def) {
var align = def.orient ==='top' ? 'left' : 'right';
setter(def, ['properties','labels', 'angle', 'value'], 270);
setter(def, ['properties','labels', 'align', 'value'], align);
setter(def, ['properties','labels', 'baseline', 'value'], 'middle');
return def;
};
axis.titleOffset = function (encoding, layout, name) {
// return specified value if specified
var value = encoding.axis(name).titleOffset;
if (value) {
return value;
}
if (value) return value;
switch (name) {
//FIXME make this adjustable
case ROW: return 0;

@@ -203,2 +239,2 @@ case COL: return 35;

return getter(layout, [name, 'axisTitleOffset']);
}
};

@@ -16,4 +16,4 @@ 'use strict';

type: 'bin',
field: encoding.field(encType, false, /*nofn*/ true),
output: encoding.field(encType),
field: encoding.fieldRef(encType, {nofn: true}),
output: encoding.fieldRef(encType),
maxbins: encoding.bin(encType).maxbins

@@ -20,0 +20,0 @@ });

@@ -53,4 +53,4 @@ 'use strict';

rawTable = filter.addFilters(rawTable, encoding); // modify rawTable
spec = compiler.time(spec, encoding); // modify rawTable, add scales
dataTable = compiler.bin(dataTable, encoding); // modify dataTable
spec = compiler.time(spec, encoding); // modify dataTable, add scales
var aggResult = compiler.aggregate(dataTable, encoding); // modify dataTable

@@ -63,3 +63,3 @@ var sorting = compiler.sort(spec.data, encoding, stats); // append new data

mark = marks[encoding.marktype()],
mdefs = marks.def(mark, encoding, layout, style),
mdefs = marks.def(mark, encoding, layout, style, stats),
mdef = mdefs[0]; // TODO: remove this dirty hack by refactoring the whole flow

@@ -90,3 +90,3 @@

// TODO: why - ?
mdef.from.transform = [{type: 'sort', by: '-' + encoding.field(f)}];
mdef.from.transform = [{type: 'sort', by: '-' + encoding.fieldRef(f)}];
}

@@ -93,0 +93,0 @@

@@ -41,3 +41,3 @@ 'use strict';

facetKeys.push(encoding.field(ROW));
facetKeys.push(encoding.fieldRef(ROW));

@@ -47,3 +47,3 @@ if (hasCol) {

from.transform = from.transform || [];
from.transform.unshift({type: 'facet', keys: [encoding.field(COL)]});
from.transform.unshift({type: 'facet', keys: [encoding.fieldRef(COL)]});
}

@@ -75,3 +75,3 @@

facetKeys.push(encoding.field(COL));
facetKeys.push(encoding.fieldRef(COL));

@@ -81,3 +81,3 @@ if (hasRow) {

from.transform = from.transform || [];
from.transform.unshift({type: 'facet', keys: [encoding.field(ROW)]});
from.transform.unshift({type: 'facet', keys: [encoding.fieldRef(ROW)]});
}

@@ -84,0 +84,0 @@

@@ -23,3 +23,3 @@ 'use strict';

// add custom filters
for (var i in filters) {
for (var i=0, l=filters.length; i<l; i++) {
var filter = filters[i];

@@ -44,3 +44,3 @@

// expects a number of fields
for (var j in operands) {
for (var j=0; j<operands.length; j++) {
condition += d + operands[j] + '!==null';

@@ -70,3 +70,3 @@ if (j < operands.length - 1) {

type: 'filter',
test: 'd.' + encoding.field(encType) + '>0'
test: 'd.' + encoding.fieldRef(encType) + '>0'
});

@@ -76,2 +76,1 @@ }

};

@@ -6,3 +6,5 @@ 'use strict';

var util = require('../util'),
setter = util.setter;
setter = util.setter,
time = require('./time'),
d3_format = require('d3-format');

@@ -43,5 +45,5 @@ module.exports = vllayout;

// for ordinal, hasCol or not doesn't matter -- we scale based on cardinality
cellWidth = (xCardinality + encoding.band(X).padding) * encoding.bandSize(X, useSmallBand);
cellWidth = (xCardinality + encoding.field(X).band.padding) * encoding.bandSize(X, useSmallBand);
} else {
cellWidth = hasCol || hasRow ? encoding.enc(COL).width : encoding.config('singleWidth');
cellWidth = hasCol || hasRow ? encoding.field(COL).width : encoding.config('singleWidth');
}

@@ -60,5 +62,5 @@ } else {

// for ordinal, hasCol or not doesn't matter -- we scale based on cardinality
cellHeight = (yCardinality + encoding.band(Y).padding) * encoding.bandSize(Y, useSmallBand);
cellHeight = (yCardinality + encoding.field(Y).band.padding) * encoding.bandSize(Y, useSmallBand);
} else {
cellHeight = hasCol || hasRow ? encoding.enc(ROW).height : encoding.config('singleHeight');
cellHeight = hasCol || hasRow ? encoding.field(ROW).height : encoding.config('singleHeight');
}

@@ -95,30 +97,49 @@ } else {

// FIXME fieldStats.max isn't always the longest
function getMaxNumberLength(encoding, et, fieldStats) {
var format = encoding.numberFormat(et, fieldStats);
return d3_format.format(format)(fieldStats.max).length;
}
function getMaxLength(encoding, stats, et) {
// FIXME determine constant for Q and T in a nicer way
if (encoding.bin(et)) {
return 5;
} else if (encoding.isType(et, Q)) {
return 10;
var field = encoding.field(et),
fieldStats = stats[field.name];
if (field.bin) {
// TODO once bin support range, need to update this
return getMaxNumberLength(encoding, et, fieldStats);
} if (encoding.isType(et, Q)) {
return getMaxNumberLength(encoding, et, fieldStats);
} else if (encoding.isType(et, T)) {
return 15;
} else if (encoding.isTypes(et, [N, O]) && encoding.axis(et).maxLabelLength) {
return Math.min(stats[encoding.fieldName(et)].max, encoding.axis(et).maxLabelLength);
return time.maxLength(encoding.field(et).timeUnit, encoding);
} else if (encoding.isTypes(et, [N, O])) {
if(fieldStats.type === 'number') {
return getMaxNumberLength(encoding, et, fieldStats);
} else {
return Math.min(fieldStats.max, encoding.axis(et).maxLabelLength || Infinity);
}
}
return stats[encoding.fieldName(et)].max;
}
function offset(encoding, stats, layout) {
[X, Y].forEach(function (x) {
var maxLength;
if (encoding.isDimension(x) || encoding.isType(x, T)) {
maxLength = getMaxLength(encoding, stats, x);
} else if (encoding.aggregate(x) === 'count') {
//assign default value for count as it won't have stats
maxLength = 3;
} else if (encoding.isType(x, Q)) {
if (x===X) {
maxLength = 3;
} else { // Y
//assume that default formating is always shorter than 7
maxLength = Math.min(getMaxLength(encoding, stats, x), 7);
[X, Y].forEach(function (et) {
// TODO(kanitw): Jul 19, 2015 - create a set of visual test for extraOffset
var extraOffset = et === X ? 20 : 22,
maxLength;
if (encoding.isDimension(et) || encoding.isType(et, T)) {
maxLength = getMaxLength(encoding, stats, et);
} else if (
// TODO once we have #512 (allow using inferred type)
// Need to adjust condition here.
encoding.isType(et, Q) ||
encoding.aggregate(et) === 'count'
) {
if (
et===Y
// || (et===X && false)
// FIXME determine when X would rotate, but should move this to axis.js first #506
) {
maxLength = getMaxLength(encoding, stats, et);
}

@@ -128,5 +149,12 @@ } else {

}
setter(layout,[x, 'axisTitleOffset'], encoding.config('characterWidth') * maxLength + 20);
if (maxLength) {
setter(layout,[et, 'axisTitleOffset'], encoding.config('characterWidth') * maxLength + extraOffset);
} else {
// if no max length (no rotation case), use maxLength = 3
setter(layout,[et, 'axisTitleOffset'], encoding.config('characterWidth') * 3 + extraOffset);
}
});
return layout;
}

@@ -13,3 +13,3 @@ 'use strict';

if (encoding.has(COLOR) && encoding.legend(COLOR)) {
if (encoding.has(COLOR) && encoding.field(COLOR).legend) {
defs.push(legend.def(COLOR, encoding, {

@@ -21,3 +21,3 @@ fill: COLOR,

if (encoding.has(SIZE) && encoding.legend(SIZE)) {
if (encoding.has(SIZE) && encoding.field(SIZE).legend) {
defs.push(legend.def(SIZE, encoding, {

@@ -29,3 +29,3 @@ size: SIZE,

if (encoding.has(SHAPE) && encoding.legend(SHAPE)) {
if (encoding.has(SHAPE) && encoding.field(SHAPE).legend) {
if (defs.length === 2) {

@@ -46,8 +46,11 @@ // TODO: fix this

legend.def = function(name, encoding, props) {
var def = props, timeUnit;
var def = props,
timeUnit = encoding.field(name).timeUnit;
def.title = encoding.fieldTitle(name);
if (encoding.isType(name, T) && (timeUnit = encoding.timeUnit(name)) &&
time.hasScale(timeUnit)) {
if (encoding.isType(name, T) &&
timeUnit &&
time.hasScale(timeUnit)
) {
var properties = def.properties = def.properties || {},

@@ -54,0 +57,0 @@ labels = properties.labels = properties.labels || {},

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

marks.def = function(mark, encoding, layout, style) {
marks.def = function(mark, encoding, layout, style, stats) {
var defs = [];

@@ -18,3 +18,3 @@

y2: {value: layout.cellHeight},
fill: {scale: COLOR, field: encoding.field(COLOR)}
fill: {scale: COLOR, field: encoding.fieldRef(COLOR)}
};

@@ -29,3 +29,3 @@ defs.push({

// add the mark def for the main thing
var p = mark.prop(encoding, layout, style);
var p = mark.prop(encoding, layout, style, stats);
defs.push({

@@ -103,3 +103,3 @@ type: mark.type,

if (e.isMeasure(X)) {
p.x = {scale: X, field: e.field(X)};
p.x = {scale: X, field: e.fieldRef(X)};
if (!e.has(Y) || e.isDimension(Y)) {

@@ -110,3 +110,3 @@ p.x2 = {value: 0};

if (e.has(X)) { // is ordinal
p.xc = {scale: X, field: e.field(X)};
p.xc = {scale: X, field: e.fieldRef(X)};
} else {

@@ -121,3 +121,3 @@ p.x = {value: 0, offset: e.config('singleBarOffset')};

if (e.has(SIZE)) {
p.width = {scale: SIZE, field: e.field(SIZE)};
p.width = {scale: SIZE, field: e.fieldRef(SIZE)};
} else {

@@ -136,7 +136,7 @@ p.width = {

if (e.isMeasure(Y)) {
p.y = {scale: Y, field: e.field(Y)};
p.y = {scale: Y, field: e.fieldRef(Y)};
p.y2 = {group: 'height'};
} else {
if (e.has(Y)) { // is ordinal
p.yc = {scale: Y, field: e.field(Y)};
p.yc = {scale: Y, field: e.fieldRef(Y)};
} else {

@@ -147,3 +147,3 @@ p.y2 = {group: 'height', offset: -e.config('singleBarOffset')};

if (e.has(SIZE)) {
p.height = {scale: SIZE, field: e.field(SIZE)};
p.height = {scale: SIZE, field: e.fieldRef(SIZE)};
} else {

@@ -161,3 +161,3 @@ p.height = {

if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.field(COLOR)};
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else {

@@ -169,3 +169,3 @@ p.fill = {value: e.value(COLOR)};

if (e.has(ALPHA)) {
p.opacity = {scale: ALPHA, field: e.field(ALPHA)};
p.opacity = {scale: ALPHA, field: e.fieldRef(ALPHA)};
} else if (e.value(ALPHA) !== undefined) {

@@ -183,3 +183,3 @@ p.opacity = {value: e.value(ALPHA)};

if (e.has(X)) {
p.x = {scale: X, field: e.field(X)};
p.x = {scale: X, field: e.fieldRef(X)};
} else if (!e.has(X)) {

@@ -191,3 +191,3 @@ p.x = {value: e.bandSize(X, layout.x.useSmallBand) / 2};

if (e.has(Y)) {
p.y = {scale: Y, field: e.field(Y)};
p.y = {scale: Y, field: e.fieldRef(Y)};
} else if (!e.has(Y)) {

@@ -199,3 +199,3 @@ p.y = {value: e.bandSize(Y, layout.y.useSmallBand) / 2};

if (e.has(SIZE)) {
p.size = {scale: SIZE, field: e.field(SIZE)};
p.size = {scale: SIZE, field: e.fieldRef(SIZE)};
} else if (!e.has(SIZE)) {

@@ -207,3 +207,3 @@ p.size = {value: e.value(SIZE)};

if (e.has(SHAPE)) {
p.shape = {scale: SHAPE, field: e.field(SHAPE)};
p.shape = {scale: SHAPE, field: e.fieldRef(SHAPE)};
} else if (!e.has(SHAPE)) {

@@ -214,6 +214,15 @@ p.shape = {value: e.value(SHAPE)};

// stroke
if (e.has(COLOR)) {
p.stroke = {scale: COLOR, field: e.field(COLOR)};
} else if (!e.has(COLOR)) {
p.stroke = {value: e.value(COLOR)};
if (e.field(SHAPE).filled) {
if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {
p.fill = {value: e.value(COLOR)};
}
} else {
if (e.has(COLOR)) {
p.stroke = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {
p.stroke = {value: e.value(COLOR)};
}
p.strokeWidth = {value: e.config('strokeWidth')};
}

@@ -223,3 +232,3 @@

if (e.has(ALPHA)) {
p.opacity = {scale: ALPHA, field: e.field(ALPHA)};
p.opacity = {scale: ALPHA, field: e.fieldRef(ALPHA)};
} else if (e.value(ALPHA) !== undefined) {

@@ -231,4 +240,2 @@ p.opacity = {value: e.value(ALPHA)};

p.strokeWidth = {value: e.config('strokeWidth')};
return p;

@@ -243,3 +250,3 @@ }

if (e.has(X)) {
p.x = {scale: X, field: e.field(X)};
p.x = {scale: X, field: e.fieldRef(X)};
} else if (!e.has(X)) {

@@ -251,3 +258,3 @@ p.x = {value: 0};

if (e.has(Y)) {
p.y = {scale: Y, field: e.field(Y)};
p.y = {scale: Y, field: e.fieldRef(Y)};
} else if (!e.has(Y)) {

@@ -259,3 +266,3 @@ p.y = {group: 'height'};

if (e.has(COLOR)) {
p.stroke = {scale: COLOR, field: e.field(COLOR)};
p.stroke = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {

@@ -267,3 +274,3 @@ p.stroke = {value: e.value(COLOR)};

if (e.has(ALPHA)) {
p.opacity = {scale: ALPHA, field: e.field(ALPHA)};
p.opacity = {scale: ALPHA, field: e.fieldRef(ALPHA)};
} else if (e.value(ALPHA) !== undefined) {

@@ -284,3 +291,3 @@ p.opacity = {value: e.value(ALPHA)};

if (e.isMeasure(X)) {
p.x = {scale: X, field: e.field(X)};
p.x = {scale: X, field: e.fieldRef(X)};
if (e.isDimension(Y)) {

@@ -291,3 +298,3 @@ p.x2 = {scale: X, value: 0};

} else if (e.has(X)) {
p.x = {scale: X, field: e.field(X)};
p.x = {scale: X, field: e.fieldRef(X)};
} else {

@@ -299,6 +306,6 @@ p.x = {value: 0};

if (e.isMeasure(Y)) {
p.y = {scale: Y, field: e.field(Y)};
p.y = {scale: Y, field: e.fieldRef(Y)};
p.y2 = {scale: Y, value: 0};
} else if (e.has(Y)) {
p.y = {scale: Y, field: e.field(Y)};
p.y = {scale: Y, field: e.fieldRef(Y)};
} else {

@@ -308,5 +315,5 @@ p.y = {group: 'height'};

// stroke
// fill
if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.field(COLOR)};
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {

@@ -318,3 +325,3 @@ p.fill = {value: e.value(COLOR)};

if (e.has(ALPHA)) {
p.opacity = {scale: ALPHA, field: e.field(ALPHA)};
p.opacity = {scale: ALPHA, field: e.fieldRef(ALPHA)};
} else if (e.value(ALPHA) !== undefined) {

@@ -332,3 +339,3 @@ p.opacity = {value: e.value(ALPHA)};

if (e.has(X)) {
p.x = {scale: X, field: e.field(X)};
p.x = {scale: X, field: e.fieldRef(X)};
if (e.isDimension(X)) {

@@ -343,3 +350,3 @@ p.x.offset = -e.bandSize(X, layout.x.useSmallBand) / 3;

if (e.has(Y)) {
p.y = {scale: Y, field: e.field(Y)};
p.y = {scale: Y, field: e.fieldRef(Y)};
if (e.isDimension(Y)) {

@@ -368,3 +375,3 @@ p.y.offset = -e.bandSize(Y, layout.y.useSmallBand) / 3;

if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.field(COLOR)};
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else {

@@ -376,3 +383,3 @@ p.fill = {value: e.value(COLOR)};

if (e.has(ALPHA)) {
p.opacity = {scale: ALPHA, field: e.field(ALPHA)};
p.opacity = {scale: ALPHA, field: e.fieldRef(ALPHA)};
} else if (e.value(ALPHA) !== undefined) {

@@ -393,3 +400,3 @@ p.opacity = {value: e.value(ALPHA)};

if (e.has(X)) {
p.x = {scale: X, field: e.field(X)};
p.x = {scale: X, field: e.fieldRef(X)};
} else if (!e.has(X)) {

@@ -401,3 +408,3 @@ p.x = {value: e.bandSize(X, layout.x.useSmallBand) / 2};

if (e.has(Y)) {
p.y = {scale: Y, field: e.field(Y)};
p.y = {scale: Y, field: e.fieldRef(Y)};
} else if (!e.has(Y)) {

@@ -409,3 +416,3 @@ p.y = {value: e.bandSize(Y, layout.y.useSmallBand) / 2};

if (e.has(SIZE)) {
p.size = {scale: SIZE, field: e.field(SIZE)};
p.size = {scale: SIZE, field: e.fieldRef(SIZE)};
} else if (!e.has(X)) {

@@ -420,3 +427,3 @@ p.size = {value: e.value(SIZE)};

if (e.has(COLOR)) {
p.fill = {scale: COLOR, field: e.field(COLOR)};
p.fill = {scale: COLOR, field: e.fieldRef(COLOR)};
} else if (!e.has(COLOR)) {

@@ -428,3 +435,3 @@ p.fill = {value: e.value(COLOR)};

if (e.has(ALPHA)) {
p.opacity = {scale: ALPHA, field: e.field(ALPHA)};
p.opacity = {scale: ALPHA, field: e.fieldRef(ALPHA)};
} else if (e.value(ALPHA) !== undefined) {

@@ -440,8 +447,9 @@ p.opacity = {value: e.value(ALPHA)};

function text_props(e, layout, style) {
var p = {};
function text_props(e, layout, style, stats) {
var p = {},
field = e.field(TEXT);
// x
if (e.has(X)) {
p.x = {scale: X, field: e.field(X)};
p.x = {scale: X, field: e.fieldRef(X)};
} else if (!e.has(X)) {

@@ -457,3 +465,3 @@ if (e.has(TEXT) && e.isType(TEXT, Q)) {

if (e.has(Y)) {
p.y = {scale: Y, field: e.field(Y)};
p.y = {scale: Y, field: e.fieldRef(Y)};
} else if (!e.has(Y)) {

@@ -465,5 +473,5 @@ p.y = {value: e.bandSize(Y, layout.y.useSmallBand) / 2};

if (e.has(SIZE)) {
p.fontSize = {scale: SIZE, field: e.field(SIZE)};
p.fontSize = {scale: SIZE, field: e.fieldRef(SIZE)};
} else if (!e.has(SIZE)) {
p.fontSize = {value: e.font('size')};
p.fontSize = {value: field.font.size};
}

@@ -473,7 +481,7 @@

// color should be set to background
p.fill = {value: 'black'};
p.fill = {value: field.text.color};
// alpha
if (e.has(ALPHA)) {
p.opacity = {scale: ALPHA, field: e.field(ALPHA)};
p.opacity = {scale: ALPHA, field: e.fieldRef(ALPHA)};
} else if (e.value(ALPHA) !== undefined) {

@@ -488,17 +496,21 @@ p.opacity = {value: e.value(ALPHA)};

if (e.isType(TEXT, Q)) {
p.text = {template: '{{' + e.field(TEXT) + ' | number:\'.3s\'}}'};
p.align = {value: 'right'};
var fieldStats = stats[e.fieldName(name)],
numberFormat = field.format || e.numberFormat(fieldStats);
p.text = {template: '{{' + e.fieldRef(TEXT) + ' | number:\'' +
numberFormat +'\'}}'};
p.align = {value: field.align};
} else {
p.text = {field: e.field(TEXT)};
p.text = {field: e.fieldRef(TEXT)};
}
} else {
p.text = {value: 'Abc'};
p.text = {value: field.placeholder};
}
p.font = {value: e.font('family')};
p.fontWeight = {value: e.font('weight')};
p.fontStyle = {value: e.font('style')};
p.baseline = {value: e.text('baseline')};
p.font = {value: field.font.family};
p.fontWeight = {value: field.font.weight};
p.fontStyle = {value: field.font.style};
p.baseline = {value: field.baseline};
return p;
}

@@ -6,3 +6,4 @@ 'use strict';

colorbrewer = require('colorbrewer'),
interpolateLab = require('d3-color').interpolateLab;
interpolateLab = require('d3-color').interpolateLab,
schema = require('../schema/schema');

@@ -25,10 +26,9 @@ var scale = module.exports = {};

type: scale.type(name, encoding),
domain: scale.domain(name, encoding, sorting, opt)
domain: scale.domain(name, encoding, stats, sorting, opt)
};
if (s.type === 'ordinal' && !encoding.bin(name) && encoding.sort(name).length === 0) {
s.sort = true;
}
scale_range(s, encoding, layout, stats, style, opt);
s.sort = scale.sort(s, encoding, name) || undefined;
scale.range(s, encoding, layout, stats, opt);
return (a.push(s), a);

@@ -38,2 +38,9 @@ }, []);

scale.sort = function(s, encoding, name) {
return s.type === 'ordinal' && (
!!encoding.bin(name) ||
encoding.sort(name).length === 0
);
};
scale.type = function(name, encoding) {

@@ -45,4 +52,4 @@

case T:
var timeUnit = encoding.timeUnit(name);
return (timeUnit && time.scale.type(timeUnit, name)) || 'time';
var timeUnit = encoding.field(name).timeUnit;
return timeUnit ? time.scale.type(timeUnit, name) : 'time';
case Q:

@@ -56,8 +63,20 @@ if (encoding.bin(name)) {

scale.domain = function (name, encoding, sorting, opt) {
scale.domain = function (name, encoding, stats, sorting, opt) {
var field = encoding.field(name);
if (encoding.isType(name, T)) {
var range = time.scale.domain(encoding.timeUnit(name), name);
var range = time.scale.domain(field.timeUnit, name);
if(range) return range;
}
if (field.bin) {
// TODO(kanitw): this must be changed in vg2
var fieldStat = stats[field.name],
bins = util.getbins(fieldStat, field.bin.maxbins || schema.MAXBINS_DEFAULT),
numbins = (bins.stop - bins.start) / bins.step;
return util.range(numbins).map(function(i) {
return bins.start + bins.step * i;
});
}
if (name == opt.stack) {

@@ -72,17 +91,35 @@ return {

}
return {data: sorting.getDataset(name), field: encoding.field(name)};
var aggregate = encoding.aggregate(name),
timeUnit = field.timeUnit,
scaleUseRawDomain = encoding.scale(name).useRawDomain,
useRawDomain = scaleUseRawDomain !== undefined ?
scaleUseRawDomain : encoding.config('useRawDomain'),
notCountOrSum = !aggregate || (aggregate !=='count' && aggregate !== 'sum');
if ( useRawDomain && notCountOrSum && (
// Q always uses non-ordinal scale except when it's binned and thus uses ordinal scale.
(encoding.isType(name, Q) && !field.bin) ||
// T uses non-ordinal scale when there's no unit or when the unit is not ordinal.
(encoding.isType(name, T) && (!timeUnit || !time.isOrdinalFn(timeUnit)))
)
) {
return {data: RAW, field: encoding.fieldRef(name, {nofn: !timeUnit})};
}
return {data: sorting.getDataset(name), field: encoding.fieldRef(name)};
};
function scale_range(s, encoding, layout, stats, style, opt) {
// jshint unused:false
var spec = encoding.scale(s.name);
scale.range = function (s, encoding, layout, stats) {
var spec = encoding.scale(s.name),
field = encoding.field(s.name),
timeUnit = field.timeUnit;
switch (s.name) {
case X:
s.range = layout.cellWidth ? [0, layout.cellWidth] : 'width';
if (s.type === 'ordinal') {
s.bandWidth = encoding.bandSize(X, layout.x.useSmallBand);
} else {
s.range = layout.cellWidth ? [0, layout.cellWidth] : 'width';
if (encoding.isType(s.name,T) && encoding.timeUnit(s.name) === 'year') {
if (encoding.isType(s.name,T) && timeUnit === 'year') {
s.zero = false;

@@ -97,3 +134,3 @@ } else {

if (s.type === 'time') {
s.nice = encoding.timeUnit(s.name);
s.nice = timeUnit || encoding.config('timeScaleNice');
}else {

@@ -105,7 +142,9 @@ s.nice = true;

if (s.type === 'ordinal') {
s.range = layout.cellHeight ?
(field.bin ? [layout.cellHeight, 0] : [0, layout.cellHeight]) :
'height';
s.bandWidth = encoding.bandSize(Y, layout.y.useSmallBand);
} else {
s.range = layout.cellHeight ? [layout.cellHeight, 0] : 'height';
if (encoding.isType(s.name,T) && encoding.timeUnit(s.name) === 'year') {
if (encoding.isType(s.name,T) && timeUnit === 'year') {
s.zero = false;

@@ -122,3 +161,3 @@ } else {

if (s.type === 'time') {
s.nice = encoding.timeUnit(s.name) || encoding.config('timeScaleNice');
s.nice = timeUnit || encoding.config('timeScaleNice');
}else {

@@ -175,9 +214,10 @@ s.nice = true;

s.points = true;
s.padding = encoding.band(s.name).padding;
s.padding = encoding.field(s.name).band.padding;
}
}
}
};
scale.color = function(s, encoding, stats) {
var range = encoding.scale(COLOR).range,
var colorScale = encoding.scale(COLOR),
range = colorScale.range,
cardinality = encoding.cardinality(COLOR, stats),

@@ -187,3 +227,3 @@ type = encoding.type(COLOR);

if (range === undefined) {
var ordinalPalette = encoding.config('ordinalPalette');
var ordinalPalette = colorScale.ordinalPalette;
if (s.type === 'ordinal') {

@@ -193,5 +233,5 @@ if (type === N) {

if (cardinality <= 10) {
range = encoding.config('c10palette');
range = colorScale.c10palette;
} else {
range = encoding.config('c20palette');
range = colorScale.c20palette;
}

@@ -258,2 +298,1 @@ } else {

};

@@ -37,4 +37,4 @@ 'use strict';

type: 'aggregate',
groupby: [encoding.field(dim)].concat(facets), // dim and other facets
fields: [{op: 'sum', field: encoding.field(val)}] // TODO check if field with aggregate is correct?
groupby: [encoding.fieldRef(dim)].concat(facets), // dim and other facets
fields: [{op: 'sum', field: encoding.fieldRef(val)}] // TODO check if field with aggregate is correct?
}]

@@ -59,4 +59,4 @@ };

type: 'stack',
point: encoding.field(dim),
height: encoding.field(val),
point: encoding.fieldRef(dim),
height: encoding.fieldRef(val),
output: {y1: val, y0: val + '2'}

@@ -63,0 +63,0 @@ }];

@@ -22,4 +22,4 @@ 'use strict';

if (stack && encoding.has(COLOR)) {
trans.unshift({type: 'sort', by: encoding.field(COLOR)});
trans.unshift({type: 'sort', by: encoding.fieldRef(COLOR)});
}
}
'use strict';
var util = require('../util');
var util = require('../util'),
d3_time_format = require('d3-time-format');
module.exports = time;
function time(spec, encoding, opt) { // FIXME refactor to reduce side effect #276
var LONG_DATE = new Date(2014, 8, 17);
function time(spec, encoding) { // FIXME refactor to reduce side effect #276
// jshint unused:false

@@ -14,3 +17,3 @@ var timeFields = {}, timeUnits = {};

if (field.type === T && field.timeUnit) {
timeFields[encoding.field(encType)] = {
timeFields[encoding.fieldRef(encType)] = {
field: field,

@@ -24,3 +27,3 @@ encType: encType

// add formula transform
var data = spec.data[1],
var data = spec.data[0],
transform = data.transform = data.transform || [];

@@ -36,3 +39,4 @@

for (var timeUnit in timeUnits) {
time.scale(scales, timeUnit, encoding);
var scale = time.scale.def(timeUnit, encoding);
if (scale) scales.push(scale);
}

@@ -42,4 +46,2 @@ return spec;

time.cardinality = function(field, stats, filterNull, type) {

@@ -67,2 +69,25 @@ var timeUnit = field.timeUnit;

time.maxLength = function(timeUnit, encoding) {
switch (timeUnit) {
case 'seconds':
case 'minutes':
case 'hours':
case 'date':
return 2;
case 'month':
case 'day':
var range = time.range(timeUnit, encoding);
if (range) {
// return the longest name in the range
return Math.max.apply(null, range.map(function(r) {return r.length;}));
}
return 2;
case 'year':
return 4; //'1998'
}
// no time unit
var timeFormat = encoding.config('timeFormat');
return d3_time_format.utcFormat(timeFormat)(LONG_DATE).length;
};
function fieldFn(func, field) {

@@ -83,3 +108,3 @@ return 'utc' + func + '(d.data.'+ field.name +')';

type: 'formula',
field: encoding.field(encType),
field: encoding.fieldRef(encType),
expr: time.formula(field)

@@ -89,30 +114,39 @@ });

/** append custom time scales for axis label */
time.scale = function(scales, timeUnit, encoding) {
var labelLength = encoding.config('timeScaleLabelLength');
// TODO add option for shorter scale / custom range
time.range = function(timeUnit, encoding) {
var labelLength = encoding.config('timeScaleLabelLength'),
scaleLabel;
switch (timeUnit) {
case 'day':
scales.push({
name: 'time-'+timeUnit,
type: 'ordinal',
domain: util.range(0, 7),
range: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].map(
function(s) { return s.substr(0, labelLength);}
)
});
scaleLabel = encoding.config('dayScaleLabel');
break;
case 'month':
scales.push({
name: 'time-'+timeUnit,
type: 'ordinal',
domain: util.range(0, 12),
range: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'].map(
function(s) { return s.substr(0, labelLength);}
)
});
scaleLabel = encoding.config('monthScaleLabel');
break;
}
if (scaleLabel) {
return labelLength ? scaleLabel.map(
function(s) { return s.substr(0, labelLength);}
) : scaleLabel;
}
return;
};
time.scale = {};
/** append custom time scales for axis label */
time.scale.def = function(timeUnit, encoding) {
var range = time.range(timeUnit, encoding);
if (range) {
return {
name: 'time-'+timeUnit,
type: 'ordinal',
domain: time.scale.domain(timeUnit),
range: range
};
}
return null;
};
time.isOrdinalFn = function(timeUnit) {

@@ -133,3 +167,3 @@ switch (timeUnit) {

if (name === COLOR) {
return 'linear'; // this has order
return 'linear'; // time has order, so use interpolated ordinal color scale.
}

@@ -162,3 +196,1 @@

};

@@ -24,3 +24,8 @@ 'use strict';

return s;
}, {count: data.length});
}, {
'*': {
max: data.length,
min: 0
}
});
};

@@ -98,3 +98,3 @@ 'use strict';

proto.enc = function(et) {
proto.field = function(et) {
return this._enc[et];

@@ -125,11 +125,3 @@ };

// get "field" property for vega
proto.field = function(et, nodata, nofn) {
if (!this.has(et)) return null;
return vlfield.fieldRef(this._enc[et], {
nofn: nofn,
data: !this._vega2 && !nodata
});
};
// get "field" reference for vega
proto.fieldRef = function(et, opt) {

@@ -156,5 +148,5 @@ opt = opt || {};

}
var timeUnit = this._enc[et].aggregate || this._enc[et].timeUnit || (this._enc[et].bin && 'bin');
if (timeUnit) {
return timeUnit.toUpperCase() + '(' + this._enc[et].name + ')';
var fn = this._enc[et].aggregate || this._enc[et].timeUnit || (this._enc[et].bin && 'bin');
if (fn) {
return fn.toUpperCase() + '(' + this._enc[et].name + ')';
} else {

@@ -173,6 +165,2 @@ return this._enc[et].name;

proto.band = function(et) {
return this._enc[et].band || {};
};
proto.bandSize = function(encType, useSmallBand) {

@@ -185,3 +173,3 @@ useSmallBand = useSmallBand ||

// if band.size is explicitly specified, follow the specification, otherwise draw value from config.
return this.band(encType).size ||
return this.field(encType).band.size ||
this.config(useSmallBand ? 'smallBandSize' : 'largeBandSize');

@@ -206,6 +194,2 @@ };

proto.legend = function(et) {
return this._enc[et].legend;
};
proto.value = function(et) {

@@ -215,4 +199,6 @@ return this._enc[et].value;

proto.timeUnit = function(et) {
return this._enc[et].timeUnit;
proto.numberFormat = function(fieldStats) {
var formatConfig = fieldStats.max > this.config('maxSmallNumber') ?
'largeNumberFormat': 'smallNumberFormat';
return this.config(formatConfig);
};

@@ -245,6 +231,2 @@

proto.length = function() {
return util.keys(this._enc).length;
};
proto.map = function(f) {

@@ -266,18 +248,4 @@ return vlenc.map(this._enc, f);

proto.role = function(et) {
return this.has(et) ? vlfield.role(this._enc[et]) : null;
};
proto.text = function(prop) {
var text = this._enc[TEXT].text;
return prop ? text[prop] : text;
};
proto.font = function(prop) {
var font = this._enc[TEXT].font;
return prop ? font[prop] : font;
};
proto.isType = function(et, type) {
var field = this.enc(et);
var field = this.field(et);
return field && vlfield.isType(field, type);

@@ -287,3 +255,3 @@ };

proto.isTypes = function(et, type) {
var field = this.enc(et);
var field = this.field(et);
return field && vlfield.isTypes(field, type);

@@ -293,11 +261,11 @@ };

Encoding.isOrdinalScale = function(encoding, encType) {
return vlfield.isOrdinalScale(encoding.enc(encType));
return vlfield.isOrdinalScale(encoding.field(encType));
};
Encoding.isDimension = function(encoding, encType) {
return vlfield.isDimension(encoding.enc(encType));
return vlfield.isDimension(encoding.field(encType));
};
Encoding.isMeasure = function(encoding, encType) {
return vlfield.isMeasure(encoding.enc(encType));
return vlfield.isMeasure(encoding.field(encType));
};

@@ -342,3 +310,3 @@

proto.cardinality = function(encType, stats) {
return vlfield.cardinality(this.enc(encType), stats, this.config('filterNull'));
return vlfield.cardinality(this.field(encType), stats, this.config('filterNull'));
};

@@ -345,0 +313,0 @@

@@ -97,35 +97,2 @@ 'use strict';

var typeOrder = {
N: 0,
O: 1,
G: 2,
T: 3,
Q: 4
};
vlfield.order = {};
vlfield.order.type = function(field) {
if (field.aggregate==='count') return 4;
return typeOrder[field.type];
};
vlfield.order.typeThenName = function(field) {
return vlfield.order.type(field) + '_' +
(field.aggregate === 'count' ? '~' : field.name.toLowerCase());
// ~ is the last character in ASCII
};
vlfield.order.original = function() {
return 0; // no swap will occur
};
vlfield.order.name = function(field) {
return field.name;
};
vlfield.order.typeThenCardinality = function(field, stats){
return stats[field.name].distinct;
};
var isType = vlfield.isType = function (fieldDef, type) {

@@ -169,6 +136,2 @@ return fieldDef.type === type;

vlfield.role = function(field) {
return isDimension(field) ? 'dimension' : 'measure';
};
vlfield.count = function() {

@@ -175,0 +138,0 @@ return {name:'*', aggregate: 'count', type: Q, displayName: vlfield.count.displayName};

@@ -30,16 +30,2 @@ // Package of defining Vega-lite Specification's json schema

};
schema.band = {
type: 'object',
properties: {
size: {
type: 'integer',
minimum: 0
},
padding: {
type: 'integer',
minimum: 0,
default: 1
}
}
};

@@ -60,6 +46,5 @@ schema.getSupportedRole = function(encType) {

//TODO(kanitw): add other type of function here
schema.scale_type = {
type: 'string',
// TODO(kanitw) read vega's schema here, add description
enum: ['linear', 'log', 'pow', 'sqrt', 'quantile'],

@@ -91,3 +76,4 @@ default: 'linear',

default: schema.MAXBINS_DEFAULT,
minimum: 2
minimum: 2,
description: 'Maximum number of bins.'
}

@@ -127,2 +113,11 @@ },

supportedTypes: toMap([T])
},
useRawDomain: {
type: 'boolean',
default: undefined,
description: 'Use the raw data range as scale domain instead of ' +
'aggregated data for aggregate axis. ' +
'This option does not work with sum or count aggregate' +
'as they might have a substantially larger scale range.' +
'By default, use value from config.useRawDomain.'
}

@@ -166,7 +161,28 @@ }

},
layer: {
type: 'string',
default: 'back',
description: 'A string indicating if the axis (and any gridlines) should be placed above or below the data marks.'
},
orient: {
type: 'string',
default: undefined,
enum: ['top', 'right', 'left', 'bottom'],
description: 'The orientation of the axis. One of top, bottom, left or right. The orientation can be used to further specialize the axis type (e.g., a y axis oriented for the right edge of the chart).'
},
ticks :{
type: 'integer',
default: 5,
description: 'A desired number of ticks, for axes visualizing quantitative scales. The resulting number may be different so that values are "nice" (multiples of 2, 5, 10) and lie within the underlying scale\'s range.'
},
title: {
type: 'boolean',
default: true,
description: 'A title for the axis.'
type: 'string',
default: undefined,
description: 'A title for the axis. (Shows field name and its function by default.)'
},
titleMaxLength: {
type: 'integer',
default: undefined,
description: 'Max length for axis title if the title is automatically generated from the field\'s description'
},
titleOffset: {

@@ -180,3 +196,6 @@ type: 'integer',

default: undefined, // auto
description: 'The formatting pattern for axis labels.'
description: 'The formatting pattern for axis labels. '+
'If not undefined, this will be determined by ' +
'small/largeNumberFormat and the max value ' +
'of the field.'
},

@@ -204,12 +223,14 @@ maxLabelLength: {

required: ['name', 'aggregate'],
name: {
type: 'string'
},
aggregate: {
type: 'string',
enum: ['avg', 'sum', 'min', 'max', 'count']
},
reverse: {
type: 'boolean',
default: false
properties: {
name: {
type: 'string'
},
aggregate: {
type: 'string',
enum: ['avg', 'sum', 'min', 'max', 'count']
},
reverse: {
type: 'boolean',
default: false
}
}

@@ -224,3 +245,17 @@ }

properties: {
band: schema.band
band: {
type: 'object',
properties: {
size: {
type: 'integer',
minimum: 0,
default: undefined
},
padding: {
type: 'integer',
minimum: 0,
default: 1
}
}
}
}

@@ -243,20 +278,24 @@ };

properties: {
text: {
type: 'object',
properties: {
align: {
type: 'string',
default: 'left'
},
baseline: {
type: 'string',
default: 'middle'
},
margin: {
type: 'integer',
default: 4,
minimum: 0
}
}
align: {
type: 'string',
default: 'right'
},
baseline: {
type: 'string',
default: 'middle'
},
color: {
type: 'string',
role: 'color',
default: '#000000'
},
margin: {
type: 'integer',
default: 4,
minimum: 0
},
placeholder: {
type: 'string',
default: 'Abc'
},
font: {

@@ -285,3 +324,11 @@ type: 'object',

}
}
},
format: {
type: 'string',
default: undefined, // auto
description: 'The formatting pattern for text value. '+
'If not undefined, this will be determined by ' +
'small/largeNumberFormat and the max value ' +
'of the field.'
},
}

@@ -315,3 +362,27 @@ };

range: {
type: ['string', 'array']
type: ['string', 'array'],
default: undefined,
description:
'color palette, if undefined vega-lite will use data property' +
'to pick one from c10palette, c20palette, or ordinalPalette'
},
c10palette: {
type: 'string',
default: 'category10',
enum: [
// Tableau
'category10', 'category10k',
// Color Brewer
'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3'
]
},
c20palette: {
type: 'string',
default: 'category20',
enum: ['category20', 'category20b', 'category20c']
},
ordinalPalette: {
type: 'string',
default: 'BuGn',
enum: util.keys(colorbrewer)
}

@@ -344,2 +415,7 @@ }

default: 'circle'
},
filled: {
type: 'boolean',
default: false,
description: 'whether the shape\'s color should be used as fill color instead of stroke color'
}

@@ -360,8 +436,3 @@ }

default: 150
},
grid: {
type: 'boolean',
default: true,
description: 'A flag indicate if gridlines should be created in addition to ticks.'
},
}
}

@@ -601,25 +672,2 @@ };

},
// color
c10palette: {
type: 'string',
default: 'category10',
enum: [
// Tableau
'category10', 'category10k',
// Color Brewer
'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3'
]
},
c20palette: {
type: 'string',
default: 'category20',
enum: ['category20', 'category20b', 'category20c']
},
ordinalPalette: {
type: 'string',
default: 'BuGn',
enum: util.keys(colorbrewer)
},
// scales

@@ -629,4 +677,22 @@ timeScaleLabelLength: {

default: 3,
minimum: 0
minimum: 0,
description: 'Max length for values in dayScaleLabel and monthScaleLabel. Zero means using full names in dayScaleLabel/monthScaleLabel.'
},
dayScaleLabel: {
type: 'array',
items: {
type: 'string'
},
default: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
description: 'Axis labels for day of week, starting from Sunday.' +
'(Consistent with Javascript -- See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDay.'
},
monthScaleLabel: {
type: 'array',
items: {
type: 'string'
},
default: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
description: 'Axis labels for month.'
},
// other

@@ -636,2 +702,34 @@ characterWidth: {

default: 6
},
maxSmallNumber: {
type: 'number',
default: 10000,
description: 'maximum number that a field will be considered smallNumber.'+
'Used for axis labelling.'
},
smallNumberFormat: {
type: 'string',
default: '',
description: 'D3 Number format for axis labels and text tables '+
'for number <= maxSmallNumber. Used for axis labelling.'
},
largeNumberFormat: {
type: 'string',
default: '.3s',
description: 'D3 Number format for axis labels and text tables ' +
'for number > maxSmallNumber.'
},
timeFormat: {
type: 'string',
default: '%Y-%m-%d',
description: 'Date format for axis labels.'
},
useRawDomain: {
type: 'boolean',
default: false,
description: 'Use the raw data range as scale domain instead of ' +
'aggregated data for aggregate axis. ' +
'This option does not work with sum or count aggregate' +
'as they might have a substantially larger scale range.' +
'By default, use value from config.useRawDomain.'
}

@@ -638,0 +736,0 @@ }

@@ -9,2 +9,8 @@ 'use strict';

describe('Axis', function() {
var stats = {a: {distinct: 5}, b: {distinct: 32}},
layout = {
cellWidth: 60, // default characterWidth = 6
cellHeight: 60
};
describe('(X) for Time Data', function() {

@@ -29,3 +35,3 @@ var fieldName = 'a',

}
});
}, stats);

@@ -41,2 +47,132 @@ //FIXME decouple the test here

});
describe('grid()', function () {
// FIXME(kanitw): Jul 19, 2015 - write test
});
describe('hideTicks()', function () {
var def = axis.hideTicks({properties:{}});
it('should adjust ticks', function () {
expect(def.properties.ticks).to.eql({opacity: {value: 0}});
});
it('should adjust majorTicks', function () {
expect(def.properties.majorTicks).to.eql({opacity: {value: 0}});
});
it('should adjust axis', function () {
expect(def.properties.axis).to.eql({opacity: {value: 0}});
});
});
describe('labels.scale()', function () {
// FIXME(kanitw): Jul 19, 2015 - write test
});
describe('labels.format()', function () {
// FIXME(kanitw): Jul 19, 2015 - write test
});
describe('labels.rotate()', function () {
// FIXME(kanitw): Jul 19, 2015 - write test
});
describe('orient()', function () {
it('should return specified orient', function () {
var orient = axis.orient('x', Encoding.fromSpec({
encoding: {
x: {name: 'a', axis:{orient: 'bottom'}}
}
}), stats);
expect(orient).to.eql('bottom');
});
it('should return undefined by default', function () {
var orient = axis.orient('x', Encoding.fromSpec({
encoding: {
x: {name: 'a'}
}
}), stats);
expect(orient).to.eql(undefined);
});
it('should return top for COL', function () {
var orient = axis.orient('col', Encoding.fromSpec({
encoding: {
x: {name: 'a'},
col: {name: 'a'}
}
}), stats);
expect(orient).to.eql('top');
});
it('should return top for X with high cardinality, ordinal Y', function () {
var orient = axis.orient('x', Encoding.fromSpec({
encoding: {
x: {name: 'a'},
y: {name: 'b', type: 'O'}
}
}), stats);
expect(orient).to.eql('top');
});
});
describe('title()', function () {
it('should add explicitly specified title', function () {
var def = axis.title({}, 'x', Encoding.fromSpec({
encoding: {
x: {name: 'a', axis: {title: 'Custom'}}
}
}), stats, layout);
expect(def.title).to.eql('Custom');
});
it('should add return fieldTitle by default', function () {
var encoding = Encoding.fromSpec({
encoding: {
x: {name: 'a', axis: {titleMaxLength: '3'}}
}
});
var def = axis.title({}, 'x', encoding, layout);
expect(def.title).to.eql('a');
});
it('should add return fieldTitle by default', function () {
var encoding = Encoding.fromSpec({
encoding: {
x: {name: 'a', aggregate: 'sum', axis: {titleMaxLength: '10'}}
}
});
var def = axis.title({}, 'x', encoding, layout);
expect(def.title).to.eql('SUM(a)');
});
it('should add return fieldTitle by default and truncate', function () {
var encoding = Encoding.fromSpec({
encoding: {
x: {name: 'a', aggregate: 'sum', axis: {titleMaxLength: '3'}}
}
});
var def = axis.title({}, 'x', encoding, layout);
expect(def.title).to.eql('SU…');
});
it('should add return fieldTitle by default and truncate', function () {
var encoding = Encoding.fromSpec({
encoding: {
x: {name: 'abcdefghijkl'}
}
});
var def = axis.title({}, 'x', encoding, layout);
expect(def.title).to.eql('abcdefghi…');
});
});
describe('titleOffset()', function () {
// FIXME(kanitw): Jul 19, 2015 - write test
});
});

@@ -11,101 +11,236 @@ 'use strict';

describe('vl.compile.scale.domain', function() {
it('should return correct stack', function() {
var scale = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
name: 'origin'
}
}
}), {}, {
stack: 'y',
facet: true
describe('vl.compile.scale', function() {
describe('sort()', function() {
it('should return true for any ordinal or binned field', function() {
var encoding = Encoding.fromSpec({
encoding: {
x: { name: 'origin', type: O},
y: { bin: true, name: 'origin', type: Q}
}
});
expect(vlscale.sort({type: 'ordinal'}, encoding, 'x'))
.to.eql(true);
expect(vlscale.sort({type: 'ordinal'}, encoding, 'y'))
.to.eql(true);
});
expect(scale).to.eql({
data: 'stacked',
field: 'data.max_sum_origin'
});
});
it('should return correct aggregated stack', function() {
var scale = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
aggregate: 'sum',
name: 'origin'
describe('domain()', function() {
var sortingReturn = 'sorted',
sorting = {
getDataset: function() {return 'sorted';}
};
it('should return correct stack', function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
name: 'origin'
}
}
}
}), {}, {
stack: 'y',
facet: true
}), {}, {}, {
stack: 'y',
facet: true
});
expect(domain).to.eql({
data: 'stacked',
field: 'data.max_sum_origin'
});
});
expect(scale).to.eql({
data: 'stacked',
field: 'data.max_sum_sum_origin'
it('should return correct aggregated stack', function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
aggregate: 'sum',
name: 'origin'
}
}
}), {}, {}, {
stack: 'y',
facet: true
});
expect(domain).to.eql({
data: 'stacked',
field: 'data.max_sum_sum_origin'
});
});
});
// TODO test other cases
});
it('should return the right domain if binned Q',
function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
bin: {maxbins: 15},
name: 'origin',
scale: {useRawDomain: true},
type: Q
}
}
}), {origin: {min: -5, max:48}}, sorting, {});
describe('vl.compile.scale.color.palette', function() {
it('should return tableau categories', function() {
expect(vlscale.color.palette('category10k')).to.eql(
['#2ca02c', '#e377c2', '#7f7f7f', '#17becf', '#8c564b', '#d62728', '#bcbd22',
'#9467bd', '#ff7f0e', '#1f77b4'
]
);
});
expect(domain).to.eql([-5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45]);
});
it('should return pre-defined brewer palette if low cardinality', function() {
var brewerPalettes = util.keys(colorbrewer);
brewerPalettes.forEach(function(palette) {
util.range(3, 9).forEach(function(cardinality) {
expect(vlscale.color.palette(palette, cardinality)).to.eql(
colorbrewer[palette][cardinality]
);
it('should return the raw domain if useRawDomain is true for non-bin, non-sum Q',
function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
aggregate: 'mean',
name: 'origin',
scale: {useRawDomain: true},
type: Q
}
}
}), {}, {}, {});
expect(domain.data).to.eql(RAW);
});
it('should return the aggregate domain for sum Q',
function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
aggregate: 'sum',
name: 'origin',
scale: {useRawDomain: true},
type: Q
}
}
}), {}, sorting, {});
expect(domain.data).to.eql(sortingReturn);
});
it('should return the raw domain if useRawDomain is true for raw T',
function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
name: 'origin',
scale: {useRawDomain: true},
type: T
}
}
}), {}, {}, {});
expect(domain.data).to.eql(RAW);
});
it('should return the raw domain if useRawDomain is true for year T',
function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
name: 'origin',
scale: {useRawDomain: true},
type: T,
timeUnit: 'year'
}
}
}), {}, {}, {});
expect(domain.data).to.eql(RAW);
expect(domain.field.indexOf('year')).to.gt(-1);
});
it('should return the correct domain for month T',
function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
name: 'origin',
scale: {useRawDomain: true},
type: T,
timeUnit: 'month'
}
}
}), {}, sorting, {});
expect(domain).to.eql([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
});
it('should return the aggregated domain if useRawDomain is false', function() {
var domain = vlscale.domain('y', Encoding.fromSpec({
encoding: {
y: {
aggregate: 'min',
name: 'origin',
scale: {useRawDomain: false},
type: Q
}
}
}), {}, sorting, {});
expect(domain.data).to.eql(sortingReturn);
});
// TODO test other cases
});
it('should return pre-defined brewer palette if high cardinality N', function() {
var brewerPalettes = util.keys(colorbrewer);
brewerPalettes.forEach(function(palette) {
var cardinality = 20;
expect(vlscale.color.palette(palette, cardinality, 'N')).to.eql(
colorbrewer[palette][Math.max.apply(null, util.keys(colorbrewer[palette]))]
describe('color.palette', function() {
it('should return tableau categories', function() {
expect(vlscale.color.palette('category10k')).to.eql(
['#2ca02c', '#e377c2', '#7f7f7f', '#17becf', '#8c564b', '#d62728', '#bcbd22',
'#9467bd', '#ff7f0e', '#1f77b4'
]
);
});
});
it('should return interpolated scale if high cardinality ordinal', function() {
var brewerPalettes = util.keys(colorbrewer);
brewerPalettes.forEach(function(palette) {
var cardinality = 20,
ps = 5,
p = colorbrewer[palette],
interpolator = d3.interpolateLab(p[ps][0], p[ps][ps - 1]);
expect(vlscale.color.palette(palette, cardinality, 'O')).to.eql(
util.range(cardinality).map(function(i) {
return interpolator(i * 1.0 / (cardinality - 1));
})
);
it('should return pre-defined brewer palette if low cardinality', function() {
var brewerPalettes = util.keys(colorbrewer);
brewerPalettes.forEach(function(palette) {
util.range(3, 9).forEach(function(cardinality) {
expect(vlscale.color.palette(palette, cardinality)).to.eql(
colorbrewer[palette][cardinality]
);
});
});
});
it('should return pre-defined brewer palette if high cardinality N', function() {
var brewerPalettes = util.keys(colorbrewer);
brewerPalettes.forEach(function(palette) {
var cardinality = 20;
expect(vlscale.color.palette(palette, cardinality, 'N')).to.eql(
colorbrewer[palette][Math.max.apply(null, util.keys(colorbrewer[palette]))]
);
});
});
it('should return interpolated scale if high cardinality ordinal', function() {
var brewerPalettes = util.keys(colorbrewer);
brewerPalettes.forEach(function(palette) {
var cardinality = 20,
ps = 5,
p = colorbrewer[palette],
interpolator = d3.interpolateLab(p[ps][0], p[ps][ps - 1]);
expect(vlscale.color.palette(palette, cardinality, 'O')).to.eql(
util.range(cardinality).map(function(i) {
return interpolator(i * 1.0 / (cardinality - 1));
})
);
});
});
});
});
describe('vl.compile.scale.color.interpolate', function() {
it('should interpolate color along the lab space', function() {
var interpolator = d3.interpolateLab('#ffffff', '#000000'),
cardinality = 8;
describe('color.interpolate', function() {
it('should interpolate color along the lab space', function() {
var interpolator = d3.interpolateLab('#ffffff', '#000000'),
cardinality = 8;
expect(vlscale.color.interpolate('#ffffff', '#000000', cardinality))
.to.eql(
util.range(cardinality).map(function(i) {
return interpolator(i * 1.0 / (cardinality - 1));
})
);
expect(vlscale.color.interpolate('#ffffff', '#000000', cardinality))
.to.eql(
util.range(cardinality).map(function(i) {
return interpolator(i * 1.0 / (cardinality - 1));
})
);
});
});
});

@@ -6,15 +6,4 @@ 'use strict';

var compile = require('../../src/vl').compile,
util = require('../../src/util');
var compile = require('../../src/vl').compile;
// mock util.getbins()
util.getbins = function() {
return {
start: 0,
stop: 10,
step: 1
};
};
var stats = {

@@ -21,0 +10,0 @@ 'Cost__Total_$': {

@@ -8,3 +8,3 @@ 'use strict';

describe('Time', function() {
describe('time', function() {
var fieldName = 'a',

@@ -20,7 +20,7 @@ timeUnit = 'month',

it('should add formula transform', function() {
var data = spec.data[1];
expect(data.transform).to.be.ok();
var data = spec.data[0];
expect(data.transform).to.be.ok;
expect(data.transform.filter(function(t) {
return t.type === 'formula' && t.field === encoding.field('x') &&
return t.type === 'formula' && t.field === encoding.fieldRef('x') &&
t.expr === time.formula(encoding._enc.x);

@@ -35,2 +35,30 @@ }).length).to.be.above(0);

});
describe('maxLength', function(){
it('should return max length based on time format', function () {
expect(time.maxLength(undefined /*no timeUnit*/, {
config: function(){ return '%A %B %e %H:%M:%S %Y';}
}))
.to.eql('Wednesday September 17 04:00:00 2014'.length);
});
it('should return max length of the month custom scale', function () {
expect(time.maxLength('month', Encoding.fromSpec({mark: 'point'})))
.to.eql(3);
});
it('should return max length of the day custom scale', function () {
expect(time.maxLength('day', Encoding.fromSpec({mark: 'point'})))
.to.eql(3);
});
it('should return max length of the month custom scale', function () {
expect(time.maxLength('month', Encoding.fromSpec({
mark: 'point',
config: {
timeScaleLabelLength: 0
}
}))).to.eql(9);
});
});
});

@@ -11,4 +11,4 @@ 'use strict';

var encoding = Encoding.fromShorthand(shorthand);
expect(encoding.has('y')).ok();
expect(encoding.has('x')).ok();
expect(encoding.has('y')).ok;
expect(encoding.has('x')).ok;

@@ -15,0 +15,0 @@ });

@@ -23,3 +23,3 @@ 'use strict';

var cardinality = vlfield.cardinality(field, stats);
expect(cardinality).to.equal(10);
expect(cardinality).to.equal(15);
});

@@ -26,0 +26,0 @@ });

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