diff --git a/plottable.d.ts b/plottable.d.ts index e3f5b97028..9da3cc0cd4 100644 --- a/plottable.d.ts +++ b/plottable.d.ts @@ -197,6 +197,7 @@ declare module Plottable { class Scale implements IBroadcaster { /** * Creates a new Scale. + * * @constructor * @param {D3.Scale.Scale} scale The D3 scale backing the Scale. */ @@ -210,6 +211,7 @@ declare module Plottable { public scale(value: any): any; /** * Retrieves the current domain, or sets the Scale's domain to the specified values. + * * @param {any[]} [values] The new value for the domain. * @returns {any[]|Scale} The current domain, or the calling Scale (if values is supplied). */ @@ -217,6 +219,7 @@ declare module Plottable { public domain(values: any[]): Scale; /** * Retrieves the current range, or sets the Scale's range to the specified values. + * * @param {any[]} [values] The new value for the range. * @returns {any[]|Scale} The current range, or the calling Scale (if values is supplied). */ @@ -224,43 +227,68 @@ declare module Plottable { public range(values: any[]): Scale; /** * Creates a copy of the Scale with the same domain and range but without any registered listeners. + * * @returns {Scale} A copy of the calling Scale. */ public copy(): Scale; /** * Registers a callback to be called when the scale's domain is changed. + * * @param {IBroadcasterCallback} callback A callback to be called when the Scale's domain changes. * @returns {Scale} The Calling Scale. */ public registerListener(callback: IBroadcasterCallback): Scale; } + class OrdinalScale extends Scale { + /** + * Creates a new OrdinalScale. Domain and Range are set later. + * + * @constructor + */ + constructor(); + public domain(): any[]; + public domain(values: any[]): Scale; + /** + * Returns the range of pixels spanned by the scale, or sets the range. + * + * @param {number[]} [values] The pixel range to set on the scale. + * @returns {number[]|OrdinalScale} The pixel range, or the calling OrdinalScale. + */ + public range(): any[]; + public range(values: number[]): Scale; + } class QuantitiveScale extends Scale { /** * Creates a new QuantitiveScale. + * * @constructor * @param {D3.Scale.QuantitiveScale} scale The D3 QuantitiveScale backing the QuantitiveScale. */ constructor(scale: D3.Scale.QuantitiveScale); /** * Retrieves the domain value corresponding to a supplied range value. + * * @param {number} value: A value from the Scale's range. * @returns {number} The domain value corresponding to the supplied range value. */ public invert(value: number): number; /** * Creates a copy of the QuantitiveScale with the same domain and range but without any registered listeners. + * * @returns {QuantitiveScale} A copy of the calling QuantitiveScale. */ public copy(): QuantitiveScale; /** * Expands the QuantitiveScale's domain to cover the new region. - * @param {number} newDomain The additional domain to be covered by the QuantitiveScale. + * + * @param {number[]} newDomain The additional domain to be covered by the QuantitiveScale. * @returns {QuantitiveScale} The scale. */ public widenDomain(newDomain: number[]): QuantitiveScale; /** * Expands the QuantitiveScale's domain to cover the data given. * Passes an accessor through to the native d3 code. + * * @param data The data to operate on. * @param [accessor] The accessor to get values out of the data * @returns {QuantitiveScale} The scale. @@ -296,6 +324,7 @@ declare module Plottable { public nice(count?: number): QuantitiveScale; /** * Generates tick values. + * * @param {number} [count] The number of ticks to generate. * @returns {any[]} The generated ticks. */ @@ -319,6 +348,7 @@ declare module Plottable { class LinearScale extends QuantitiveScale { /** * Creates a new LinearScale. + * * @constructor * @param {D3.Scale.LinearScale} [scale] The D3 LinearScale backing the LinearScale. If not supplied, uses a default scale. */ @@ -326,6 +356,7 @@ declare module Plottable { constructor(scale: D3.Scale.LinearScale); /** * Creates a copy of the LinearScale with the same domain and range but without any registered listeners. + * * @returns {LinearScale} A copy of the calling LinearScale. */ public copy(): LinearScale; @@ -333,6 +364,7 @@ declare module Plottable { class ColorScale extends Scale { /** * Creates a ColorScale. + * * @constructor * @param {string} [scaleType] the type of color scale to create (Category10/Category20/Category20b/Category20c) */ @@ -477,6 +509,27 @@ declare module Plottable { public metadata(metadata: IMetadata): Renderer; public data(data: any[]): Renderer; public colorAccessor(a: IAccessor): Renderer; + public autorange(): Renderer; + } +} +declare module Plottable { + class CategoryRenderer extends Renderer { + public dataSelection: D3.UpdateSelection; + public xScale: OrdinalScale; + public yScale: QuantitiveScale; + public autorangeDataOnLayout: boolean; + /** + * Creates a CategoryRenderer with an Ordinal x scale and Quantitive y scale. + * + * @constructor + * @param {IDataset} dataset The dataset to render. + * @param {OrdinalScale} xScale The x scale to use. + * @param {QuantitiveScale} yScale The y scale to use. + * @param {IAccessor} [xAccessor] A function for extracting x values from the data. + * @param {IAccessor} [yAccessor] A function for extracting y values from the data. + */ + constructor(dataset: IDataset, xScale: OrdinalScale, yScale: QuantitiveScale, xAccessor?: IAccessor, yAccessor?: IAccessor); + public autorange(): CategoryRenderer; } } declare module Plottable { @@ -484,8 +537,7 @@ declare module Plottable { public dataSelection: D3.UpdateSelection; public xScale: QuantitiveScale; public yScale: QuantitiveScale; - public xAccessor: IAccessor; - public yAccessor: IAccessor; + public autorangeDataOnLayout: boolean; /** * Creates an XYRenderer. * @@ -497,6 +549,9 @@ declare module Plottable { * @param {IAccessor} [yAccessor] A function for extracting y values from the data. */ constructor(dataset: IDataset, xScale: QuantitiveScale, yScale: QuantitiveScale, xAccessor?: IAccessor, yAccessor?: IAccessor); + public xAccessor(accessor: any): XYRenderer; + public yAccessor(accessor: any): XYRenderer; + public autorange(): XYRenderer; /** * Converts a SelectionArea with pixel ranges to one with data ranges. * @@ -533,7 +588,8 @@ declare module Plottable { * @param {IAccessor} [yAccessor] A function for extracting y values from the data. * @param {IAccessor} [rAccessor] A function for extracting radius values from the data. */ - constructor(dataset: IDataset, xScale: QuantitiveScale, yScale: QuantitiveScale, xAccessor?: IAccessor, yAccessor?: IAccessor, rAccessor?: IAccessor); + constructor(dataset: IDataset, xScale: QuantitiveScale, yScale: QuantitiveScale, xAccessor?: any, yAccessor?: any, rAccessor?: any); + public rAccessor(a: any): CircleRenderer; } } declare module Plottable { @@ -554,7 +610,7 @@ declare module Plottable { declare module Plottable { class BarRenderer extends XYRenderer { public barPaddingPx: number; - public dxAccessor: IAccessor; + public dxAccessor: any; /** * Creates a BarRenderer. * @@ -567,6 +623,7 @@ declare module Plottable { * @param {IAccessor} [yAccessor] A function for extracting height of each bar from the data. */ constructor(dataset: IDataset, xScale: QuantitiveScale, yScale: QuantitiveScale, xAccessor?: IAccessor, dxAccessor?: IAccessor, yAccessor?: IAccessor); + public autorange(): BarRenderer; } } declare module Plottable { @@ -583,6 +640,7 @@ declare module Plottable { * @param {IAccessor} [rAccessor] A function for extracting radius values from the data. */ constructor(dataset: IDataset, xScale: QuantitiveScale, yScale: QuantitiveScale, xAccessor?: IAccessor, yAccessor?: IAccessor, rAccessor?: IAccessor); + public rAccessor(a: any): SquareRenderer; } } declare module Plottable { @@ -669,6 +727,48 @@ declare module Plottable { public rowMinimum(newVal: number): Legend; } } +declare module Plottable { + class StandardChart extends Table { + constructor(); + public yAxis(y: YAxis): StandardChart; + public yAxis(): YAxis; + public xAxis(x: XAxis): StandardChart; + public xAxis(): XAxis; + public yLabel(y: AxisLabel): StandardChart; + public yLabel(y: string): StandardChart; + public yLabel(): AxisLabel; + public xLabel(x: AxisLabel): StandardChart; + public xLabel(x: string): StandardChart; + public xLabel(): AxisLabel; + public titleLabel(x: TitleLabel): StandardChart; + public titleLabel(x: string): StandardChart; + public titleLabel(): TitleLabel; + public addCenterComponent(c: Component): StandardChart; + } +} +declare module Plottable { + class CategoryBarRenderer extends CategoryRenderer { + /** + * Creates a CategoryBarRenderer. + * + * @constructor + * @param {IDataset} dataset The dataset to render. + * @param {OrdinalScale} xScale The x scale to use. + * @param {QuantitiveScale} yScale The y scale to use. + * @param {IAccessor} [xAccessor] A function for extracting the start position of each bar from the data. + * @param {IAccessor} [widthAccessor] A function for extracting the width position of each bar, in pixels, from the data. + * @param {IAccessor} [yAccessor] A function for extracting height of each bar from the data. + */ + constructor(dataset: IDataset, xScale: OrdinalScale, yScale: QuantitiveScale, xAccessor?: IAccessor, widthAccessor?: IAccessor, yAccessor?: IAccessor); + /** + * Sets the width accessor. + * + * @param {any} accessor The new width accessor. + * @returns {CategoryBarRenderer} The calling CategoryBarRenderer. + */ + public widthAccessor(accessor: any): CategoryBarRenderer; + } +} declare module Plottable { class Axis extends Component { static yWidth: number; diff --git a/plottable.js b/plottable.js index 213b0f2206..61e681f706 100644 --- a/plottable.js +++ b/plottable.js @@ -1,5 +1,5 @@ /*! -Plottable v0.5.1 (https://github.com/palantir/plottable) +Plottable v0.7.0 (https://github.com/palantir/plottable) Copyright 2014 Palantir Technologies Licensed under MIT (https://github.com/palantir/plottable/blob/master/LICENSE) */ @@ -480,11 +480,12 @@ var Plottable; var Scale = (function () { /** * Creates a new Scale. + * * @constructor * @param {D3.Scale.Scale} scale The D3 scale backing the Scale. */ function Scale(scale) { - this.broadcasterCallbacks = []; + this._broadcasterCallbacks = []; this._d3Scale = scale; } /** @@ -503,7 +504,7 @@ var Plottable; return this._d3Scale.domain(); } else { this._d3Scale.domain(values); - this.broadcasterCallbacks.forEach(function (b) { + this._broadcasterCallbacks.forEach(function (b) { return b(_this); }); return this; @@ -521,6 +522,7 @@ var Plottable; /** * Creates a copy of the Scale with the same domain and range but without any registered listeners. + * * @returns {Scale} A copy of the calling Scale. */ Scale.prototype.copy = function () { @@ -529,21 +531,62 @@ var Plottable; /** * Registers a callback to be called when the scale's domain is changed. + * * @param {IBroadcasterCallback} callback A callback to be called when the Scale's domain changes. * @returns {Scale} The Calling Scale. */ Scale.prototype.registerListener = function (callback) { - this.broadcasterCallbacks.push(callback); + this._broadcasterCallbacks.push(callback); return this; }; return Scale; })(); Plottable.Scale = Scale; + var OrdinalScale = (function (_super) { + __extends(OrdinalScale, _super); + /** + * Creates a new OrdinalScale. Domain and Range are set later. + * + * @constructor + */ + function OrdinalScale() { + _super.call(this, d3.scale.ordinal()); + this.END_PADDING = 0.5; + this._range = [0, 1]; + } + OrdinalScale.prototype.domain = function (values) { + var _this = this; + if (values == null) { + return this._d3Scale.domain(); + } else { + this._d3Scale.domain(values); + this._broadcasterCallbacks.forEach(function (b) { + return b(_this); + }); + this._d3Scale.rangePoints(this.range(), 2 * this.END_PADDING); // d3 scale takes total padding + return this; + } + }; + + OrdinalScale.prototype.range = function (values) { + if (values == null) { + return this._range; + } else { + this._range = values; + this._d3Scale.rangePoints(values, 2 * this.END_PADDING); // d3 scale takes total padding + return this; + } + }; + return OrdinalScale; + })(Scale); + Plottable.OrdinalScale = OrdinalScale; + var QuantitiveScale = (function (_super) { __extends(QuantitiveScale, _super); /** * Creates a new QuantitiveScale. + * * @constructor * @param {D3.Scale.QuantitiveScale} scale The D3 QuantitiveScale backing the QuantitiveScale. */ @@ -553,6 +596,7 @@ var Plottable; } /** * Retrieves the domain value corresponding to a supplied range value. + * * @param {number} value: A value from the Scale's range. * @returns {number} The domain value corresponding to the supplied range value. */ @@ -562,6 +606,7 @@ var Plottable; /** * Creates a copy of the QuantitiveScale with the same domain and range but without any registered listeners. + * * @returns {QuantitiveScale} A copy of the calling QuantitiveScale. */ QuantitiveScale.prototype.copy = function () { @@ -570,7 +615,8 @@ var Plottable; /** * Expands the QuantitiveScale's domain to cover the new region. - * @param {number} newDomain The additional domain to be covered by the QuantitiveScale. + * + * @param {number[]} newDomain The additional domain to be covered by the QuantitiveScale. * @returns {QuantitiveScale} The scale. */ QuantitiveScale.prototype.widenDomain = function (newDomain) { @@ -583,6 +629,7 @@ var Plottable; /** * Expands the QuantitiveScale's domain to cover the data given. * Passes an accessor through to the native d3 code. + * * @param data The data to operate on. * @param [accessor] The accessor to get values out of the data * @returns {QuantitiveScale} The scale. @@ -632,6 +679,7 @@ var Plottable; /** * Generates tick values. + * * @param {number} [count] The number of ticks to generate. * @returns {any[]} The generated ticks. */ @@ -679,6 +727,7 @@ var Plottable; } /** * Creates a copy of the LinearScale with the same domain and range but without any registered listeners. + * * @returns {LinearScale} A copy of the calling LinearScale. */ LinearScale.prototype.copy = function () { @@ -692,6 +741,7 @@ var Plottable; __extends(ColorScale, _super); /** * Creates a ColorScale. + * * @constructor * @param {string} [scaleType] the type of color scale to create (Category10/Category20/Category20b/Category20c) */ @@ -1021,12 +1071,14 @@ var Plottable; this.lasty = y; var domainX = this.renderer.xScale.invert(x); var data = this.renderer._data; - var dataIndex = Plottable.OSUtils.sortedIndex(domainX, data, this.renderer.xAccessor); + var xA = this.renderer._getAppliedAccessor(this.renderer._xAccessor); + var yA = this.renderer._getAppliedAccessor(this.renderer._yAccessor); + var dataIndex = Plottable.OSUtils.sortedIndex(domainX, data, xA); dataIndex = dataIndex > 0 ? dataIndex - 1 : 0; var dataPoint = data[dataIndex]; - var dataX = this.renderer.xAccessor(dataPoint); - var dataY = this.renderer.yAccessor(dataPoint); + var dataX = xA(dataPoint, dataIndex); + var dataY = yA(dataPoint, dataIndex); var pixelX = this.renderer.xScale.scale(dataX); var pixelY = this.renderer.yScale.scale(dataY); this.circle.attr("cx", pixelX).attr("cy", pixelY); @@ -1251,11 +1303,33 @@ var Plottable; // no-op }; + Renderer.prototype.autorange = function () { + // no-op + return this; + }; + Renderer.prototype._anchor = function (element) { _super.prototype._anchor.call(this, element); this.renderArea = this.content.append("g").classed("render-area", true); return this; }; + + Renderer.prototype._getAppliedAccessor = function (accessor) { + var _this = this; + if (typeof (accessor) === "function") { + return function (d, i) { + return accessor(d, i, _this._metadata); + }; + } else if (typeof (accessor) === "string") { + return function (d, i) { + return d[accessor]; + }; + } else { + return function (d, i) { + return accessor; + }; + } + }; Renderer.defaultColorAccessor = function (d) { return "#1f77b4"; }; @@ -1266,42 +1340,99 @@ var Plottable; /// var Plottable; (function (Plottable) { - var XYRenderer = (function (_super) { - __extends(XYRenderer, _super); + var CategoryRenderer = (function (_super) { + __extends(CategoryRenderer, _super); /** - * Creates an XYRenderer. + * Creates a CategoryRenderer with an Ordinal x scale and Quantitive y scale. * * @constructor * @param {IDataset} dataset The dataset to render. - * @param {QuantitiveScale} xScale The x scale to use. + * @param {OrdinalScale} xScale The x scale to use. * @param {QuantitiveScale} yScale The y scale to use. * @param {IAccessor} [xAccessor] A function for extracting x values from the data. * @param {IAccessor} [yAccessor] A function for extracting y values from the data. */ - function XYRenderer(dataset, xScale, yScale, xAccessor, yAccessor) { + function CategoryRenderer(dataset, xScale, yScale, xAccessor, yAccessor) { var _this = this; _super.call(this, dataset); - this.classed("xy-renderer"); - - this.xAccessor = (xAccessor != null) ? xAccessor : XYRenderer.defaultXAccessor; - this.yAccessor = (yAccessor != null) ? yAccessor : XYRenderer.defaultYAccessor; + this.autorangeDataOnLayout = true; + this.classed("category-renderer", true); this.xScale = xScale; this.yScale = yScale; - var data = dataset.data; + this._xAccessor = (xAccessor != null) ? xAccessor : "x"; // default + this._yAccessor = (yAccessor != null) ? yAccessor : "y"; // default + + this.xScale.registerListener(function () { + return _this.rescale(); + }); + this.yScale.registerListener(function () { + return _this.rescale(); + }); + } + CategoryRenderer.prototype._computeLayout = function (xOffset, yOffset, availableWidth, availableHeight) { + _super.prototype._computeLayout.call(this, xOffset, yOffset, availableWidth, availableHeight); + this.xScale.range([0, this.availableWidth]); + this.yScale.range([this.availableHeight, 0]); + if (this.autorangeDataOnLayout) { + this.autorange(); + } + return this; + }; - var appliedXAccessor = function (d) { - return _this.xAccessor(d, null, _this._metadata); + CategoryRenderer.prototype.autorange = function () { + var _this = this; + _super.prototype.autorange.call(this); + var data = this._data; + var xA = function (d) { + return _this._getAppliedAccessor(_this._xAccessor)(d, null); }; - var xDomain = d3.extent(data, appliedXAccessor); - this.xScale.widenDomain(xDomain); + this.xScale.domain(data.map(xA)); - var appliedYAccessor = function (d) { - return _this.yAccessor(d, null, _this._metadata); + var yA = function (d) { + return _this._getAppliedAccessor(_this._yAccessor)(d, null); }; - var yDomain = d3.extent(data, appliedYAccessor); + var yDomain = d3.extent(data, yA); this.yScale.widenDomain(yDomain); + return this; + }; + + CategoryRenderer.prototype.rescale = function () { + if (this.element != null) { + this._render(); + } + }; + return CategoryRenderer; + })(Plottable.Renderer); + Plottable.CategoryRenderer = CategoryRenderer; +})(Plottable || (Plottable = {})); +/// +var Plottable; +(function (Plottable) { + var XYRenderer = (function (_super) { + __extends(XYRenderer, _super); + /** + * Creates an XYRenderer. + * + * @constructor + * @param {IDataset} dataset The dataset to render. + * @param {QuantitiveScale} xScale The x scale to use. + * @param {QuantitiveScale} yScale The y scale to use. + * @param {IAccessor} [xAccessor] A function for extracting x values from the data. + * @param {IAccessor} [yAccessor] A function for extracting y values from the data. + */ + function XYRenderer(dataset, xScale, yScale, xAccessor, yAccessor) { + var _this = this; + _super.call(this, dataset); + this.autorangeDataOnLayout = true; + this.classed("xy-renderer", true); + + this._xAccessor = (xAccessor != null) ? xAccessor : XYRenderer.defaultXAccessor; + this._yAccessor = (yAccessor != null) ? yAccessor : XYRenderer.defaultYAccessor; + + this.xScale = xScale; + this.yScale = yScale; this.xScale.registerListener(function () { return _this.rescale(); @@ -1310,10 +1441,45 @@ var Plottable; return _this.rescale(); }); } + XYRenderer.prototype.xAccessor = function (accessor) { + this._xAccessor = accessor; + this._requireRerender = true; + this._rerenderUpdateSelection = true; + return this; + }; + + XYRenderer.prototype.yAccessor = function (accessor) { + this._yAccessor = accessor; + this._requireRerender = true; + this._rerenderUpdateSelection = true; + return this; + }; + XYRenderer.prototype._computeLayout = function (xOffset, yOffset, availableWidth, availableHeight) { _super.prototype._computeLayout.call(this, xOffset, yOffset, availableWidth, availableHeight); this.xScale.range([0, this.availableWidth]); this.yScale.range([this.availableHeight, 0]); + if (this.autorangeDataOnLayout) { + this.autorange(); + } + return this; + }; + + XYRenderer.prototype.autorange = function () { + var _this = this; + _super.prototype.autorange.call(this); + var data = this._data; + var xA = function (d) { + return _this._getAppliedAccessor(_this._xAccessor)(d, null); + }; + var xDomain = d3.extent(data, xA); + this.xScale.widenDomain(xDomain); + + var yA = function (d) { + return _this._getAppliedAccessor(_this._yAccessor)(d, null); + }; + var yDomain = d3.extent(data, yA); + this.yScale.widenDomain(yDomain); return this; }; @@ -1333,10 +1499,11 @@ var Plottable; }; XYRenderer.prototype.getDataFilterFunction = function (dataArea) { - var _this = this; + var xA = this._getAppliedAccessor(this._xAccessor); + var yA = this._getAppliedAccessor(this._yAccessor); var filterFunction = function (d, i) { - var x = _this.xAccessor(d, i, _this._metadata); - var y = _this.yAccessor(d, i, _this._metadata); + var x = xA(d, i); + var y = yA(d, i); return Plottable.Utils.inRange(x, dataArea.xMin, dataArea.xMax) && Plottable.Utils.inRange(y, dataArea.yMin, dataArea.yMax); }; return filterFunction; @@ -1375,12 +1542,8 @@ var Plottable; this._render(); } }; - XYRenderer.defaultXAccessor = function (d) { - return d.x; - }; - XYRenderer.defaultYAccessor = function (d) { - return d.y; - }; + XYRenderer.defaultXAccessor = "x"; + XYRenderer.defaultYAccessor = "y"; return XYRenderer; })(Plottable.Renderer); Plottable.XYRenderer = XYRenderer; @@ -1403,28 +1566,33 @@ var Plottable; */ function CircleRenderer(dataset, xScale, yScale, xAccessor, yAccessor, rAccessor) { _super.call(this, dataset, xScale, yScale, xAccessor, yAccessor); - this.rAccessor = (rAccessor != null) ? rAccessor : CircleRenderer.defaultRAccessor; + this._rAccessor = (rAccessor != null) ? rAccessor : CircleRenderer.defaultRAccessor; this.classed("circle-renderer", true); } + CircleRenderer.prototype.rAccessor = function (a) { + this._rAccessor = a; + this._requireRerender = true; + this._rerenderUpdateSelection = true; + return this; + }; + CircleRenderer.prototype._paint = function () { var _this = this; _super.prototype._paint.call(this); + var cx = function (d, i) { + return _this.xScale.scale(_this._getAppliedAccessor(_this._xAccessor)(d, i)); + }; + var cy = function (d, i) { + return _this.yScale.scale(_this._getAppliedAccessor(_this._yAccessor)(d, i)); + }; + var r = this._getAppliedAccessor(this._rAccessor); + var color = this._getAppliedAccessor(this._colorAccessor); this.dataSelection = this.renderArea.selectAll("circle").data(this._data); this.dataSelection.enter().append("circle"); - this.dataSelection.attr("cx", function (d, i) { - return _this.xScale.scale(_this.xAccessor(d, i, _this._metadata)); - }).attr("cy", function (d, i) { - return _this.yScale.scale(_this.yAccessor(d, i, _this._metadata)); - }).attr("r", function (d, i) { - return _this.rAccessor(d, i, _this._metadata); - }).attr("fill", function (d, i) { - return _this._colorAccessor(d, i, _this._metadata); - }); + this.dataSelection.attr("cx", cx).attr("cy", cy).attr("r", r).attr("fill", color); this.dataSelection.exit().remove(); }; - CircleRenderer.defaultRAccessor = function (d) { - return 3; - }; + CircleRenderer.defaultRAccessor = 3; return CircleRenderer; })(Plottable.XYRenderer); Plottable.CircleRenderer = CircleRenderer; @@ -1450,23 +1618,23 @@ var Plottable; } LineRenderer.prototype._anchor = function (element) { _super.prototype._anchor.call(this, element); - this.path = this.renderArea.append("path"); + this.path = this.renderArea.append("path").classed("line", true); return this; }; LineRenderer.prototype._paint = function () { var _this = this; _super.prototype._paint.call(this); + var xA = this._getAppliedAccessor(this._xAccessor); + var yA = this._getAppliedAccessor(this._yAccessor); + var cA = this._getAppliedAccessor(this._colorAccessor); this.line = d3.svg.line().x(function (d, i) { - return _this.xScale.scale(_this.xAccessor(d, i, _this._metadata)); + return _this.xScale.scale(xA(d, i)); }).y(function (d, i) { - return _this.yScale.scale(_this.yAccessor(d, i, _this._metadata)); + return _this.yScale.scale(yA(d, i)); }); - this.dataSelection = this.path.classed("line", true).datum(this._data); - this.path.attr("d", this.line); - - // Since we can only set one stroke for the full line, call colorAccessor on first datum with index 0 - this.path.attr("stroke", this._colorAccessor(this._data[0], 0, this._metadata)); + this.dataSelection = this.path.datum(this._data); + this.path.attr("d", this.line).attr("stroke", cA); }; return LineRenderer; })(Plottable.XYRenderer); @@ -1489,19 +1657,24 @@ var Plottable; * @param {IAccessor} [yAccessor] A function for extracting height of each bar from the data. */ function BarRenderer(dataset, xScale, yScale, xAccessor, dxAccessor, yAccessor) { - var _this = this; _super.call(this, dataset, xScale, yScale, xAccessor, yAccessor); this.barPaddingPx = 1; this.classed("bar-renderer", true); this.dxAccessor = (dxAccessor != null) ? dxAccessor : BarRenderer.defaultDxAccessor; - + } + BarRenderer.prototype.autorange = function () { + _super.prototype.autorange.call(this); + var xA = this._getAppliedAccessor(this._xAccessor); + var dxA = this._getAppliedAccessor(this.dxAccessor); var x2Accessor = function (d) { - return _this.xAccessor(d, null, _this._metadata) + _this.dxAccessor(d, null, _this._metadata); + return xA(d, null) + dxA(d, null); }; - var x2Extent = d3.extent(dataset.data, x2Accessor); + var x2Extent = d3.extent(this._data, x2Accessor); this.xScale.widenDomain(x2Extent); - } + return this; + }; + BarRenderer.prototype._paint = function () { var _this = this; _super.prototype._paint.call(this); @@ -1513,41 +1686,36 @@ var Plottable; var xrr = this.xScale.range()[1] - this.xScale.range()[0]; this.dataSelection.enter().append("rect"); + var xA = this._getAppliedAccessor(this._xAccessor); var xFunction = function (d, i) { - var x = _this.xAccessor(d, i, _this._metadata); + var x = xA(d, i); var scaledX = _this.xScale.scale(x); return scaledX + _this.barPaddingPx; }; + var yA = this._getAppliedAccessor(this._yAccessor); var yFunction = function (d, i) { - var y = _this.yAccessor(d, i, _this._metadata); + var y = yA(d, i); var scaledY = _this.yScale.scale(y); return scaledY; }; + var dxA = this._getAppliedAccessor(this.dxAccessor); var widthFunction = function (d, i) { - var dx = _this.dxAccessor(d, i, _this._metadata); + var dx = dxA(d, i); var scaledDx = _this.xScale.scale(dx); var scaledOffset = _this.xScale.scale(0); return scaledDx - scaledOffset - 2 * _this.barPaddingPx; }; var heightFunction = function (d, i) { - var y = _this.yAccessor(d, i, _this._metadata); - var scaledY = _this.yScale.scale(y); - return maxScaledY - scaledY; + return maxScaledY - yFunction(d, i); }; - var colorFunction = function (d, i) { - return _this._colorAccessor(d, i, _this._metadata); - }; - - this.dataSelection.attr("x", xFunction).attr("y", yFunction).attr("width", widthFunction).attr("height", heightFunction).attr("fill", colorFunction); + this.dataSelection.attr("x", xFunction).attr("y", yFunction).attr("width", widthFunction).attr("height", heightFunction).attr("fill", this._getAppliedAccessor(this._colorAccessor)); this.dataSelection.exit().remove(); }; - BarRenderer.defaultDxAccessor = function (d) { - return d.dx; - }; + BarRenderer.defaultDxAccessor = "dx"; return BarRenderer; })(Plottable.XYRenderer); Plottable.BarRenderer = BarRenderer; @@ -1570,34 +1738,37 @@ var Plottable; */ function SquareRenderer(dataset, xScale, yScale, xAccessor, yAccessor, rAccessor) { _super.call(this, dataset, xScale, yScale, xAccessor, yAccessor); - this.rAccessor = (rAccessor != null) ? rAccessor : SquareRenderer.defaultRAccessor; + this._rAccessor = (rAccessor != null) ? rAccessor : SquareRenderer.defaultRAccessor; this.classed("square-renderer", true); } + SquareRenderer.prototype.rAccessor = function (a) { + this._rAccessor = a; + this._requireRerender = true; + this._rerenderUpdateSelection = true; + return this; + }; + SquareRenderer.prototype._paint = function () { var _this = this; _super.prototype._paint.call(this); + var xA = this._getAppliedAccessor(this._xAccessor); + var yA = this._getAppliedAccessor(this._yAccessor); + var rA = this._getAppliedAccessor(this._rAccessor); + var cA = this._getAppliedAccessor(this._colorAccessor); var xFn = function (d, i) { - return _this.xScale.scale(_this.xAccessor(d, i, _this._metadata)) - _this.rAccessor(d, i, _this._metadata); + return _this.xScale.scale(xA(d, i)) - rA(d, i); }; var yFn = function (d, i) { - return _this.yScale.scale(_this.yAccessor(d, i, _this._metadata)) - _this.rAccessor(d, i, _this._metadata); + return _this.yScale.scale(yA(d, i)) - rA(d, i); }; this.dataSelection = this.renderArea.selectAll("rect").data(this._data); this.dataSelection.enter().append("rect"); - this.dataSelection.attr("x", xFn).attr("y", yFn).attr("width", function (d, i) { - return _this.rAccessor(d, i, _this._metadata); - }).attr("height", function (d, i) { - return _this.rAccessor(d, i, _this._metadata); - }).attr("fill", function (d, i) { - return _this._colorAccessor(d, i, _this._metadata); - }); + this.dataSelection.attr("x", xFn).attr("y", yFn).attr("width", rA).attr("height", rA).attr("fill", cA); this.dataSelection.exit().remove(); }; - SquareRenderer.defaultRAccessor = function (d) { - return 3; - }; + SquareRenderer.defaultRAccessor = 3; return SquareRenderer; })(Plottable.XYRenderer); Plottable.SquareRenderer = SquareRenderer; @@ -1815,7 +1986,6 @@ var Plottable; } for (var j = 0; j < nCols; j++) { if (this.rows[i][j] === undefined) { - // this.rows[i][j] = new Component(); this.rows[i][j] = null; } } @@ -1991,6 +2161,188 @@ var Plottable; })(Plottable.Component); Plottable.Legend = Legend; })(Plottable || (Plottable = {})); +/// +var Plottable; +(function (Plottable) { + var StandardChart = (function (_super) { + __extends(StandardChart, _super); + function StandardChart() { + _super.call(this); + this.xTable = new Plottable.Table(); + this.yTable = new Plottable.Table(); + this.centerComponent = new Plottable.ComponentGroup(); + this.xyTable = new Plottable.Table().addComponent(0, 0, this.yTable).addComponent(1, 1, this.xTable).addComponent(0, 1, this.centerComponent); + this.addComponent(1, 0, this.xyTable); + } + StandardChart.prototype.yAxis = function (y) { + if (y != null) { + if (this._yAxis != null) { + throw new Error("yAxis already assigned!"); + } + this._yAxis = y; + this.yTable.addComponent(0, 1, this._yAxis); + return this; + } else { + return this._yAxis; + } + }; + + StandardChart.prototype.xAxis = function (x) { + if (x != null) { + if (this._xAxis != null) { + throw new Error("xAxis already assigned!"); + } + this._xAxis = x; + this.xTable.addComponent(0, 0, this._xAxis); + return this; + } else { + return this._xAxis; + } + }; + + StandardChart.prototype.yLabel = function (y) { + if (y != null) { + if (this._yLabel != null) { + if (typeof (y) === "string") { + this._yLabel.setText(y); + return this; + } else { + throw new Error("yLabel already assigned!"); + } + } + if (typeof (y) === "string") { + y = new Plottable.AxisLabel(y, "vertical-left"); + } + this._yLabel = y; + this.yTable.addComponent(0, 0, this._yLabel); + return this; + } else { + return this._yLabel; + } + }; + + StandardChart.prototype.xLabel = function (x) { + if (x != null) { + if (this._xLabel != null) { + if (typeof (x) === "string") { + this._xLabel.setText(x); + return this; + } else { + throw new Error("xLabel already assigned!"); + } + } + if (typeof (x) === "string") { + x = new Plottable.AxisLabel(x, "horizontal"); + } + this._xLabel = x; + this.xTable.addComponent(1, 0, this._xLabel); + return this; + } else { + return this._xLabel; + } + }; + + StandardChart.prototype.titleLabel = function (x) { + if (x != null) { + if (this._titleLabel != null) { + if (typeof (x) === "string") { + this._titleLabel.setText(x); + return this; + } else { + throw new Error("titleLabel already assigned!"); + } + } + if (typeof (x) === "string") { + x = new Plottable.TitleLabel(x, "horizontal"); + } + this._titleLabel = x; + this.addComponent(0, 0, this._titleLabel); + return this; + } else { + return this._titleLabel; + } + }; + + StandardChart.prototype.addCenterComponent = function (c) { + this.centerComponent.merge(c); + return this; + }; + return StandardChart; + })(Plottable.Table); + Plottable.StandardChart = StandardChart; +})(Plottable || (Plottable = {})); +/// +var Plottable; +(function (Plottable) { + var CategoryBarRenderer = (function (_super) { + __extends(CategoryBarRenderer, _super); + /** + * Creates a CategoryBarRenderer. + * + * @constructor + * @param {IDataset} dataset The dataset to render. + * @param {OrdinalScale} xScale The x scale to use. + * @param {QuantitiveScale} yScale The y scale to use. + * @param {IAccessor} [xAccessor] A function for extracting the start position of each bar from the data. + * @param {IAccessor} [widthAccessor] A function for extracting the width position of each bar, in pixels, from the data. + * @param {IAccessor} [yAccessor] A function for extracting height of each bar from the data. + */ + function CategoryBarRenderer(dataset, xScale, yScale, xAccessor, widthAccessor, yAccessor) { + _super.call(this, dataset, xScale, yScale, xAccessor, yAccessor); + this.classed("bar-renderer", true); + this._widthAccessor = (widthAccessor != null) ? widthAccessor : 10; // default width is 10px + } + /** + * Sets the width accessor. + * + * @param {any} accessor The new width accessor. + * @returns {CategoryBarRenderer} The calling CategoryBarRenderer. + */ + CategoryBarRenderer.prototype.widthAccessor = function (accessor) { + this._widthAccessor = accessor; + this._requireRerender = true; + this._rerenderUpdateSelection = true; + return this; + }; + + CategoryBarRenderer.prototype._paint = function () { + var _this = this; + _super.prototype._paint.call(this); + var yRange = this.yScale.range(); + var maxScaledY = Math.max(yRange[0], yRange[1]); + + this.dataSelection = this.renderArea.selectAll("rect").data(this._data); + var xdr = this.xScale.domain()[1] - this.xScale.domain()[0]; + var xrr = this.xScale.range()[1] - this.xScale.range()[0]; + this.dataSelection.enter().append("rect"); + + var widthFunction = this._getAppliedAccessor(this._widthAccessor); + + var xA = this._getAppliedAccessor(this._xAccessor); + var xFunction = function (d, i) { + var x = xA(d, i); + var scaledX = _this.xScale.scale(x); + return scaledX - widthFunction(d, i) / 2; + }; + + var yA = this._getAppliedAccessor(this._yAccessor); + var yFunction = function (d, i) { + var y = yA(d, i); + var scaledY = _this.yScale.scale(y); + return scaledY; + }; + + var heightFunction = function (d, i) { + return maxScaledY - yFunction(d, i); + }; + + this.dataSelection.attr("x", xFunction).attr("y", yFunction).attr("width", widthFunction).attr("height", heightFunction).attr("fill", this._getAppliedAccessor(this._colorAccessor)); + this.dataSelection.exit().remove(); + }; + return CategoryBarRenderer; + })(Plottable.CategoryRenderer); + Plottable.CategoryBarRenderer = CategoryBarRenderer; +})(Plottable || (Plottable = {})); /// /// /// @@ -2000,6 +2352,7 @@ var Plottable; /// /// /// +/// /// /// /// @@ -2008,6 +2361,8 @@ var Plottable; /// /// /// +/// +/// //grunt-end /// var Plottable; @@ -2432,13 +2787,8 @@ var Plottable; }; ComponentGroup.prototype.merge = function (c) { - if (ComponentGroup.prototype.isPrototypeOf(c)) { - var cg = new ComponentGroup([this, c]); - return cg; - } else { - this._addComponentToGroup(c); - return this; - } + this._addComponentToGroup(c); + return this; }; ComponentGroup.prototype._anchor = function (element) { diff --git a/test/tests.js b/test/tests.js index 60aac0d479..0a6c70857d 100644 --- a/test/tests.js +++ b/test/tests.js @@ -312,12 +312,13 @@ describe("ComponentGroups", function () { var cg1 = new Plottable.ComponentGroup([c1, c2]); var cg2 = new Plottable.ComponentGroup([c3, c4]); var cg = cg1.merge(cg2); - assert.notEqual(cg, cg1, "merged != cg1"); + assert.equal(cg, cg1, "merged == cg1"); assert.notEqual(cg, cg2, "merged != cg2"); var components = cg.components; - assert.lengthOf(components, 2, "there are two inner components"); - assert.equal(components[0], cg1, "cg1 nested inside"); - assert.equal(components[1], cg2, "cg2 nested inside"); + assert.lengthOf(components, 3, "there are three inner components"); + assert.equal(components[0], c1, "components are inside"); + assert.equal(components[1], c2, "components are inside"); + assert.equal(components[2], cg2, "componentGroup2 inside componentGroup1"); }); }); }); @@ -1257,6 +1258,7 @@ describe("Renderers", function () { }; var dataset = { data: data, metadata: metadata }; var renderer = new Plottable.CircleRenderer(dataset, xScale, yScale, xAccessor, yAccessor); + renderer.autorangeDataOnLayout = false; xScale.domain([0, 400]); yScale.domain([400, 0]); renderer.renderTo(svg); @@ -1266,21 +1268,21 @@ describe("Renderers", function () { assert.closeTo(parseFloat(c1.attr("cx")), 0, 0.01, "first circle cx is correct"); assert.closeTo(parseFloat(c1.attr("cy")), 20, 0.01, "first circle cy is correct"); assert.closeTo(parseFloat(c2.attr("cx")), 11, 0.01, "second circle cx is correct"); - assert.closeTo(parseFloat(c1.attr("cy")), 20, 0.01, "second circle cy is correct"); + assert.closeTo(parseFloat(c2.attr("cy")), 20, 0.01, "second circle cy is correct"); data = [{ x: 2, y: 2 }, { x: 4, y: 4 }]; renderer.data(data).renderTo(svg); assert.closeTo(parseFloat(c1.attr("cx")), 2, 0.01, "first circle cx is correct after data change"); assert.closeTo(parseFloat(c1.attr("cy")), 20, 0.01, "first circle cy is correct after data change"); assert.closeTo(parseFloat(c2.attr("cx")), 14, 0.01, "second circle cx is correct after data change"); - assert.closeTo(parseFloat(c1.attr("cy")), 20, 0.01, "second circle cy is correct after data change"); + assert.closeTo(parseFloat(c2.attr("cy")), 20, 0.01, "second circle cy is correct after data change"); metadata = { foo: 0, bar: 0 }; renderer.metadata(metadata).renderTo(svg); assert.closeTo(parseFloat(c1.attr("cx")), 2, 0.01, "first circle cx is correct after metadata change"); assert.closeTo(parseFloat(c1.attr("cy")), 0, 0.01, "first circle cy is correct after metadata change"); assert.closeTo(parseFloat(c2.attr("cx")), 4, 0.01, "second circle cx is correct after metadata change"); - assert.closeTo(parseFloat(c1.attr("cy")), 0, 0.01, "second circle cy is correct after metadata change"); + assert.closeTo(parseFloat(c2.attr("cy")), 0, 0.01, "second circle cy is correct after metadata change"); svg.remove(); }); @@ -1579,6 +1581,60 @@ describe("Renderers", function () { ; }); }); + + describe("Category Bar Renderer", function () { + var verifier = new MultiTestVerifier(); + var svg; + var dataset; + var xScale; + var yScale; + var renderer; + var SVG_WIDTH = 600; + var SVG_HEIGHT = 400; + + before(function () { + svg = generateSVG(SVG_WIDTH, SVG_HEIGHT); + xScale = new Plottable.OrdinalScale(); + yScale = new Plottable.LinearScale(); + dataset = { + data: [ + { x: "A", y: 1 }, + { x: "B", y: 2 } + ], + metadata: { cssClass: "letters" } + }; + + renderer = new Plottable.CategoryBarRenderer(dataset, xScale, yScale); + renderer._anchor(svg); + renderer._computeLayout(); + }); + + beforeEach(function () { + verifier.start(); + }); + + it("renders correctly", function () { + yScale.domain([0, 4]); + var renderArea = renderer.renderArea; + var bars = renderArea.selectAll("rect"); + var bar0 = d3.select(bars[0][0]); + var bar1 = d3.select(bars[0][1]); + assert.equal(bar0.attr("width"), "10", "bar0 width is correct"); + assert.equal(bar1.attr("width"), "10", "bar1 width is correct"); + assert.equal(bar0.attr("height"), "100", "bar0 height is correct"); + assert.equal(bar1.attr("height"), "200", "bar1 height is correct"); + assert.equal(bar0.attr("x"), "145", "bar0 x is correct"); + assert.equal(bar1.attr("x"), "445", "bar1 x is correct"); + verifier.end(); + }); + + after(function () { + if (verifier.passed) { + svg.remove(); + } + ; + }); + }); }); }); /// @@ -1594,7 +1650,7 @@ describe("Scales", function () { var scaleCopy = scale.copy(); assert.deepEqual(scale.domain(), scaleCopy.domain(), "Copied scale has the same domain as the original."); assert.deepEqual(scale.range(), scaleCopy.range(), "Copied scale has the same range as the original."); - assert.notDeepEqual(scale.broadcasterCallbacks, scaleCopy.broadcasterCallbacks, "Registered callbacks are not copied over"); + assert.notDeepEqual(scale._broadcasterCallbacks, scaleCopy._broadcasterCallbacks, "Registered callbacks are not copied over"); }); it("Scale alerts listeners when its domain is updated", function () {